Commit 24c98674 authored by Farhan Ali's avatar Farhan Ali Committed by Cornelia Huck
Browse files

vfio-ccw: Introduce a new schib region



The schib region can be used by userspace to get the subchannel-
information block (SCHIB) for the passthrough subchannel.
This can be useful to get information such as channel path
information via the SCHIB.PMCW fields.

Signed-off-by: default avatarFarhan Ali <alifm@linux.ibm.com>
Signed-off-by: default avatarEric Farman <farman@linux.ibm.com>
Reviewed-by: default avatarCornelia Huck <cohuck@redhat.com>
Message-Id: <20200505122745.53208-5-farman@linux.ibm.com>
Signed-off-by: default avatarCornelia Huck <cohuck@redhat.com>
parent 600279b5
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -282,6 +282,21 @@ for each access of the region. The following values may occur:
``-EBUSY``
  The subchannel was status pending or busy while processing a halt request.

vfio-ccw schib region
---------------------

The vfio-ccw schib region is used to return Subchannel-Information
Block (SCHIB) data to userspace::

  struct ccw_schib_region {
  #define SCHIB_AREA_SIZE 52
         __u8 schib_area[SCHIB_AREA_SIZE];
  } __packed;

This region is exposed via region type VFIO_REGION_SUBTYPE_CCW_SCHIB.

Reading this region triggers a STORE SUBCHANNEL to be issued to the
associated hardware.

vfio-ccw operation details
--------------------------
@@ -385,7 +400,8 @@ through DASD/ECKD device online in a guest now and use it as a block
device.

The current code allows the guest to start channel programs via
START SUBCHANNEL, and to issue HALT SUBCHANNEL and CLEAR SUBCHANNEL.
START SUBCHANNEL, and to issue HALT SUBCHANNEL, CLEAR SUBCHANNEL,
and STORE SUBCHANNEL.

Currently all channel programs are prefetched, regardless of the
p-bit setting in the ORB.  As a result, self modifying channel
+1 −1
Original line number Diff line number Diff line
@@ -21,5 +21,5 @@ qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o
obj-$(CONFIG_QDIO) += qdio.o

vfio_ccw-objs += vfio_ccw_drv.o vfio_ccw_cp.o vfio_ccw_ops.o vfio_ccw_fsm.o \
	vfio_ccw_async.o vfio_ccw_trace.o
	vfio_ccw_async.o vfio_ccw_trace.o vfio_ccw_chp.o
obj-$(CONFIG_VFIO_CCW) += vfio_ccw.o
+76 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 * Channel path related status regions for vfio_ccw
 *
 * Copyright IBM Corp. 2020
 *
 * Author(s): Farhan Ali <alifm@linux.ibm.com>
 *            Eric Farman <farman@linux.ibm.com>
 */

#include <linux/vfio.h>
#include "vfio_ccw_private.h"

static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private,
					  char __user *buf, size_t count,
					  loff_t *ppos)
{
	unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS;
	loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
	struct ccw_schib_region *region;
	int ret;

	if (pos + count > sizeof(*region))
		return -EINVAL;

	mutex_lock(&private->io_mutex);
	region = private->region[i].data;

	if (cio_update_schib(private->sch)) {
		ret = -ENODEV;
		goto out;
	}

	memcpy(region, &private->sch->schib, sizeof(*region));

	if (copy_to_user(buf, (void *)region + pos, count)) {
		ret = -EFAULT;
		goto out;
	}

	ret = count;

out:
	mutex_unlock(&private->io_mutex);
	return ret;
}

static ssize_t vfio_ccw_schib_region_write(struct vfio_ccw_private *private,
					   const char __user *buf, size_t count,
					   loff_t *ppos)
{
	return -EINVAL;
}


static void vfio_ccw_schib_region_release(struct vfio_ccw_private *private,
					  struct vfio_ccw_region *region)
{

}

const struct vfio_ccw_regops vfio_ccw_schib_region_ops = {
	.read = vfio_ccw_schib_region_read,
	.write = vfio_ccw_schib_region_write,
	.release = vfio_ccw_schib_region_release,
};

int vfio_ccw_register_schib_dev_regions(struct vfio_ccw_private *private)
{
	return vfio_ccw_register_dev_region(private,
					    VFIO_REGION_SUBTYPE_CCW_SCHIB,
					    &vfio_ccw_schib_region_ops,
					    sizeof(struct ccw_schib_region),
					    VFIO_REGION_INFO_FLAG_READ,
					    private->schib_region);
}
+20 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
struct workqueue_struct *vfio_ccw_work_q;
static struct kmem_cache *vfio_ccw_io_region;
static struct kmem_cache *vfio_ccw_cmd_region;
static struct kmem_cache *vfio_ccw_schib_region;

debug_info_t *vfio_ccw_debug_msg_id;
debug_info_t *vfio_ccw_debug_trace_id;
@@ -119,6 +120,8 @@ static void vfio_ccw_sch_irq(struct subchannel *sch)

static void vfio_ccw_free_regions(struct vfio_ccw_private *private)
{
	if (private->schib_region)
		kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
	if (private->cmd_region)
		kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
	if (private->io_region)
@@ -156,6 +159,12 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
	if (!private->cmd_region)
		goto out_free;

	private->schib_region = kmem_cache_zalloc(vfio_ccw_schib_region,
						  GFP_KERNEL | GFP_DMA);

	if (!private->schib_region)
		goto out_free;

	private->sch = sch;
	dev_set_drvdata(&sch->dev, private);
	mutex_init(&private->io_mutex);
@@ -357,6 +366,7 @@ static void vfio_ccw_debug_exit(void)

static void vfio_ccw_destroy_regions(void)
{
	kmem_cache_destroy(vfio_ccw_schib_region);
	kmem_cache_destroy(vfio_ccw_cmd_region);
	kmem_cache_destroy(vfio_ccw_io_region);
}
@@ -393,6 +403,16 @@ static int __init vfio_ccw_sch_init(void)
		goto out_err;
	}

	vfio_ccw_schib_region = kmem_cache_create_usercopy("vfio_ccw_schib_region",
					sizeof(struct ccw_schib_region), 0,
					SLAB_ACCOUNT, 0,
					sizeof(struct ccw_schib_region), NULL);

	if (!vfio_ccw_schib_region) {
		ret = -ENOMEM;
		goto out_err;
	}

	isc_register(VFIO_CCW_ISC);
	ret = css_driver_register(&vfio_ccw_sch_driver);
	if (ret) {
+12 −2
Original line number Diff line number Diff line
@@ -172,6 +172,16 @@ static int vfio_ccw_mdev_open(struct mdev_device *mdev)

	ret = vfio_ccw_register_async_dev_regions(private);
	if (ret)
		goto out_unregister;

	ret = vfio_ccw_register_schib_dev_regions(private);
	if (ret)
		goto out_unregister;

	return ret;

out_unregister:
	vfio_ccw_unregister_dev_regions(private);
	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
				 &private->nb);
	return ret;
Loading