aboutsummaryrefslogtreecommitdiff
path: root/hw/vfio
diff options
context:
space:
mode:
authorCornelia Huck <cohuck@redhat.com>2019-05-07 17:47:33 +0200
committerCornelia Huck <cohuck@redhat.com>2019-06-24 17:27:57 +0200
commit8fadea24de4ef83dd2db3a9cd06b384fd820b9db (patch)
treeaab722dbd72a8690a2cc024a879cd95802873656 /hw/vfio
parentb5e89f044dea477cef0f8bf5da52035369d95157 (diff)
vfio-ccw: support async command subregion
A vfio-ccw device may provide an async command subregion for issuing halt/clear subchannel requests. If it is present, use it for sending halt/clear request to the device; if not, fall back to emulation (as done today). Reviewed-by: Farhan Ali <alifm@linux.ibm.com> Message-Id: <20190613092542.2834-1-cohuck@redhat.com> Reviewed-by: Eric Farman <farman@linux.ibm.com> Signed-off-by: Cornelia Huck <cohuck@redhat.com>
Diffstat (limited to 'hw/vfio')
-rw-r--r--hw/vfio/ccw.c113
1 files changed, 111 insertions, 2 deletions
diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c
index a18bbd33b0..6d0296fe4d 100644
--- a/hw/vfio/ccw.c
+++ b/hw/vfio/ccw.c
@@ -2,9 +2,12 @@
* vfio based subchannel assignment support
*
* Copyright 2017 IBM Corp.
+ * Copyright 2019 Red Hat, Inc.
+ *
* Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
* Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
* Pierre Morel <pmorel@linux.vnet.ibm.com>
+ * Cornelia Huck <cohuck@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
@@ -33,6 +36,9 @@ struct VFIOCCWDevice {
uint64_t io_region_size;
uint64_t io_region_offset;
struct ccw_io_region *io_region;
+ uint64_t async_cmd_region_size;
+ uint64_t async_cmd_region_offset;
+ struct ccw_cmd_region *async_cmd_region;
EventNotifier io_notifier;
bool force_orb_pfch;
bool warned_orb_pfch;
@@ -115,6 +121,87 @@ again:
}
}
+static int vfio_ccw_handle_clear(SubchDev *sch)
+{
+ S390CCWDevice *cdev = sch->driver_data;
+ VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
+ struct ccw_cmd_region *region = vcdev->async_cmd_region;
+ int ret;
+
+ if (!vcdev->async_cmd_region) {
+ /* Async command region not available, fall back to emulation */
+ return -ENOSYS;
+ }
+
+ memset(region, 0, sizeof(*region));
+ region->command = VFIO_CCW_ASYNC_CMD_CSCH;
+
+again:
+ ret = pwrite(vcdev->vdev.fd, region,
+ vcdev->async_cmd_region_size, vcdev->async_cmd_region_offset);
+ if (ret != vcdev->async_cmd_region_size) {
+ if (errno == EAGAIN) {
+ goto again;
+ }
+ error_report("vfio-ccw: write cmd region failed with errno=%d", errno);
+ ret = -errno;
+ } else {
+ ret = region->ret_code;
+ }
+ switch (ret) {
+ case 0:
+ case -ENODEV:
+ case -EACCES:
+ return 0;
+ case -EFAULT:
+ default:
+ sch_gen_unit_exception(sch);
+ css_inject_io_interrupt(sch);
+ return 0;
+ }
+}
+
+static int vfio_ccw_handle_halt(SubchDev *sch)
+{
+ S390CCWDevice *cdev = sch->driver_data;
+ VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev);
+ struct ccw_cmd_region *region = vcdev->async_cmd_region;
+ int ret;
+
+ if (!vcdev->async_cmd_region) {
+ /* Async command region not available, fall back to emulation */
+ return -ENOSYS;
+ }
+
+ memset(region, 0, sizeof(*region));
+ region->command = VFIO_CCW_ASYNC_CMD_HSCH;
+
+again:
+ ret = pwrite(vcdev->vdev.fd, region,
+ vcdev->async_cmd_region_size, vcdev->async_cmd_region_offset);
+ if (ret != vcdev->async_cmd_region_size) {
+ if (errno == EAGAIN) {
+ goto again;
+ }
+ error_report("vfio-ccw: write cmd region failed with errno=%d", errno);
+ ret = -errno;
+ } else {
+ ret = region->ret_code;
+ }
+ switch (ret) {
+ case 0:
+ case -EBUSY:
+ case -ENODEV:
+ case -EACCES:
+ return 0;
+ case -EFAULT:
+ default:
+ sch_gen_unit_exception(sch);
+ css_inject_io_interrupt(sch);
+ return 0;
+ }
+}
+
static void vfio_ccw_reset(DeviceState *dev)
{
CcwDevice *ccw_dev = DO_UPCAST(CcwDevice, parent_obj, dev);
@@ -263,9 +350,13 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
return;
}
+ /*
+ * We always expect at least the I/O region to be present. We also
+ * may have a variable number of regions governed by capabilities.
+ */
if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) {
- error_setg(errp, "vfio: Unexpected number of the I/O region %u",
- vdev->num_regions);
+ error_setg(errp, "vfio: too few regions (%u), expected at least %u",
+ vdev->num_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1);
return;
}
@@ -285,11 +376,27 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp)
vcdev->io_region_offset = info->offset;
vcdev->io_region = g_malloc0(info->size);
+ /* check for the optional async command region */
+ ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW,
+ VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, &info);
+ if (!ret) {
+ vcdev->async_cmd_region_size = info->size;
+ if (sizeof(*vcdev->async_cmd_region) != vcdev->async_cmd_region_size) {
+ error_setg(errp, "vfio: Unexpected size of the async cmd region");
+ g_free(vcdev->io_region);
+ g_free(info);
+ return;
+ }
+ vcdev->async_cmd_region_offset = info->offset;
+ vcdev->async_cmd_region = g_malloc0(info->size);
+ }
+
g_free(info);
}
static void vfio_ccw_put_region(VFIOCCWDevice *vcdev)
{
+ g_free(vcdev->async_cmd_region);
g_free(vcdev->io_region);
}
@@ -462,6 +569,8 @@ static void vfio_ccw_class_init(ObjectClass *klass, void *data)
dc->reset = vfio_ccw_reset;
cdc->handle_request = vfio_ccw_handle_request;
+ cdc->handle_halt = vfio_ccw_handle_halt;
+ cdc->handle_clear = vfio_ccw_handle_clear;
}
static const TypeInfo vfio_ccw_info = {