9ae3a8
From 3e7a0ab48e4cd233caf8ab9e2593d1d2978bad9d Mon Sep 17 00:00:00 2001
9ae3a8
From: Alex Williamson <alex.williamson@redhat.com>
9ae3a8
Date: Fri, 10 Apr 2015 16:34:19 +0200
9ae3a8
Subject: [PATCH 08/14] vfio-pci: Enable device request notification support
9ae3a8
9ae3a8
Message-id: <20150410163419.15324.17072.stgit@gimli.home>
9ae3a8
Patchwork-id: 64788
9ae3a8
O-Subject: [RHEL7.2 qemu-kvm PATCH 8/8] vfio-pci: Enable device request notification support
9ae3a8
Bugzilla: 1210509
9ae3a8
RH-Acked-by: Thomas Huth <thuth@redhat.com>
9ae3a8
RH-Acked-by: Laszlo Ersek <lersek@redhat.com>
9ae3a8
RH-Acked-by: Bandan Das <bsd@redhat.com>
9ae3a8
9ae3a8
Upstream: 47cbe50cc8d8e59129311bcdb827e1116e935bde
9ae3a8
9ae3a8
Linux v4.0-rc1 vfio-pci introduced a new virtual interrupt to allow
9ae3a8
the kernel to request a device from the user.  When signaled, QEMU
9ae3a8
will by default attmempt to hot-unplug the device.  This is a one-
9ae3a8
shot attempt with the expectation that the kernel will continue to
9ae3a8
poll for the device if it is not returned.  Returning the device when
9ae3a8
requested is the expected standard model of cooperative usage, but we
9ae3a8
also add an option option to disable this feature.  Initially this
9ae3a8
opt-out is set as an experimental option because we really should
9ae3a8
honor kernel requests for the device.
9ae3a8
9ae3a8
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
9ae3a8
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
---
9ae3a8
 hw/misc/vfio.c             | 100 +++++++++++++++++++++++++++++++++++++++++++++
9ae3a8
 linux-headers/linux/vfio.h |   1 +
9ae3a8
 2 files changed, 101 insertions(+)
9ae3a8
9ae3a8
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
9ae3a8
index d06b485..cbc9d8a 100644
9ae3a8
--- a/hw/misc/vfio.c
9ae3a8
+++ b/hw/misc/vfio.c
9ae3a8
@@ -195,14 +195,18 @@ typedef struct VFIODevice {
9ae3a8
     QLIST_ENTRY(VFIODevice) next;
9ae3a8
     struct VFIOGroup *group;
9ae3a8
     EventNotifier err_notifier;
9ae3a8
+    EventNotifier req_notifier;
9ae3a8
     uint32_t features;
9ae3a8
 #define VFIO_FEATURE_ENABLE_VGA_BIT 0
9ae3a8
 #define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT)
9ae3a8
+#define VFIO_FEATURE_ENABLE_REQ_BIT 1
9ae3a8
+#define VFIO_FEATURE_ENABLE_REQ (1 << VFIO_FEATURE_ENABLE_REQ_BIT)
9ae3a8
     int32_t bootindex;
9ae3a8
     uint8_t pm_cap;
9ae3a8
     bool reset_works;
9ae3a8
     bool has_vga;
9ae3a8
     bool pci_aer;
9ae3a8
+    bool req_enabled;
9ae3a8
     bool has_flr;
9ae3a8
     bool has_pm_reset;
9ae3a8
     bool needs_reset;
9ae3a8
@@ -3595,6 +3599,7 @@ static int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vdev)
9ae3a8
 
9ae3a8
         vdev->has_vga = true;
9ae3a8
     }
9ae3a8
+
9ae3a8
     irq_info.index = VFIO_PCI_ERR_IRQ_INDEX;
9ae3a8
 
9ae3a8
     ret = ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info);
9ae3a8
@@ -3737,6 +3742,97 @@ static void vfio_unregister_err_notifier(VFIODevice *vdev)
9ae3a8
     event_notifier_cleanup(&vdev->err_notifier);
9ae3a8
 }
9ae3a8
 
9ae3a8
+static void vfio_req_notifier_handler(void *opaque)
9ae3a8
+{
9ae3a8
+    VFIODevice *vdev = opaque;
9ae3a8
+
9ae3a8
+    if (!event_notifier_test_and_clear(&vdev->req_notifier)) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    qdev_unplug(&vdev->pdev.qdev, NULL);
9ae3a8
+}
9ae3a8
+
9ae3a8
+static void vfio_register_req_notifier(VFIODevice *vdev)
9ae3a8
+{
9ae3a8
+    struct vfio_irq_info irq_info = { .argsz = sizeof(irq_info),
9ae3a8
+                                      .index = VFIO_PCI_REQ_IRQ_INDEX };
9ae3a8
+    int argsz;
9ae3a8
+    struct vfio_irq_set *irq_set;
9ae3a8
+    int32_t *pfd;
9ae3a8
+
9ae3a8
+    if (!(vdev->features & VFIO_FEATURE_ENABLE_REQ)) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (ioctl(vdev->fd,
9ae3a8
+              VFIO_DEVICE_GET_IRQ_INFO, &irq_info) < 0 || irq_info.count < 1) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    if (event_notifier_init(&vdev->req_notifier, 0)) {
9ae3a8
+        error_report("vfio: Unable to init event notifier for device request");
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    argsz = sizeof(*irq_set) + sizeof(*pfd);
9ae3a8
+
9ae3a8
+    irq_set = g_malloc0(argsz);
9ae3a8
+    irq_set->argsz = argsz;
9ae3a8
+    irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
9ae3a8
+                     VFIO_IRQ_SET_ACTION_TRIGGER;
9ae3a8
+    irq_set->index = VFIO_PCI_REQ_IRQ_INDEX;
9ae3a8
+    irq_set->start = 0;
9ae3a8
+    irq_set->count = 1;
9ae3a8
+    pfd = (int32_t *)&irq_set->data;
9ae3a8
+
9ae3a8
+    *pfd = event_notifier_get_fd(&vdev->req_notifier);
9ae3a8
+    qemu_set_fd_handler(*pfd, vfio_req_notifier_handler, NULL, vdev);
9ae3a8
+
9ae3a8
+    if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
9ae3a8
+        error_report("vfio: Failed to set up device request notification");
9ae3a8
+        qemu_set_fd_handler(*pfd, NULL, NULL, vdev);
9ae3a8
+        event_notifier_cleanup(&vdev->req_notifier);
9ae3a8
+    } else {
9ae3a8
+        vdev->req_enabled = true;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    g_free(irq_set);
9ae3a8
+}
9ae3a8
+
9ae3a8
+static void vfio_unregister_req_notifier(VFIODevice *vdev)
9ae3a8
+{
9ae3a8
+    int argsz;
9ae3a8
+    struct vfio_irq_set *irq_set;
9ae3a8
+    int32_t *pfd;
9ae3a8
+
9ae3a8
+    if (!vdev->req_enabled) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    argsz = sizeof(*irq_set) + sizeof(*pfd);
9ae3a8
+
9ae3a8
+    irq_set = g_malloc0(argsz);
9ae3a8
+    irq_set->argsz = argsz;
9ae3a8
+    irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
9ae3a8
+                     VFIO_IRQ_SET_ACTION_TRIGGER;
9ae3a8
+    irq_set->index = VFIO_PCI_REQ_IRQ_INDEX;
9ae3a8
+    irq_set->start = 0;
9ae3a8
+    irq_set->count = 1;
9ae3a8
+    pfd = (int32_t *)&irq_set->data;
9ae3a8
+    *pfd = -1;
9ae3a8
+
9ae3a8
+    if (ioctl(vdev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) {
9ae3a8
+        error_report("vfio: Failed to de-assign device request fd: %m");
9ae3a8
+    }
9ae3a8
+    g_free(irq_set);
9ae3a8
+    qemu_set_fd_handler(event_notifier_get_fd(&vdev->req_notifier),
9ae3a8
+                        NULL, NULL, vdev);
9ae3a8
+    event_notifier_cleanup(&vdev->req_notifier);
9ae3a8
+
9ae3a8
+    vdev->req_enabled = false;
9ae3a8
+}
9ae3a8
+
9ae3a8
 static int vfio_initfn(PCIDevice *pdev)
9ae3a8
 {
9ae3a8
     VFIODevice *pvdev, *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
9ae3a8
@@ -3889,6 +3985,7 @@ static int vfio_initfn(PCIDevice *pdev)
9ae3a8
 
9ae3a8
     add_boot_device_path(vdev->bootindex, &pdev->qdev, NULL);
9ae3a8
     vfio_register_err_notifier(vdev);
9ae3a8
+    vfio_register_req_notifier(vdev);
9ae3a8
 
9ae3a8
     return 0;
9ae3a8
 
9ae3a8
@@ -3908,6 +4005,7 @@ static void vfio_exitfn(PCIDevice *pdev)
9ae3a8
     VFIODevice *vdev = DO_UPCAST(VFIODevice, pdev, pdev);
9ae3a8
     VFIOGroup *group = vdev->group;
9ae3a8
 
9ae3a8
+    vfio_unregister_req_notifier(vdev);
9ae3a8
     vfio_unregister_err_notifier(vdev);
9ae3a8
     pci_device_set_intx_routing_notifier(&vdev->pdev, NULL);
9ae3a8
     vfio_disable_interrupts(vdev);
9ae3a8
@@ -3962,6 +4060,8 @@ static Property vfio_pci_dev_properties[] = {
9ae3a8
                        intx.mmap_timeout, 1100),
9ae3a8
     DEFINE_PROP_BIT("x-vga", VFIODevice, features,
9ae3a8
                     VFIO_FEATURE_ENABLE_VGA_BIT, false),
9ae3a8
+    DEFINE_PROP_BIT("x-req", VFIODevice, features,
9ae3a8
+                    VFIO_FEATURE_ENABLE_REQ_BIT, true),
9ae3a8
     DEFINE_PROP_INT32("bootindex", VFIODevice, bootindex, -1),
9ae3a8
     /*
9ae3a8
      * TODO - support passed fds... is this necessary?
9ae3a8
diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h
9ae3a8
index 7919c76..d197fd4 100644
9ae3a8
--- a/linux-headers/linux/vfio.h
9ae3a8
+++ b/linux-headers/linux/vfio.h
9ae3a8
@@ -321,6 +321,7 @@ enum {
9ae3a8
 	VFIO_PCI_MSI_IRQ_INDEX,
9ae3a8
 	VFIO_PCI_MSIX_IRQ_INDEX,
9ae3a8
 	VFIO_PCI_ERR_IRQ_INDEX,
9ae3a8
+	VFIO_PCI_REQ_IRQ_INDEX,
9ae3a8
 	VFIO_PCI_NUM_IRQS
9ae3a8
 };
9ae3a8
 
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8