From 61c7bbd236b90da7531fcf957223a2456670d44d Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 12 May 2014 11:51:37 +0200 Subject: kvm: Fix enable_cap helpers on older gcc Commit 40f1ee27aa1 introduced handy helpers for enable_cap calls on vcpu and vm level. Unfortunately some older gcc versions (4.7.1, 4.6) seem to choke on signedness detection in inline created variables: target-ppc/kvm.c: In function 'kvmppc_booke_watchdog_enable': target-ppc/kvm.c:1302:21: error: comparison of unsigned expression < 0 is always false [-Werror=type-limits] target-ppc/kvm.c: In function 'kvmppc_set_papr': target-ppc/kvm.c:1504:21: error: comparison of unsigned expression < 0 is always false [-Werror=type-limits] However - thanks to Thomas Huth for the suggestion - we can just cast the offending potentially 0 value to a signed type, making the comparison signed. Reviewed-by: Thomas Huth Acked-by: Cornelia Huck Signed-off-by: Alexander Graf Signed-off-by: Cornelia Huck --- include/sysemu/kvm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index e7ad9d159a..25c8a2bdfd 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -300,7 +300,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension); }; \ uint64_t args_tmp[] = { __VA_ARGS__ }; \ int i; \ - for (i = 0; i < ARRAY_SIZE(args_tmp) && \ + for (i = 0; i < (int)ARRAY_SIZE(args_tmp) && \ i < ARRAY_SIZE(cap.args); i++) { \ cap.args[i] = args_tmp[i]; \ } \ @@ -315,7 +315,7 @@ int kvm_check_extension(KVMState *s, unsigned int extension); }; \ uint64_t args_tmp[] = { __VA_ARGS__ }; \ int i; \ - for (i = 0; i < ARRAY_SIZE(args_tmp) && \ + for (i = 0; i < (int)ARRAY_SIZE(args_tmp) && \ i < ARRAY_SIZE(cap.args); i++) { \ cap.args[i] = args_tmp[i]; \ } \ -- cgit v1.2.3-55-g7522 From 7b35d0c44cae3dcce6347a0729a416c2929cd4bb Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Thu, 17 Apr 2014 15:59:48 +0200 Subject: s390x: split flic into kvm and non-kvm parts Introduce a common parent class for both cases, where kvm and non-kvm can hook up callbacks. This will be used by follow-on patches for adapter registration and mapping. We now always have a flic, regardless of whether we use kvm; the non-kvm implementation just doesn't do anything. Reviewed-by: Jens Freimann Reviewed-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- default-configs/s390x-softmmu.mak | 3 +- hw/intc/Makefile.objs | 1 + hw/intc/s390_flic.c | 318 ++++--------------------------------- hw/intc/s390_flic_kvm.c | 325 ++++++++++++++++++++++++++++++++++++++ include/hw/s390x/s390_flic.h | 51 ++++-- 5 files changed, 399 insertions(+), 299 deletions(-) create mode 100644 hw/intc/s390_flic_kvm.c (limited to 'include') diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index d843dc0d57..126d88dc15 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -1,3 +1,4 @@ CONFIG_VIRTIO=y CONFIG_SCLPCONSOLE=y -CONFIG_S390_FLIC=$(CONFIG_KVM) +CONFIG_S390_FLIC=y +CONFIG_S390_FLIC_KVM=$(CONFIG_KVM) diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index c8a2318d56..843864a3ef 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -26,3 +26,4 @@ obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o obj-$(CONFIG_S390_FLIC) += s390_flic.o +obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index b2ef3e3f8e..7dc8c7d145 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -1,322 +1,66 @@ /* - * QEMU S390x KVM floating interrupt controller (flic) + * QEMU S390x floating interrupt controller (flic) * * Copyright 2014 IBM Corp. * Author(s): Jens Freimann + * Cornelia Huck * * 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 * directory. */ -#include #include "qemu/error-report.h" #include "hw/sysbus.h" -#include "sysemu/kvm.h" #include "migration/qemu-file.h" #include "hw/s390x/s390_flic.h" #include "trace.h" -#define FLIC_SAVE_INITIAL_SIZE getpagesize() -#define FLIC_FAILED (-1UL) -#define FLIC_SAVEVM_VERSION 1 - -void s390_flic_init(void) -{ - DeviceState *dev; - int r; - - if (kvm_enabled()) { - dev = qdev_create(NULL, "s390-flic"); - object_property_add_child(qdev_get_machine(), "s390-flic", - OBJECT(dev), NULL); - r = qdev_init(dev); - if (r) { - error_report("flic: couldn't create qdev"); - } - } -} - -/** - * flic_get_all_irqs - store all pending irqs in buffer - * @buf: pointer to buffer which is passed to kernel - * @len: length of buffer - * @flic: pointer to flic device state - * - * Returns: -ENOMEM if buffer is too small, - * -EINVAL if attr.group is invalid, - * -EFAULT if copying to userspace failed, - * on success return number of stored interrupts - */ -static int flic_get_all_irqs(KVMS390FLICState *flic, - void *buf, int len) -{ - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_GET_ALL_IRQS, - .addr = (uint64_t) buf, - .attr = len, - }; - int rc; - - rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr); - - return rc == -1 ? -errno : rc; -} - -static void flic_enable_pfault(KVMS390FLICState *flic) -{ - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_APF_ENABLE, - }; - int rc; - - rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - - if (rc) { - fprintf(stderr, "flic: couldn't enable pfault\n"); - } -} - -static void flic_disable_wait_pfault(KVMS390FLICState *flic) +S390FLICState *s390_get_flic(void) { - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_APF_DISABLE_WAIT, - }; - int rc; - - rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + S390FLICState *fs; - if (rc) { - fprintf(stderr, "flic: couldn't disable pfault\n"); + fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL)); + if (!fs) { + fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL)); } + return fs; } -/** flic_enqueue_irqs - returns 0 on success - * @buf: pointer to buffer which is passed to kernel - * @len: length of buffer - * @flic: pointer to flic device state - * - * Returns: -EINVAL if attr.group is unknown - */ -static int flic_enqueue_irqs(void *buf, uint64_t len, - KVMS390FLICState *flic) -{ - int rc; - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_ENQUEUE, - .addr = (uint64_t) buf, - .attr = len, - }; - - rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - - return rc ? -errno : 0; -} - -/** - * __get_all_irqs - store all pending irqs in buffer - * @flic: pointer to flic device state - * @buf: pointer to pointer to a buffer - * @len: length of buffer - * - * Returns: return value of flic_get_all_irqs - * Note: Retry and increase buffer size until flic_get_all_irqs - * either returns a value >= 0 or a negative error code. - * -ENOMEM is an exception, which means the buffer is too small - * and we should try again. Other negative error codes can be - * -EFAULT and -EINVAL which we ignore at this point - */ -static int __get_all_irqs(KVMS390FLICState *flic, - void **buf, int len) +void s390_flic_init(void) { + DeviceState *dev; int r; - do { - /* returns -ENOMEM if buffer is too small and number - * of queued interrupts on success */ - r = flic_get_all_irqs(flic, *buf, len); - if (r >= 0) { - break; - } - len *= 2; - *buf = g_try_realloc(*buf, len); - if (!buf) { - return -ENOMEM; - } - } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER); - - return r; -} - -/** - * kvm_flic_save - Save pending floating interrupts - * @f: QEMUFile containing migration state - * @opaque: pointer to flic device state - * - * Note: Pass buf and len to kernel. Start with one page and - * increase until buffer is sufficient or maxium size is - * reached - */ -static void kvm_flic_save(QEMUFile *f, void *opaque) -{ - KVMS390FLICState *flic = opaque; - int len = FLIC_SAVE_INITIAL_SIZE; - void *buf; - int count; - - flic_disable_wait_pfault((struct KVMS390FLICState *) opaque); - - buf = g_try_malloc0(len); - if (!buf) { - /* Storing FLIC_FAILED into the count field here will cause the - * target system to fail when attempting to load irqs from the - * migration state */ - error_report("flic: couldn't allocate memory"); - qemu_put_be64(f, FLIC_FAILED); - return; + dev = s390_flic_kvm_create(); + if (!dev) { + dev = qdev_create(NULL, TYPE_QEMU_S390_FLIC); + object_property_add_child(qdev_get_machine(), TYPE_QEMU_S390_FLIC, + OBJECT(dev), NULL); } - - count = __get_all_irqs(flic, &buf, len); - if (count < 0) { - error_report("flic: couldn't retrieve irqs from kernel, rc %d", - count); - /* Storing FLIC_FAILED into the count field here will cause the - * target system to fail when attempting to load irqs from the - * migration state */ - qemu_put_be64(f, FLIC_FAILED); - } else { - qemu_put_be64(f, count); - qemu_put_buffer(f, (uint8_t *) buf, - count * sizeof(struct kvm_s390_irq)); + r = qdev_init(dev); + if (r) { + error_report("flic: couldn't create qdev"); } - g_free(buf); } -/** - * kvm_flic_load - Load pending floating interrupts - * @f: QEMUFile containing migration state - * @opaque: pointer to flic device state - * @version_id: version id for migration - * - * Returns: value of flic_enqueue_irqs, -EINVAL on error - * Note: Do nothing when no interrupts where stored - * in QEMUFile - */ -static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id) -{ - uint64_t len = 0; - uint64_t count = 0; - void *buf = NULL; - int r = 0; - - if (version_id != FLIC_SAVEVM_VERSION) { - r = -EINVAL; - goto out; - } - - flic_enable_pfault((struct KVMS390FLICState *) opaque); - - count = qemu_get_be64(f); - len = count * sizeof(struct kvm_s390_irq); - if (count == FLIC_FAILED) { - r = -EINVAL; - goto out; - } - if (count == 0) { - r = 0; - goto out; - } - buf = g_try_malloc0(len); - if (!buf) { - r = -ENOMEM; - goto out; - } - - if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) { - r = -EINVAL; - goto out_free; - } - r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque); - -out_free: - g_free(buf); -out: - return r; -} - -static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) -{ - KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); - struct kvm_create_device cd = {0}; - int ret; - - flic_state->fd = -1; - if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { - trace_flic_no_device_api(errno); - return; - } - - cd.type = KVM_DEV_TYPE_FLIC; - ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); - if (ret < 0) { - trace_flic_create_device(errno); - return; - } - flic_state->fd = cd.fd; - - /* Register savevm handler for floating interrupts */ - register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save, - kvm_flic_load, (void *) flic_state); -} - -static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp) -{ - KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); - - unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state); -} - -static void kvm_s390_flic_reset(DeviceState *dev) -{ - KVMS390FLICState *flic = KVM_S390_FLIC(dev); - struct kvm_device_attr attr = { - .group = KVM_DEV_FLIC_CLEAR_IRQS, - }; - int rc = 0; - - if (flic->fd == -1) { - return; - } - - flic_disable_wait_pfault(flic); - - rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); - if (rc) { - trace_flic_reset_failed(errno); - } - - flic_enable_pfault(flic); -} - -static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = kvm_s390_flic_realize; - dc->unrealize = kvm_s390_flic_unrealize; - dc->reset = kvm_s390_flic_reset; -} +static const TypeInfo qemu_s390_flic_info = { + .name = TYPE_QEMU_S390_FLIC, + .parent = TYPE_S390_FLIC_COMMON, + .instance_size = sizeof(QEMUS390FLICState), +}; -static const TypeInfo kvm_s390_flic_info = { - .name = TYPE_KVM_S390_FLIC, +static const TypeInfo s390_flic_common_info = { + .name = TYPE_S390_FLIC_COMMON, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(KVMS390FLICState), - .class_init = kvm_s390_flic_class_init, + .instance_size = sizeof(S390FLICState), + .class_size = sizeof(S390FLICStateClass), }; -static void kvm_s390_flic_register_types(void) +static void qemu_s390_flic_register_types(void) { - type_register_static(&kvm_s390_flic_info); + type_register_static(&s390_flic_common_info); + type_register_static(&qemu_s390_flic_info); } -type_init(kvm_s390_flic_register_types) +type_init(qemu_s390_flic_register_types) diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c new file mode 100644 index 0000000000..70c1710ec3 --- /dev/null +++ b/hw/intc/s390_flic_kvm.c @@ -0,0 +1,325 @@ +/* + * QEMU S390x KVM floating interrupt controller (flic) + * + * Copyright 2014 IBM Corp. + * Author(s): Jens Freimann + * Cornelia Huck + * + * 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 + * directory. + */ + +#include +#include "qemu/error-report.h" +#include "hw/sysbus.h" +#include "sysemu/kvm.h" +#include "migration/qemu-file.h" +#include "hw/s390x/s390_flic.h" +#include "trace.h" + +#define FLIC_SAVE_INITIAL_SIZE getpagesize() +#define FLIC_FAILED (-1UL) +#define FLIC_SAVEVM_VERSION 1 + +typedef struct KVMS390FLICState { + S390FLICState parent_obj; + + uint32_t fd; +} KVMS390FLICState; + +DeviceState *s390_flic_kvm_create(void) +{ + DeviceState *dev = NULL; + + if (kvm_enabled()) { + dev = qdev_create(NULL, TYPE_KVM_S390_FLIC); + object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC, + OBJECT(dev), NULL); + } + return dev; +} + +/** + * flic_get_all_irqs - store all pending irqs in buffer + * @buf: pointer to buffer which is passed to kernel + * @len: length of buffer + * @flic: pointer to flic device state + * + * Returns: -ENOMEM if buffer is too small, + * -EINVAL if attr.group is invalid, + * -EFAULT if copying to userspace failed, + * on success return number of stored interrupts + */ +static int flic_get_all_irqs(KVMS390FLICState *flic, + void *buf, int len) +{ + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_GET_ALL_IRQS, + .addr = (uint64_t) buf, + .attr = len, + }; + int rc; + + rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr); + + return rc == -1 ? -errno : rc; +} + +static void flic_enable_pfault(KVMS390FLICState *flic) +{ + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_APF_ENABLE, + }; + int rc; + + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + + if (rc) { + fprintf(stderr, "flic: couldn't enable pfault\n"); + } +} + +static void flic_disable_wait_pfault(KVMS390FLICState *flic) +{ + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_APF_DISABLE_WAIT, + }; + int rc; + + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + + if (rc) { + fprintf(stderr, "flic: couldn't disable pfault\n"); + } +} + +/** flic_enqueue_irqs - returns 0 on success + * @buf: pointer to buffer which is passed to kernel + * @len: length of buffer + * @flic: pointer to flic device state + * + * Returns: -EINVAL if attr.group is unknown + */ +static int flic_enqueue_irqs(void *buf, uint64_t len, + KVMS390FLICState *flic) +{ + int rc; + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_ENQUEUE, + .addr = (uint64_t) buf, + .attr = len, + }; + + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + + return rc ? -errno : 0; +} + +/** + * __get_all_irqs - store all pending irqs in buffer + * @flic: pointer to flic device state + * @buf: pointer to pointer to a buffer + * @len: length of buffer + * + * Returns: return value of flic_get_all_irqs + * Note: Retry and increase buffer size until flic_get_all_irqs + * either returns a value >= 0 or a negative error code. + * -ENOMEM is an exception, which means the buffer is too small + * and we should try again. Other negative error codes can be + * -EFAULT and -EINVAL which we ignore at this point + */ +static int __get_all_irqs(KVMS390FLICState *flic, + void **buf, int len) +{ + int r; + + do { + /* returns -ENOMEM if buffer is too small and number + * of queued interrupts on success */ + r = flic_get_all_irqs(flic, *buf, len); + if (r >= 0) { + break; + } + len *= 2; + *buf = g_try_realloc(*buf, len); + if (!buf) { + return -ENOMEM; + } + } while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER); + + return r; +} + +/** + * kvm_flic_save - Save pending floating interrupts + * @f: QEMUFile containing migration state + * @opaque: pointer to flic device state + * + * Note: Pass buf and len to kernel. Start with one page and + * increase until buffer is sufficient or maxium size is + * reached + */ +static void kvm_flic_save(QEMUFile *f, void *opaque) +{ + KVMS390FLICState *flic = opaque; + int len = FLIC_SAVE_INITIAL_SIZE; + void *buf; + int count; + + flic_disable_wait_pfault((struct KVMS390FLICState *) opaque); + + buf = g_try_malloc0(len); + if (!buf) { + /* Storing FLIC_FAILED into the count field here will cause the + * target system to fail when attempting to load irqs from the + * migration state */ + error_report("flic: couldn't allocate memory"); + qemu_put_be64(f, FLIC_FAILED); + return; + } + + count = __get_all_irqs(flic, &buf, len); + if (count < 0) { + error_report("flic: couldn't retrieve irqs from kernel, rc %d", + count); + /* Storing FLIC_FAILED into the count field here will cause the + * target system to fail when attempting to load irqs from the + * migration state */ + qemu_put_be64(f, FLIC_FAILED); + } else { + qemu_put_be64(f, count); + qemu_put_buffer(f, (uint8_t *) buf, + count * sizeof(struct kvm_s390_irq)); + } + g_free(buf); +} + +/** + * kvm_flic_load - Load pending floating interrupts + * @f: QEMUFile containing migration state + * @opaque: pointer to flic device state + * @version_id: version id for migration + * + * Returns: value of flic_enqueue_irqs, -EINVAL on error + * Note: Do nothing when no interrupts where stored + * in QEMUFile + */ +static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id) +{ + uint64_t len = 0; + uint64_t count = 0; + void *buf = NULL; + int r = 0; + + if (version_id != FLIC_SAVEVM_VERSION) { + r = -EINVAL; + goto out; + } + + flic_enable_pfault((struct KVMS390FLICState *) opaque); + + count = qemu_get_be64(f); + len = count * sizeof(struct kvm_s390_irq); + if (count == FLIC_FAILED) { + r = -EINVAL; + goto out; + } + if (count == 0) { + r = 0; + goto out; + } + buf = g_try_malloc0(len); + if (!buf) { + r = -ENOMEM; + goto out; + } + + if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) { + r = -EINVAL; + goto out_free; + } + r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque); + +out_free: + g_free(buf); +out: + return r; +} + +static void kvm_s390_flic_realize(DeviceState *dev, Error **errp) +{ + KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); + struct kvm_create_device cd = {0}; + int ret; + + flic_state->fd = -1; + if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) { + trace_flic_no_device_api(errno); + return; + } + + cd.type = KVM_DEV_TYPE_FLIC; + ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd); + if (ret < 0) { + trace_flic_create_device(errno); + return; + } + flic_state->fd = cd.fd; + + /* Register savevm handler for floating interrupts */ + register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save, + kvm_flic_load, (void *) flic_state); +} + +static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp) +{ + KVMS390FLICState *flic_state = KVM_S390_FLIC(dev); + + unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state); +} + +static void kvm_s390_flic_reset(DeviceState *dev) +{ + KVMS390FLICState *flic = KVM_S390_FLIC(dev); + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_CLEAR_IRQS, + }; + int rc = 0; + + if (flic->fd == -1) { + return; + } + + flic_disable_wait_pfault(flic); + + rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + if (rc) { + trace_flic_reset_failed(errno); + } + + flic_enable_pfault(flic); +} + +static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = kvm_s390_flic_realize; + dc->unrealize = kvm_s390_flic_unrealize; + dc->reset = kvm_s390_flic_reset; +} + +static const TypeInfo kvm_s390_flic_info = { + .name = TYPE_KVM_S390_FLIC, + .parent = TYPE_S390_FLIC_COMMON, + .instance_size = sizeof(KVMS390FLICState), + .class_init = kvm_s390_flic_class_init, +}; + +static void kvm_s390_flic_register_types(void) +{ + type_register_static(&kvm_s390_flic_info); +} + +type_init(kvm_s390_flic_register_types) diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 497b219e30..3bc60fd77f 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -1,33 +1,62 @@ /* - * QEMU S390x KVM floating interrupt controller (flic) + * QEMU S390x floating interrupt controller (flic) * * Copyright 2014 IBM Corp. * Author(s): Jens Freimann + * Cornelia Huck * * 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 * directory. */ -#ifndef __KVM_S390_FLIC_H -#define __KVM_S390_FLIC_H +#ifndef __HW_S390_FLIC_H +#define __HW_S390_FLIC_H #include "hw/sysbus.h" -#define TYPE_KVM_S390_FLIC "s390-flic" +#define TYPE_S390_FLIC_COMMON "s390-flic" +#define S390_FLIC_COMMON(obj) \ + OBJECT_CHECK(S390FLICState, (obj), TYPE_S390_FLIC_COMMON) + +typedef struct S390FLICState { + SysBusDevice parent_obj; + +} S390FLICState; + +#define S390_FLIC_COMMON_CLASS(klass) \ + OBJECT_CLASS_CHECK(S390FLICStateClass, (klass), TYPE_S390_FLIC_COMMON) +#define S390_FLIC_COMMON_GET_CLASS(obj) \ + OBJECT_GET_CLASS(S390FLICStateClass, (obj), TYPE_S390_FLIC_COMMON) + +typedef struct S390FLICStateClass { + DeviceClass parent_class; + +} S390FLICStateClass; + +#define TYPE_KVM_S390_FLIC "s390-flic-kvm" #define KVM_S390_FLIC(obj) \ OBJECT_CHECK(KVMS390FLICState, (obj), TYPE_KVM_S390_FLIC) -typedef struct KVMS390FLICState { - SysBusDevice parent_obj; +#define TYPE_QEMU_S390_FLIC "s390-flic-qemu" +#define QEMU_S390_FLIC(obj) \ + OBJECT_CHECK(QEMUS390FLICState, (obj), TYPE_QEMU_S390_FLIC) - uint32_t fd; -} KVMS390FLICState; +typedef struct QEMUS390FLICState { + S390FLICState parent_obj; +} QEMUS390FLICState; -#ifdef CONFIG_KVM void s390_flic_init(void); + +S390FLICState *s390_get_flic(void); + +#ifdef CONFIG_KVM +DeviceState *s390_flic_kvm_create(void); #else -static inline void s390_flic_init(void) { } +static inline DeviceState *s390_flic_kvm_create(void) +{ + return NULL; +} #endif -#endif /* __KVM_S390_FLIC_H */ +#endif /* __HW_S390_FLIC_H */ -- cgit v1.2.3-55-g7522 From 03cf077ac9c02ccc192b9011ced12cc74f9151bb Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 15 Jul 2013 17:45:03 +0200 Subject: s390x: add I/O adapter registration Register an I/O adapter interrupt source for when virtio-ccw devices start using adapter interrupts. Reviewed-by: Thomas Huth Reviewed-by: Christian Borntraeger Signed-off-by: Cornelia Huck --- hw/intc/s390_flic.c | 16 ++++++++++++++ hw/intc/s390_flic_kvm.c | 29 +++++++++++++++++++++++++ hw/s390x/css.c | 50 ++++++++++++++++++++++++++++++++++++++++++++ hw/s390x/css.h | 4 ++++ hw/s390x/virtio-ccw.c | 4 ++++ hw/s390x/virtio-ccw.h | 1 + include/hw/s390x/s390_flic.h | 2 ++ 7 files changed, 106 insertions(+) (limited to 'include') diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 7dc8c7d145..2b568354cd 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -44,10 +44,26 @@ void s390_flic_init(void) } } +static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id, + uint8_t isc, bool swap, + bool is_maskable) +{ + /* nothing to do */ + return 0; +} + +static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) +{ + S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); + + fsc->register_io_adapter = qemu_s390_register_io_adapter; +} + static const TypeInfo qemu_s390_flic_info = { .name = TYPE_QEMU_S390_FLIC, .parent = TYPE_S390_FLIC_COMMON, .instance_size = sizeof(QEMUS390FLICState), + .class_init = qemu_s390_flic_class_init, }; static const TypeInfo s390_flic_common_info = { diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index 70c1710ec3..cc4072e5df 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -151,6 +151,33 @@ static int __get_all_irqs(KVMS390FLICState *flic, return r; } +static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, + uint8_t isc, bool swap, + bool is_maskable) +{ + struct kvm_s390_io_adapter adapter = { + .id = id, + .isc = isc, + .maskable = is_maskable, + .swap = swap, + }; + KVMS390FLICState *flic = KVM_S390_FLIC(fs); + int r, ret; + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_ADAPTER_REGISTER, + .addr = (uint64_t)&adapter, + }; + + if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) { + return -ENOSYS; + } + + r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + + ret = r ? -errno : 0; + return ret; +} + /** * kvm_flic_save - Save pending floating interrupts * @f: QEMUFile containing migration state @@ -304,10 +331,12 @@ static void kvm_s390_flic_reset(DeviceState *dev) static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); + S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); dc->realize = kvm_s390_flic_realize; dc->unrealize = kvm_s390_flic_unrealize; dc->reset = kvm_s390_flic_reset; + fsc->register_io_adapter = kvm_s390_register_io_adapter; } static const TypeInfo kvm_s390_flic_info = { diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 122cc7e66f..2678e4432c 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -16,6 +16,7 @@ #include "ioinst.h" #include "css.h" #include "trace.h" +#include "hw/s390x/s390_flic.h" typedef struct CrwContainer { CRW crw; @@ -39,6 +40,13 @@ typedef struct CssImage { ChpInfo chpids[MAX_CHPID + 1]; } CssImage; +typedef struct IoAdapter { + uint32_t id; + uint8_t type; + uint8_t isc; + QTAILQ_ENTRY(IoAdapter) sibling; +} IoAdapter; + typedef struct ChannelSubSys { QTAILQ_HEAD(, CrwContainer) pending_crws; bool do_crw_mchk; @@ -49,6 +57,7 @@ typedef struct ChannelSubSys { uint64_t chnmon_area; CssImage *css[MAX_CSSID + 1]; uint8_t default_cssid; + QTAILQ_HEAD(, IoAdapter) io_adapters; } ChannelSubSys; static ChannelSubSys *channel_subsys; @@ -69,6 +78,46 @@ int css_create_css_image(uint8_t cssid, bool default_image) return 0; } +int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, + bool maskable, uint32_t *id) +{ + IoAdapter *adapter; + bool found = false; + int ret; + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + + *id = 0; + QTAILQ_FOREACH(adapter, &channel_subsys->io_adapters, sibling) { + if ((adapter->type == type) && (adapter->isc == isc)) { + *id = adapter->id; + found = true; + ret = 0; + break; + } + if (adapter->id >= *id) { + *id = adapter->id + 1; + } + } + if (found) { + goto out; + } + adapter = g_new0(IoAdapter, 1); + ret = fsc->register_io_adapter(fs, *id, isc, swap, maskable); + if (ret == 0) { + adapter->id = *id; + adapter->isc = isc; + adapter->type = type; + QTAILQ_INSERT_TAIL(&channel_subsys->io_adapters, adapter, sibling); + } else { + g_free(adapter); + fprintf(stderr, "Unexpected error %d when registering adapter %d\n", + ret, *id); + } +out: + return ret; +} + uint16_t css_build_subchannel_id(SubchDev *sch) { if (channel_subsys->max_cssid > 0) { @@ -1235,6 +1284,7 @@ static void css_init(void) channel_subsys->do_crw_mchk = true; channel_subsys->crws_lost = false; channel_subsys->chnmon_active = false; + QTAILQ_INIT(&channel_subsys->io_adapters); } machine_init(css_init); diff --git a/hw/s390x/css.h b/hw/s390x/css.h index 220169e7c3..6586106fa7 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -98,4 +98,8 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid, int hotplugged, int add); void css_generate_chp_crws(uint8_t cssid, uint8_t chpid); void css_adapter_interrupt(uint8_t isc); + +#define CSS_IO_ADAPTER_VIRTIO 1 +int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap, + bool maskable, uint32_t *id); #endif diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 1cb4e2c2f8..e3b7120cea 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -522,6 +522,10 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) dev->thinint_isc = thinint->isc; dev->ind_bit = thinint->ind_bit; cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); + ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, + dev->thinint_isc, true, false, + &dev->adapter_id); + assert(ret == 0); sch->thinint_active = ((dev->indicators != 0) && (dev->summary_indicator != 0)); sch->curr_status.scsw.count = ccw.count - len; diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 4393e44814..0b70b91cfe 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -85,6 +85,7 @@ struct VirtioCcwDevice { bool ioeventfd_disabled; uint32_t flags; uint8_t thinint_isc; + uint32_t adapter_id; /* Guest provided values: */ hwaddr indicators; hwaddr indicators2; diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 3bc60fd77f..83913ec097 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -32,6 +32,8 @@ typedef struct S390FLICState { typedef struct S390FLICStateClass { DeviceClass parent_class; + int (*register_io_adapter)(S390FLICState *fs, uint32_t id, uint8_t isc, + bool swap, bool maskable); } S390FLICStateClass; #define TYPE_KVM_S390_FLIC "s390-flic-kvm" -- cgit v1.2.3-55-g7522 From d426d9fba8ea5d728038a9bea6a7c51f11941157 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 15 Jul 2013 17:45:03 +0200 Subject: s390x/virtio-ccw: wire up irq routing and irqfds Make use of the new s390 adapter irq routing support to enable real in-kernel irqfds for virtio-ccw with adapter interrupts. Note that s390 doesn't provide the common KVM_CAP_IRQCHIP capability, but rather needs KVM_CAP_S390_IRQCHIP to be enabled. This is to ensure backward compatibility. Reviewed-by: Thomas Huth Reviewed-by: Christian Borntraeger Acked-by: Paolo Bonzini Signed-off-by: Cornelia Huck --- hw/intc/s390_flic.c | 21 ++++++ hw/intc/s390_flic_kvm.c | 66 +++++++++++++++++ hw/s390x/virtio-ccw.c | 165 ++++++++++++++++++++++++++++++++++++++----- hw/s390x/virtio-ccw.h | 4 +- include/hw/s390x/adapter.h | 23 ++++++ include/hw/s390x/s390_flic.h | 12 ++++ include/qemu/typedefs.h | 1 + include/sysemu/kvm.h | 2 + kvm-all.c | 38 +++++++++- kvm-stub.c | 5 ++ target-s390x/kvm.c | 10 +++ 11 files changed, 329 insertions(+), 18 deletions(-) create mode 100644 include/hw/s390x/adapter.h (limited to 'include') diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 2b568354cd..03c5e89f4e 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -52,11 +52,32 @@ static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id, return 0; } +static int qemu_s390_io_adapter_map(S390FLICState *fs, uint32_t id, + uint64_t map_addr, bool do_map) +{ + /* nothing to do */ + return 0; +} + +static int qemu_s390_add_adapter_routes(S390FLICState *fs, + AdapterRoutes *routes) +{ + return -ENOSYS; +} + +static void qemu_s390_release_adapter_routes(S390FLICState *fs, + AdapterRoutes *routes) +{ +} + static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) { S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); fsc->register_io_adapter = qemu_s390_register_io_adapter; + fsc->io_adapter_map = qemu_s390_io_adapter_map; + fsc->add_adapter_routes = qemu_s390_add_adapter_routes; + fsc->release_adapter_routes = qemu_s390_release_adapter_routes; } static const TypeInfo qemu_s390_flic_info = { diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index cc4072e5df..46c9e612d1 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -16,6 +16,7 @@ #include "sysemu/kvm.h" #include "migration/qemu-file.h" #include "hw/s390x/s390_flic.h" +#include "hw/s390x/adapter.h" #include "trace.h" #define FLIC_SAVE_INITIAL_SIZE getpagesize() @@ -178,6 +179,68 @@ static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id, return ret; } +static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id, + uint64_t map_addr, bool do_map) +{ + struct kvm_s390_io_adapter_req req = { + .id = id, + .type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP, + .addr = map_addr, + }; + struct kvm_device_attr attr = { + .group = KVM_DEV_FLIC_ADAPTER_MODIFY, + .addr = (uint64_t)&req, + }; + KVMS390FLICState *flic = KVM_S390_FLIC(fs); + int r; + + if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) { + return -ENOSYS; + } + + r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr); + return r ? -errno : 0; +} + +static int kvm_s390_add_adapter_routes(S390FLICState *fs, + AdapterRoutes *routes) +{ + int ret, i; + uint64_t ind_offset = routes->adapter.ind_offset; + + for (i = 0; i < routes->num_routes; i++) { + ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter); + if (ret < 0) { + goto out_undo; + } + routes->gsi[i] = ret; + routes->adapter.ind_offset++; + } + /* Restore passed-in structure to original state. */ + routes->adapter.ind_offset = ind_offset; + return 0; +out_undo: + while (--i >= 0) { + kvm_irqchip_release_virq(kvm_state, routes->gsi[i]); + routes->gsi[i] = -1; + } + routes->adapter.ind_offset = ind_offset; + return ret; +} + +static void kvm_s390_release_adapter_routes(S390FLICState *fs, + AdapterRoutes *routes) +{ + int i; + + for (i = 0; i < routes->num_routes; i++) { + if (routes->gsi[i] >= 0) { + kvm_irqchip_release_virq(kvm_state, routes->gsi[i]); + routes->gsi[i] = -1; + } + } +} + /** * kvm_flic_save - Save pending floating interrupts * @f: QEMUFile containing migration state @@ -337,6 +400,9 @@ static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) dc->unrealize = kvm_s390_flic_unrealize; dc->reset = kvm_s390_flic_reset; fsc->register_io_adapter = kvm_s390_register_io_adapter; + fsc->io_adapter_map = kvm_s390_io_adapter_map; + fsc->add_adapter_routes = kvm_s390_add_adapter_routes; + fsc->release_adapter_routes = kvm_s390_release_adapter_routes; } static const TypeInfo kvm_s390_flic_info = { diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index d11a78384f..c4f21d3816 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -21,6 +21,8 @@ #include "hw/sysbus.h" #include "qemu/bitops.h" #include "hw/virtio/virtio-bus.h" +#include "hw/s390x/adapter.h" +#include "hw/s390x/s390_flic.h" #include "ioinst.h" #include "css.h" @@ -48,7 +50,16 @@ static IndAddr *get_indicator(hwaddr ind_addr, int len) return indicator; } -static void release_indicator(IndAddr *indicator) +static int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr, + bool do_map) +{ + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + + return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map); +} + +static void release_indicator(AdapterInfo *adapter, IndAddr *indicator) { assert(indicator->refcnt > 0); indicator->refcnt--; @@ -56,9 +67,31 @@ static void release_indicator(IndAddr *indicator) return; } QTAILQ_REMOVE(&indicator_addresses, indicator, sibling); + if (indicator->map) { + s390_io_adapter_map(adapter, indicator->map, false); + } g_free(indicator); } +static int map_indicator(AdapterInfo *adapter, IndAddr *indicator) +{ + int ret; + + if (indicator->map) { + return 0; /* already mapped is not an error */ + } + indicator->map = indicator->addr; + ret = s390_io_adapter_map(adapter, indicator->map, true); + if ((ret != 0) && (ret != -ENOSYS)) { + goto out_err; + } + return 0; + +out_err: + indicator->map = 0; + return ret; +} + static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, VirtioCcwDevice *dev); @@ -554,11 +587,12 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) dev->indicators = get_indicator(thinint->device_indicator, thinint->ind_bit / 8 + 1); dev->thinint_isc = thinint->isc; - dev->ind_bit = thinint->ind_bit; + dev->routes.adapter.ind_offset = thinint->ind_bit; + dev->routes.adapter.summary_offset = 7; cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len); ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO, dev->thinint_isc, true, false, - &dev->adapter_id); + &dev->routes.adapter.adapter_id); assert(ret == 0); sch->thinint_active = ((dev->indicators != NULL) && (dev->summary_indicator != NULL)); @@ -732,7 +766,7 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev) g_free(sch); } if (dev->indicators) { - release_indicator(dev->indicators); + release_indicator(&dev->routes.adapter, dev->indicators); dev->indicators = NULL; } return 0; @@ -991,9 +1025,11 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) * ind_bit indicates the start of the indicators in a big * endian notation. */ + uint64_t ind_bit = dev->routes.adapter.ind_offset; + virtio_set_ind_atomic(sch, dev->indicators->addr + - (dev->ind_bit + vector) / 8, - 0x80 >> ((dev->ind_bit + vector) % 8)); + (ind_bit + vector) / 8, + 0x80 >> ((ind_bit + vector) % 8)); if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr, 0x01)) { css_adapter_interrupt(dev->thinint_isc); @@ -1033,15 +1069,15 @@ static void virtio_ccw_reset(DeviceState *d) virtio_reset(vdev); css_reset_sch(dev->sch); if (dev->indicators) { - release_indicator(dev->indicators); + release_indicator(&dev->routes.adapter, dev->indicators); dev->indicators = NULL; } if (dev->indicators2) { - release_indicator(dev->indicators2); + release_indicator(&dev->routes.adapter, dev->indicators2); dev->indicators2 = NULL; } if (dev->summary_indicator) { - release_indicator(dev->summary_indicator); + release_indicator(&dev->routes.adapter, dev->summary_indicator); dev->summary_indicator = NULL; } } @@ -1077,6 +1113,79 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign) return virtio_ccw_set_guest2host_notifier(dev, n, assign, false); } +static int virtio_ccw_get_mappings(VirtioCcwDevice *dev) +{ + int r; + + if (!dev->sch->thinint_active) { + return -EINVAL; + } + + r = map_indicator(&dev->routes.adapter, dev->summary_indicator); + if (r) { + return r; + } + r = map_indicator(&dev->routes.adapter, dev->indicators); + if (r) { + return r; + } + dev->routes.adapter.summary_addr = dev->summary_indicator->map; + dev->routes.adapter.ind_addr = dev->indicators->map; + + return 0; +} + +static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs) +{ + int i; + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + int ret; + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + + ret = virtio_ccw_get_mappings(dev); + if (ret) { + return ret; + } + for (i = 0; i < nvqs; i++) { + if (!virtio_queue_get_num(vdev, i)) { + break; + } + } + dev->routes.num_routes = i; + return fsc->add_adapter_routes(fs, &dev->routes); +} + +static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs) +{ + S390FLICState *fs = s390_get_flic(); + S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs); + + fsc->release_adapter_routes(fs, &dev->routes); +} + +static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n) +{ + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + VirtQueue *vq = virtio_get_queue(vdev, n); + EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); + + return kvm_irqchip_add_irqfd_notifier(kvm_state, notifier, NULL, + dev->routes.gsi[n]); +} + +static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n) +{ + VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + VirtQueue *vq = virtio_get_queue(vdev, n); + EventNotifier *notifier = virtio_queue_get_guest_notifier(vq); + int ret; + + ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, notifier, + dev->routes.gsi[n]); + assert(ret == 0); +} + static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, bool assign, bool with_irqfd) { @@ -1092,11 +1201,17 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, return r; } virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd); - /* We do not support irqfd for classic I/O interrupts, because the - * classic interrupts are intermixed with the subchannel status, that - * is queried with test subchannel. We want to use vhost, though. - * Lets make sure to have vhost running and wire up the irq fd to - * land in qemu (and only the irq fd) in this code. + if (with_irqfd) { + r = virtio_ccw_add_irqfd(dev, n); + if (r) { + virtio_queue_set_guest_notifier_fd_handler(vq, false, + with_irqfd); + return r; + } + } + /* + * We do not support individual masking for channel devices, so we + * need to manually trigger any guest masking callbacks here. */ if (k->guest_notifier_mask) { k->guest_notifier_mask(vdev, n, false); @@ -1110,6 +1225,9 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n, if (k->guest_notifier_mask) { k->guest_notifier_mask(vdev, n, true); } + if (with_irqfd) { + virtio_ccw_remove_irqfd(dev, n); + } virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd); event_notifier_cleanup(notifier); } @@ -1121,24 +1239,39 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs, { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); VirtIODevice *vdev = virtio_bus_get_device(&dev->bus); + bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled(); int r, n; + if (with_irqfd && assigned) { + /* irq routes need to be set up before assigning irqfds */ + r = virtio_ccw_setup_irqroutes(dev, nvqs); + if (r < 0) { + goto irqroute_error; + } + } for (n = 0; n < nvqs; n++) { if (!virtio_queue_get_num(vdev, n)) { break; } - /* false -> true, as soon as irqfd works */ - r = virtio_ccw_set_guest_notifier(dev, n, assigned, false); + r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd); if (r < 0) { goto assign_error; } } + if (with_irqfd && !assigned) { + /* release irq routes after irqfds have been released */ + virtio_ccw_release_irqroutes(dev, nvqs); + } return 0; assign_error: while (--n >= 0) { virtio_ccw_set_guest_notifier(dev, n, !assigned, false); } +irqroute_error: + if (with_irqfd && assigned) { + virtio_ccw_release_irqroutes(dev, nvqs); + } return r; } diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index d340bf4aca..b8b8a8abaa 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -22,6 +22,7 @@ #include #include #include +#include #define VIRTUAL_CSSID 0xfe @@ -77,6 +78,7 @@ typedef struct VirtIOCCWDeviceClass { typedef struct IndAddr { hwaddr addr; + uint64_t map; unsigned long refcnt; int len; QTAILQ_ENTRY(IndAddr) sibling; @@ -92,7 +94,7 @@ struct VirtioCcwDevice { bool ioeventfd_disabled; uint32_t flags; uint8_t thinint_isc; - uint32_t adapter_id; + AdapterRoutes routes; /* Guest provided values: */ IndAddr *indicators; IndAddr *indicators2; diff --git a/include/hw/s390x/adapter.h b/include/hw/s390x/adapter.h new file mode 100644 index 0000000000..7f1703508c --- /dev/null +++ b/include/hw/s390x/adapter.h @@ -0,0 +1,23 @@ +/* + * s390 adapter definitions + * + * Copyright 2013,2014 IBM Corp. + * Author(s): Cornelia Huck + * + * 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 + * directory. + */ + +#ifndef S390X_ADAPTER_H +#define S390X_ADAPTER_H + +struct AdapterInfo { + uint64_t ind_addr; + uint64_t summary_addr; + uint64_t ind_offset; + uint32_t summary_offset; + uint32_t adapter_id; +}; + +#endif diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 83913ec097..489d73b9b3 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -14,6 +14,14 @@ #define __HW_S390_FLIC_H #include "hw/sysbus.h" +#include "hw/s390x/adapter.h" +#include "hw/virtio/virtio.h" + +typedef struct AdapterRoutes { + AdapterInfo adapter; + int num_routes; + int gsi[VIRTIO_PCI_QUEUE_MAX]; +} AdapterRoutes; #define TYPE_S390_FLIC_COMMON "s390-flic" #define S390_FLIC_COMMON(obj) \ @@ -34,6 +42,10 @@ typedef struct S390FLICStateClass { int (*register_io_adapter)(S390FLICState *fs, uint32_t id, uint8_t isc, bool swap, bool maskable); + int (*io_adapter_map)(S390FLICState *fs, uint32_t id, uint64_t map_addr, + bool do_map); + int (*add_adapter_routes)(S390FLICState *fs, AdapterRoutes *routes); + void (*release_adapter_routes)(S390FLICState *fs, AdapterRoutes *routes); } S390FLICStateClass; #define TYPE_KVM_S390_FLIC "s390-flic-kvm" diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 86bab123a4..5f20b0e263 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -74,5 +74,6 @@ typedef struct SHPCDevice SHPCDevice; typedef struct FWCfgState FWCfgState; typedef struct PcGuestInfo PcGuestInfo; typedef struct Range Range; +typedef struct AdapterInfo AdapterInfo; #endif /* QEMU_TYPEDEFS_H */ diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 25c8a2bdfd..e79e92c50e 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -363,6 +363,8 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg); int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg); void kvm_irqchip_release_virq(KVMState *s, int virq); +int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter); + int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, EventNotifier *rn, int virq); int kvm_irqchip_remove_irqfd_notifier(KVMState *s, EventNotifier *n, int virq); diff --git a/kvm-all.c b/kvm-all.c index a343ede4d4..721a3904a9 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -27,6 +27,7 @@ #include "sysemu/sysemu.h" #include "hw/hw.h" #include "hw/pci/msi.h" +#include "hw/s390x/adapter.h" #include "exec/gdbstub.h" #include "sysemu/kvm.h" #include "qemu/bswap.h" @@ -1236,6 +1237,35 @@ static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int rfd, int virq, return kvm_vm_ioctl(s, KVM_IRQFD, &irqfd); } +int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) +{ + struct kvm_irq_routing_entry kroute; + int virq; + + if (!kvm_gsi_routing_enabled()) { + return -ENOSYS; + } + + virq = kvm_irqchip_get_virq(s); + if (virq < 0) { + return virq; + } + + kroute.gsi = virq; + kroute.type = KVM_IRQ_ROUTING_S390_ADAPTER; + kroute.flags = 0; + kroute.u.adapter.summary_addr = adapter->summary_addr; + kroute.u.adapter.ind_addr = adapter->ind_addr; + kroute.u.adapter.summary_offset = adapter->summary_offset; + kroute.u.adapter.ind_offset = adapter->ind_offset; + kroute.u.adapter.adapter_id = adapter->adapter_id; + + kvm_add_routing_entry(s, &kroute); + kvm_irqchip_commit_routes(s); + + return virq; +} + #else /* !KVM_CAP_IRQ_ROUTING */ void kvm_init_irq_routing(KVMState *s) @@ -1256,6 +1286,11 @@ int kvm_irqchip_add_msi_route(KVMState *s, MSIMessage msg) return -ENOSYS; } +int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) +{ + return -ENOSYS; +} + static int kvm_irqchip_assign_irqfd(KVMState *s, int fd, int virq, bool assign) { abort(); @@ -1285,7 +1320,8 @@ static int kvm_irqchip_create(KVMState *s) int ret; if (!qemu_opt_get_bool(qemu_get_machine_opts(), "kernel_irqchip", true) || - !kvm_check_extension(s, KVM_CAP_IRQCHIP)) { + (!kvm_check_extension(s, KVM_CAP_IRQCHIP) && + (kvm_vm_enable_cap(s, KVM_CAP_S390_IRQCHIP, 0) < 0))) { return 0; } diff --git a/kvm-stub.c b/kvm-stub.c index 8acda86ced..ac33d8666d 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -136,6 +136,11 @@ int kvm_irqchip_update_msi_route(KVMState *s, int virq, MSIMessage msg) return -ENOSYS; } +int kvm_irqchip_add_adapter_route(KVMState *s, AdapterInfo *adapter) +{ + return -ENOSYS; +} + int kvm_irqchip_add_irqfd_notifier(KVMState *s, EventNotifier *n, EventNotifier *rn, int virq) { diff --git a/target-s390x/kvm.c b/target-s390x/kvm.c index 56179afece..bb731a0dcd 100644 --- a/target-s390x/kvm.c +++ b/target-s390x/kvm.c @@ -911,6 +911,16 @@ void kvm_s390_enable_css_support(S390CPU *cpu) void kvm_arch_init_irq_routing(KVMState *s) { + /* + * Note that while irqchip capabilities generally imply that cpustates + * are handled in-kernel, it is not true for s390 (yet); therefore, we + * have to override the common code kvm_halt_in_kernel_allowed setting. + */ + if (kvm_check_extension(s, KVM_CAP_IRQ_ROUTING)) { + kvm_irqfds_allowed = true; + kvm_gsi_routing_allowed = true; + kvm_halt_in_kernel_allowed = false; + } } int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, -- cgit v1.2.3-55-g7522