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 <address@hidden>
Signed-off-by: Cornelia Huck <address@hidden>
---
hw/s390x/virtio-ccw.c | 165 ++++++++++++++++++++++++++++++++++++++++----
hw/s390x/virtio-ccw.h | 2 +
include/hw/s390x/adapter.h | 23 ++++++
include/qemu/typedefs.h | 1 +
include/sysemu/kvm.h | 2 +
kvm-all.c | 38 +++++++++-
kvm-stub.c | 5 ++
target-s390x/kvm.c | 5 ++
8 files changed, 228 insertions(+), 13 deletions(-)
create mode 100644 include/hw/s390x/adapter.h
diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c
index 69efa6c..5612ccc 100644
--- a/hw/s390x/virtio-ccw.c
+++ b/hw/s390x/virtio-ccw.c
@@ -21,6 +21,7 @@
#include "hw/sysbus.h"
#include "qemu/bitops.h"
#include "hw/virtio/virtio-bus.h"
+#include "hw/s390x/adapter.h"
#include "ioinst.h"
#include "css.h"
@@ -48,7 +49,7 @@ static IndAddr *get_indicator(hwaddr ind_addr, int len)
return indicator;
}
-static void release_indicator(IndAddr *indicator)
+static void release_indicator(uint32_t adapter_id, IndAddr *indicator)
{
assert(indicator->refcnt > 0);
indicator->refcnt--;
@@ -56,9 +57,31 @@ static void release_indicator(IndAddr *indicator)
return;
}
QTAILQ_REMOVE(&indicator_addresses, indicator, sibling);
+ if (indicator->map) {
+ s390_io_adapter_map(adapter_id, indicator->map, false);
+ }
g_free(indicator);
}
+static int map_indicator(uint32_t adapter_id, 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_id, indicator->map, true);
+ if ((ret != 0) && (ret != -ENOSYS)) {
+ goto out_err;
+ }
+ return 0;
+
+out_err:
+ indicator->map = 0;
+ return -EFAULT;
+}
+
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
VirtioCcwDevice *dev);
@@ -733,7 +756,7 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev)
g_free(sch);
}
if (dev->indicators) {
- release_indicator(dev->indicators);
+ release_indicator(dev->adapter_id, dev->indicators);
dev->indicators = NULL;
}
return 0;
@@ -1034,15 +1057,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->adapter_id, dev->indicators);
dev->indicators = NULL;
}
if (dev->indicators2) {
- release_indicator(dev->indicators2);
+ release_indicator(dev->adapter_id, dev->indicators2);
dev->indicators2 = NULL;
}
if (dev->summary_indicator) {
- release_indicator(dev->summary_indicator);
+ release_indicator(dev->adapter_id, dev->summary_indicator);
dev->summary_indicator = NULL;
}
}
@@ -1078,6 +1101,100 @@ 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_adapter_info(VirtioCcwDevice *dev,
+ AdapterInfo *adapter)
+{
+ int r;
+
+ if (!dev->sch->thinint_active) {
+ return -EINVAL;
+ }
+
+ r = map_indicator(dev->adapter_id, dev->summary_indicator);
+ if (r) {
+ return r;
+ }
+ r = map_indicator(dev->adapter_id, dev->indicators);
+ if (r) {
+ return r;
+ }
+ adapter->summary_addr = dev->summary_indicator->map;
+ adapter->ind_addr = dev->indicators->map;
+ adapter->ind_offset = dev->ind_bit;
+ adapter->summary_offset = 7;
+ adapter->adapter_id = dev->adapter_id;
+
+ return 0;
+}
+
+static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs)
+{
+ int i;
+ VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
+ int ret;
+ AdapterInfo adapter;
+
+ ret = virtio_ccw_get_adapter_info(dev, &adapter);
+ if (ret) {
+ return ret;
+ }
+ for (i = 0; i < nvqs; i++) {
+ if (!virtio_queue_get_num(vdev, i)) {
+ break;
+ }
+ ret = kvm_irqchip_add_adapter_route(kvm_state, &adapter);