diff options
Diffstat (limited to 'hw')
-rw-r--r-- | hw/s390x/css.c | 57 | ||||
-rw-r--r-- | hw/s390x/s390-ccw.c | 21 | ||||
-rw-r--r-- | hw/vfio/ccw.c | 208 |
3 files changed, 248 insertions, 38 deletions
diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 5d8e086..d1e365e 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -1335,11 +1335,20 @@ static void copy_schib_to_guest(SCHIB *dest, const SCHIB *src) } } -int css_do_stsch(SubchDev *sch, SCHIB *schib) +IOInstEnding css_do_stsch(SubchDev *sch, SCHIB *schib) { + int ret; + + /* + * For some subchannels, we may want to update parts of + * the schib (e.g., update path masks from the host device + * for passthrough subchannels). + */ + ret = s390_ccw_store(sch); + /* Use current status. */ copy_schib_to_guest(schib, &sch->curr_status); - return 0; + return ret; } static void copy_pmcw_from_guest(PMCW *dest, const PMCW *src) @@ -2161,30 +2170,23 @@ void css_subch_assign(uint8_t cssid, uint8_t ssid, uint16_t schid, } } -void css_queue_crw(uint8_t rsc, uint8_t erc, int solicited, - int chain, uint16_t rsid) +void css_crw_add_to_queue(CRW crw) { CrwContainer *crw_cont; - trace_css_crw(rsc, erc, rsid, chain ? "(chained)" : ""); + trace_css_crw((crw.flags & CRW_FLAGS_MASK_RSC) >> 8, + crw.flags & CRW_FLAGS_MASK_ERC, + crw.rsid, + (crw.flags & CRW_FLAGS_MASK_C) ? "(chained)" : ""); + /* TODO: Maybe use a static crw pool? */ crw_cont = g_try_new0(CrwContainer, 1); if (!crw_cont) { channel_subsys.crws_lost = true; return; } - crw_cont->crw.flags = (rsc << 8) | erc; - if (solicited) { - crw_cont->crw.flags |= CRW_FLAGS_MASK_S; - } - if (chain) { - crw_cont->crw.flags |= CRW_FLAGS_MASK_C; - } - crw_cont->crw.rsid = rsid; - if (channel_subsys.crws_lost) { - crw_cont->crw.flags |= CRW_FLAGS_MASK_R; - channel_subsys.crws_lost = false; - } + + crw_cont->crw = crw; QTAILQ_INSERT_TAIL(&channel_subsys.pending_crws, crw_cont, sibling); @@ -2195,6 +2197,27 @@ void css_queue_crw(uint8_t rsc, uint8_t erc, int solicited, } } +void css_queue_crw(uint8_t rsc, uint8_t erc, int solicited, + int chain, uint16_t rsid) +{ + CRW crw; + + crw.flags = (rsc << 8) | erc; + if (solicited) { + crw.flags |= CRW_FLAGS_MASK_S; + } + if (chain) { + crw.flags |= CRW_FLAGS_MASK_C; + } + crw.rsid = rsid; + if (channel_subsys.crws_lost) { + crw.flags |= CRW_FLAGS_MASK_R; + channel_subsys.crws_lost = false; + } + + css_crw_add_to_queue(crw); +} + void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, int hotplugged, int add) { diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index c48510f..b497571 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -51,6 +51,27 @@ int s390_ccw_clear(SubchDev *sch) return cdc->handle_clear(sch); } +IOInstEnding s390_ccw_store(SubchDev *sch) +{ + S390CCWDeviceClass *cdc = NULL; + int ret = IOINST_CC_EXPECTED; + + /* + * This code is called for both virtual and passthrough devices, + * but only applies to to the latter. This ugly check makes that + * distinction for us. + */ + if (object_dynamic_cast(OBJECT(sch->driver_data), TYPE_S390_CCW)) { + cdc = S390_CCW_DEVICE_GET_CLASS(sch->driver_data); + } + + if (cdc && cdc->handle_store) { + ret = cdc->handle_store(sch); + } + + return ret; +} + static void s390_ccw_get_dev_info(S390CCWDevice *cdev, char *sysfsdev, Error **errp) diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 6340618..06e69d7 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -41,7 +41,14 @@ struct VFIOCCWDevice { uint64_t async_cmd_region_size; uint64_t async_cmd_region_offset; struct ccw_cmd_region *async_cmd_region; + uint64_t schib_region_size; + uint64_t schib_region_offset; + struct ccw_schib_region *schib_region; + uint64_t crw_region_size; + uint64_t crw_region_offset; + struct ccw_crw_region *crw_region; EventNotifier io_notifier; + EventNotifier crw_notifier; bool force_orb_pfch; bool warned_orb_pfch; }; @@ -116,6 +123,51 @@ again: } } +static IOInstEnding vfio_ccw_handle_store(SubchDev *sch) +{ + S390CCWDevice *cdev = sch->driver_data; + VFIOCCWDevice *vcdev = DO_UPCAST(VFIOCCWDevice, cdev, cdev); + SCHIB *schib = &sch->curr_status; + struct ccw_schib_region *region = vcdev->schib_region; + SCHIB *s; + int ret; + + /* schib region not available so nothing else to do */ + if (!region) { + return IOINST_CC_EXPECTED; + } + + memset(region, 0, sizeof(*region)); + ret = pread(vcdev->vdev.fd, region, vcdev->schib_region_size, + vcdev->schib_region_offset); + + if (ret == -1) { + /* + * Device is probably damaged, but store subchannel does not + * have a nonzero cc defined for this scenario. Log an error, + * and presume things are otherwise fine. + */ + error_report("vfio-ccw: store region read failed with errno=%d", errno); + return IOINST_CC_EXPECTED; + } + + /* + * Selectively copy path-related bits of the SCHIB, + * rather than copying the entire struct. + */ + s = (SCHIB *)region->schib_area; + schib->pmcw.pnom = s->pmcw.pnom; + schib->pmcw.lpum = s->pmcw.lpum; + schib->pmcw.pam = s->pmcw.pam; + schib->pmcw.pom = s->pmcw.pom; + + if (s->scsw.flags & SCSW_FLAGS_MASK_PNO) { + schib->scsw.flags |= SCSW_FLAGS_MASK_PNO; + } + + return IOINST_CC_EXPECTED; +} + static int vfio_ccw_handle_clear(SubchDev *sch) { S390CCWDevice *cdev = sch->driver_data; @@ -206,6 +258,44 @@ static void vfio_ccw_reset(DeviceState *dev) ioctl(vcdev->vdev.fd, VFIO_DEVICE_RESET); } +static void vfio_ccw_crw_read(VFIOCCWDevice *vcdev) +{ + struct ccw_crw_region *region = vcdev->crw_region; + CRW crw; + int size; + + /* Keep reading CRWs as long as data is returned */ + do { + memset(region, 0, sizeof(*region)); + size = pread(vcdev->vdev.fd, region, vcdev->crw_region_size, + vcdev->crw_region_offset); + + if (size == -1) { + error_report("vfio-ccw: Read crw region failed with errno=%d", + errno); + break; + } + + if (region->crw == 0) { + /* No more CRWs to queue */ + break; + } + + memcpy(&crw, ®ion->crw, sizeof(CRW)); + + css_crw_add_to_queue(crw); + } while (1); +} + +static void vfio_ccw_crw_notifier_handler(void *opaque) +{ + VFIOCCWDevice *vcdev = opaque; + + while (event_notifier_test_and_clear(&vcdev->crw_notifier)) { + vfio_ccw_crw_read(vcdev); + } +} + static void vfio_ccw_io_notifier_handler(void *opaque) { VFIOCCWDevice *vcdev = opaque; @@ -276,22 +366,40 @@ read_err: css_inject_io_interrupt(sch); } -static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp) +static void vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, + unsigned int irq, + Error **errp) { VFIODevice *vdev = &vcdev->vdev; struct vfio_irq_info *irq_info; size_t argsz; int fd; + EventNotifier *notifier; + IOHandler *fd_read; + + switch (irq) { + case VFIO_CCW_IO_IRQ_INDEX: + notifier = &vcdev->io_notifier; + fd_read = vfio_ccw_io_notifier_handler; + break; + case VFIO_CCW_CRW_IRQ_INDEX: + notifier = &vcdev->crw_notifier; + fd_read = vfio_ccw_crw_notifier_handler; + break; + default: + error_setg(errp, "vfio: Unsupported device irq(%d)", irq); + return; + } - if (vdev->num_irqs < VFIO_CCW_IO_IRQ_INDEX + 1) { - error_setg(errp, "vfio: unexpected number of io irqs %u", + if (vdev->num_irqs < irq + 1) { + error_setg(errp, "vfio: unexpected number of irqs %u", vdev->num_irqs); return; } argsz = sizeof(*irq_info); irq_info = g_malloc0(argsz); - irq_info->index = VFIO_CCW_IO_IRQ_INDEX; + irq_info->index = irq; irq_info->argsz = argsz; if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, irq_info) < 0 || irq_info->count < 1) { @@ -299,37 +407,52 @@ static void vfio_ccw_register_io_notifier(VFIOCCWDevice *vcdev, Error **errp) goto out_free_info; } - if (event_notifier_init(&vcdev->io_notifier, 0)) { + if (event_notifier_init(notifier, 0)) { error_setg_errno(errp, errno, - "vfio: Unable to init event notifier for IO"); + "vfio: Unable to init event notifier for irq (%d)", + irq); goto out_free_info; } - fd = event_notifier_get_fd(&vcdev->io_notifier); - qemu_set_fd_handler(fd, vfio_ccw_io_notifier_handler, NULL, vcdev); + fd = event_notifier_get_fd(notifier); + qemu_set_fd_handler(fd, fd_read, NULL, vcdev); - if (vfio_set_irq_signaling(vdev, VFIO_CCW_IO_IRQ_INDEX, 0, + if (vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vcdev); - event_notifier_cleanup(&vcdev->io_notifier); + event_notifier_cleanup(notifier); } out_free_info: g_free(irq_info); } -static void vfio_ccw_unregister_io_notifier(VFIOCCWDevice *vcdev) +static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, + unsigned int irq) { Error *err = NULL; + EventNotifier *notifier; + + switch (irq) { + case VFIO_CCW_IO_IRQ_INDEX: + notifier = &vcdev->io_notifier; + break; + case VFIO_CCW_CRW_IRQ_INDEX: + notifier = &vcdev->crw_notifier; + break; + default: + error_report("vfio: Unsupported device irq(%d)", irq); + return; + } - if (vfio_set_irq_signaling(&vcdev->vdev, VFIO_CCW_IO_IRQ_INDEX, 0, + if (vfio_set_irq_signaling(&vcdev->vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); } - qemu_set_fd_handler(event_notifier_get_fd(&vcdev->io_notifier), + qemu_set_fd_handler(event_notifier_get_fd(notifier), NULL, NULL, vcdev); - event_notifier_cleanup(&vcdev->io_notifier); + event_notifier_cleanup(notifier); } static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) @@ -363,8 +486,7 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) vcdev->io_region_size = info->size; if (sizeof(*vcdev->io_region) != vcdev->io_region_size) { error_setg(errp, "vfio: Unexpected size of the I/O region"); - g_free(info); - return; + goto out_err; } vcdev->io_region_offset = info->offset; @@ -377,19 +499,53 @@ static void vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) 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; + goto out_err; } vcdev->async_cmd_region_offset = info->offset; vcdev->async_cmd_region = g_malloc0(info->size); } + ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_SCHIB, &info); + if (!ret) { + vcdev->schib_region_size = info->size; + if (sizeof(*vcdev->schib_region) != vcdev->schib_region_size) { + error_setg(errp, "vfio: Unexpected size of the schib region"); + goto out_err; + } + vcdev->schib_region_offset = info->offset; + vcdev->schib_region = g_malloc(info->size); + } + + ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_CRW, &info); + + if (!ret) { + vcdev->crw_region_size = info->size; + if (sizeof(*vcdev->crw_region) != vcdev->crw_region_size) { + error_setg(errp, "vfio: Unexpected size of the CRW region"); + goto out_err; + } + vcdev->crw_region_offset = info->offset; + vcdev->crw_region = g_malloc(info->size); + } + g_free(info); + return; + +out_err: + g_free(vcdev->crw_region); + g_free(vcdev->schib_region); + g_free(vcdev->async_cmd_region); + g_free(vcdev->io_region); + g_free(info); + return; } static void vfio_ccw_put_region(VFIOCCWDevice *vcdev) { + g_free(vcdev->crw_region); + g_free(vcdev->schib_region); g_free(vcdev->async_cmd_region); g_free(vcdev->io_region); } @@ -499,11 +655,19 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) goto out_region_err; } - vfio_ccw_register_io_notifier(vcdev, &err); + vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX, &err); if (err) { goto out_notifier_err; } + if (vcdev->crw_region) { + vfio_ccw_register_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX, &err); + if (err) { + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); + goto out_notifier_err; + } + } + return; out_notifier_err: @@ -528,7 +692,8 @@ static void vfio_ccw_unrealize(DeviceState *dev) S390CCWDeviceClass *cdc = S390_CCW_DEVICE_GET_CLASS(cdev); VFIOGroup *group = vcdev->vdev.group; - vfio_ccw_unregister_io_notifier(vcdev); + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); + vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); vfio_ccw_put_region(vcdev); vfio_ccw_put_device(vcdev); vfio_put_group(group); @@ -565,6 +730,7 @@ static void vfio_ccw_class_init(ObjectClass *klass, void *data) cdc->handle_request = vfio_ccw_handle_request; cdc->handle_halt = vfio_ccw_handle_halt; cdc->handle_clear = vfio_ccw_handle_clear; + cdc->handle_store = vfio_ccw_handle_store; } static const TypeInfo vfio_ccw_info = { |