[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [RFC v2 26/28] hw/vfio/common: Handle fault_handler
From: |
Eric Auger |
Subject: |
[Qemu-devel] [RFC v2 26/28] hw/vfio/common: Handle fault_handler |
Date: |
Fri, 21 Sep 2018 10:18:17 +0200 |
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 <address@hidden>
---
hw/vfio/common.c | 118 ++++++++++++++++++++++++++++++++++
include/hw/vfio/vfio-common.h | 1 +
2 files changed, 119 insertions(+)
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 {
--
2.17.1
- [Qemu-devel] [RFC v2 15/28] hw/arm/smmuv3: Notify on config changes, (continued)
- [Qemu-devel] [RFC v2 15/28] hw/arm/smmuv3: Notify on config changes, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 17/28] hw/vfio/common: Introduce vfio_alloc_guest_iommu helper, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 18/28] hw/vfio/common: Introduce vfio_dma_(un)map_ram_section helpers, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 19/28] hw/vfio/common: Register specific nested mode notifiers and memory_listener, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 20/28] hw/vfio/common: Register MAP notifier for MSI binding, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 22/28] vfio/pci: Always set up MSI route before enabling vectors, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 21/28] target/arm/kvm: Notifies IOMMU on MSI stage 1 binding, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 23/28] hw/arm/smmuv3: Remove warning about unsupported MAP notifiers, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 24/28] memory: Introduce IOMMU_NOTIFIER_INIT_CFG IOMMU Config Notifier, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 25/28] memory: Introduce IOMMU Memory Region inject_faults API, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 26/28] hw/vfio/common: Handle fault_handler,
Eric Auger <=
- [Qemu-devel] [RFC v2 27/28] hw/arm/smmuv3: Init fault handling, Eric Auger, 2018/09/21
- [Qemu-devel] [RFC v2 28/28] hw/arm/smmuv3: Implement fault injection, Eric Auger, 2018/09/21