[RFC,v2,26/28] hw/vfio/common: Handle fault_handler
diff mbox series

Message ID 20180921081819.9203-27-eric.auger@redhat.com
State New
Headers show
Series
  • vSMMUv3/pSMMUv3 2 stage VFIO integration
Related show

Commit Message

Auger Eric Sept. 21, 2018, 8:18 a.m. UTC
We introduce a new IOMMU notifier, whose role is to register
the fault notifier.

The patch also implements the userspace fault handler. When the
eventfd is signalled, the fault handler injects the set of
collected faults into the IOMMU memory region.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
---
 hw/vfio/common.c              | 118 ++++++++++++++++++++++++++++++++++
 include/hw/vfio/vfio-common.h |   1 +
 2 files changed, 119 insertions(+)

Patch
diff mbox series

diff --git a/hw/vfio/common.c b/hw/vfio/common.c
index 172ddd396f..9548e962e5 100644
--- a/hw/vfio/common.c
+++ b/hw/vfio/common.c
@@ -37,12 +37,16 @@ 
 #include "sysemu/kvm.h"
 #include "trace.h"
 #include "qapi/error.h"
+#include "qemu/event_notifier.h"
+#include "qemu/main-loop.h"
 
 struct vfio_group_head vfio_group_list =
     QLIST_HEAD_INITIALIZER(vfio_group_list);
 struct vfio_as_head vfio_address_spaces =
     QLIST_HEAD_INITIALIZER(vfio_address_spaces);
 
+#define MAX_QUERIED_FAULT_EVENTS 10
+
 #ifdef CONFIG_KVM
 /*
  * We have a single VFIO pseudo device per KVM VM.  Once created it lives
@@ -349,6 +353,111 @@  static bool vfio_get_vaddr(IOMMUTLBEntry *iotlb, void **vaddr,
     return true;
 }
 
+/*
+ * At the moment we retrieve MAX_QUERIED_FAULT_EVENTS events
+ * TODO: loop until the ioctl returns count = 0
+ */
+static void vfio_iommu_fault_handler(void *data)
+{
+    VFIOGuestIOMMU *giommu = (VFIOGuestIOMMU *)data;
+    IOMMUMemoryRegion *iommu = giommu->iommu;
+    struct vfio_iommu_type1_get_fault_events *ustruct;
+    size_t fault_buffer_size =
+        MAX_QUERIED_FAULT_EVENTS * sizeof(struct iommu_fault);
+    size_t argsz = sizeof(*ustruct) + fault_buffer_size;
+    int ret;
+
+    ustruct = g_malloc0(argsz);
+    ustruct->argsz = argsz;
+    ustruct->count = 0;
+
+    ret = ioctl(giommu->container->fd, VFIO_IOMMU_GET_FAULT_EVENTS, ustruct);
+    if (ret) {
+        error_report("%s: failed to get pending faults (%d)",
+                     __func__, ret);
+    }
+
+    assert(ustruct->count > 0);
+    memory_region_inject_faults(iommu, ustruct->count, ustruct->events);
+
+    g_free(ustruct);
+
+    ret = event_notifier_test_and_clear(giommu->fault_notifier);
+    if (!ret) {
+        error_report("Error when clearing fd=%d (ret = %d)",
+                     event_notifier_get_fd(giommu->fault_notifier), ret);
+    }
+}
+
+static void
+vfio_iommu_fault_handler_register(IOMMUNotifier *n, IOMMUConfig *cfg)
+
+{
+    VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n);
+    VFIOContainer *container = giommu->container;
+    struct vfio_iommu_type1_set_fault_eventfd info;
+    int ret;
+
+    info.argsz = sizeof(info);
+    info.flags = 0;
+
+    giommu->fault_notifier = g_malloc0(sizeof(EventNotifier));
+    ret = event_notifier_init(giommu->fault_notifier, 0);
+    if (ret) {
+        error_report("%s: failed to init fault eventfd (%d)",
+                     __func__, ret);
+        goto out;
+    }
+
+    info.eventfd = event_notifier_get_fd(giommu->fault_notifier);
+    qemu_set_fd_handler(info.eventfd, vfio_iommu_fault_handler, NULL, giommu);
+
+    memcpy(&info.config, &cfg->fault_cfg, sizeof(cfg->fault_cfg));
+
+    ret = ioctl(container->fd, VFIO_IOMMU_SET_FAULT_EVENTFD, &info);
+    if (ret) {
+        error_report("%s: failed to setup iommu fault propagation (%d)",
+                     __func__, ret);
+        goto cleanup;
+    }
+    return;
+cleanup:
+    qemu_set_fd_handler(info.eventfd, NULL, NULL, NULL);
+    event_notifier_cleanup(giommu->fault_notifier);
+out:
+    g_free(giommu->fault_notifier);
+}
+
+static void vfio_iommu_fault_handler_unregister(IOMMUNotifier *n)
+{
+    VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n);
+    VFIOContainer *container = giommu->container;
+    struct vfio_iommu_type1_set_fault_eventfd info;
+    int eventfd, ret;
+
+    if (!(n->notifier_flags & IOMMU_NOTIFIER_INIT_CFG) ||
+        !giommu->fault_notifier) {
+        return;
+    }
+
+    eventfd = event_notifier_get_fd(giommu->fault_notifier);
+    if (eventfd < 0) {
+        return;
+    }
+
+    info.argsz = sizeof(info);
+    info.flags = 0;
+    info.eventfd = -1;
+    ret = ioctl(container->fd, VFIO_IOMMU_SET_FAULT_EVENTFD, &info);
+    if (ret) {
+        error_report("%s: failed to stop iommu fault propagation (%d)",
+                     __func__, ret);
+    }
+    qemu_set_fd_handler(eventfd, NULL, NULL, NULL);
+    event_notifier_cleanup(giommu->fault_notifier);
+    g_free(giommu->fault_notifier);
+}
+
 /* Program the guest @cfg on physical IOMMU stage 1 (nested mode) */
 static void vfio_iommu_nested_notify(IOMMUNotifier *n,
                                      IOMMUConfig *cfg)
@@ -754,6 +863,14 @@  static void vfio_listener_region_add(MemoryListener *listener,
             QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next);
             memory_region_register_iommu_notifier(section->mr, &giommu->n);
 
+            /* Config notifier to register fault handler */
+            giommu = vfio_alloc_guest_iommu(container, iommu_mr, offset);
+            iommu_config_notifier_init(&giommu->n,
+                                       vfio_iommu_fault_handler_register,
+                                       IOMMU_NOTIFIER_INIT_CFG, iommu_idx);
+            QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next);
+            memory_region_register_iommu_notifier(section->mr, &giommu->n);
+
             /* IOTLB unmap notifier to propagate guest IOTLB invalidations */
             giommu = vfio_alloc_guest_iommu(container, iommu_mr, offset);
             iommu_iotlb_notifier_init(&giommu->n, vfio_iommu_unmap_notify,
@@ -846,6 +963,7 @@  static void vfio_listener_region_del(MemoryListener *listener,
                 } else if (is_iommu_config_notifier(&giommu->n)) {
                     memory_region_unregister_iommu_notifier(section->mr,
                                                             &giommu->n);
+                    vfio_iommu_fault_handler_unregister(&giommu->n);
                 }
                 QLIST_REMOVE(giommu, giommu_next);
                 g_free(giommu);
diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h
index 821def0565..db9cd93d77 100644
--- a/include/hw/vfio/vfio-common.h
+++ b/include/hw/vfio/vfio-common.h
@@ -91,6 +91,7 @@  typedef struct VFIOGuestIOMMU {
     hwaddr iommu_offset;
     IOMMUNotifier n;
     QLIST_ENTRY(VFIOGuestIOMMU) giommu_next;
+    EventNotifier *fault_notifier;
 } VFIOGuestIOMMU;
 
 typedef struct VFIOHostDMAWindow {