Blame SOURCES/kvm-memory-add-section-range-info-for-IOMMU-notifier.patch

76daa3
From 03b074fce3bc0aa09fb2f9c4a464c898c6277559 Mon Sep 17 00:00:00 2001
76daa3
From: Peter Xu <peterx@redhat.com>
76daa3
Date: Mon, 24 Apr 2017 02:52:45 +0200
76daa3
Subject: [PATCH 06/23] memory: add section range info for IOMMU notifier
76daa3
76daa3
RH-Author: Peter Xu <peterx@redhat.com>
76daa3
Message-id: <1493002373-13010-2-git-send-email-peterx@redhat.com>
76daa3
Patchwork-id: 74849
76daa3
O-Subject: [RHEL7.4 qemu-kvm-rhev PATCH v2 1/9] memory: add section range info for IOMMU notifier
76daa3
Bugzilla: 1335808
76daa3
RH-Acked-by: Marcel Apfelbaum <marcel@redhat.com>
76daa3
RH-Acked-by: Michael S. Tsirkin <mst@redhat.com>
76daa3
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
76daa3
76daa3
In this patch, IOMMUNotifier.{start|end} are introduced to store section
76daa3
information for a specific notifier. When notification occurs, we not
76daa3
only check the notification type (MAP|UNMAP), but also check whether the
76daa3
notified iova range overlaps with the range of specific IOMMU notifier,
76daa3
and skip those notifiers if not in the listened range.
76daa3
76daa3
When removing an region, we need to make sure we removed the correct
76daa3
VFIOGuestIOMMU by checking the IOMMUNotifier.start address as well.
76daa3
76daa3
This patch is solving the problem that vfio-pci devices receive
76daa3
duplicated UNMAP notification on x86 platform when vIOMMU is there. The
76daa3
issue is that x86 IOMMU has a (0, 2^64-1) IOMMU region, which is
76daa3
splitted by the (0xfee00000, 0xfeefffff) IRQ region. AFAIK
76daa3
this (splitted IOMMU region) is only happening on x86.
76daa3
76daa3
This patch also helps vhost to leverage the new interface as well, so
76daa3
that vhost won't get duplicated cache flushes. In that sense, it's an
76daa3
slight performance improvement.
76daa3
76daa3
Suggested-by: David Gibson <david@gibson.dropbear.id.au>
76daa3
Reviewed-by: Eric Auger <eric.auger@redhat.com>
76daa3
Reviewed-by: Michael S. Tsirkin <mst@redhat.com>
76daa3
Acked-by: Alex Williamson <alex.williamson@redhat.com>
76daa3
Signed-off-by: Peter Xu <peterx@redhat.com>
76daa3
Message-Id: <1491562755-23867-2-git-send-email-peterx@redhat.com>
76daa3
[ehabkost: included extra vhost_iommu_region_del() change from Peter Xu]
76daa3
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
76daa3
76daa3
(cherry picked from commit 698feb5e13a2d763369909ce33f2bd7a7c1c11c0)
76daa3
Signed-off-by: Peter Xu <peterx@redhat.com>
76daa3
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
76daa3
---
76daa3
 hw/vfio/common.c      | 12 +++++++++---
76daa3
 hw/virtio/vhost.c     | 13 ++++++++++---
76daa3
 include/exec/memory.h | 19 ++++++++++++++++++-
76daa3
 memory.c              |  9 +++++++++
76daa3
 4 files changed, 46 insertions(+), 7 deletions(-)
76daa3
76daa3
diff --git a/hw/vfio/common.c b/hw/vfio/common.c
76daa3
index f3ba9b9..6b33b9f 100644
76daa3
--- a/hw/vfio/common.c
76daa3
+++ b/hw/vfio/common.c
76daa3
@@ -478,8 +478,13 @@ static void vfio_listener_region_add(MemoryListener *listener,
76daa3
         giommu->iommu_offset = section->offset_within_address_space -
76daa3
                                section->offset_within_region;
76daa3
         giommu->container = container;
76daa3
-        giommu->n.notify = vfio_iommu_map_notify;
76daa3
-        giommu->n.notifier_flags = IOMMU_NOTIFIER_ALL;
76daa3
+        llend = int128_add(int128_make64(section->offset_within_region),
76daa3
+                           section->size);
76daa3
+        llend = int128_sub(llend, int128_one());
76daa3
+        iommu_notifier_init(&giommu->n, vfio_iommu_map_notify,
76daa3
+                            IOMMU_NOTIFIER_ALL,
76daa3
+                            section->offset_within_region,
76daa3
+                            int128_get64(llend));
76daa3
         QLIST_INSERT_HEAD(&container->giommu_list, giommu, giommu_next);
76daa3
 
76daa3
         memory_region_register_iommu_notifier(giommu->iommu, &giommu->n);
76daa3
@@ -550,7 +555,8 @@ static void vfio_listener_region_del(MemoryListener *listener,
76daa3
         VFIOGuestIOMMU *giommu;
76daa3
 
76daa3
         QLIST_FOREACH(giommu, &container->giommu_list, giommu_next) {
76daa3
-            if (giommu->iommu == section->mr) {
76daa3
+            if (giommu->iommu == section->mr &&
76daa3
+                giommu->n.start == section->offset_within_region) {
76daa3
                 memory_region_unregister_iommu_notifier(giommu->iommu,
76daa3
                                                         &giommu->n);
76daa3
                 QLIST_REMOVE(giommu, giommu_next);
76daa3
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
76daa3
index 613494d..0001e60 100644
76daa3
--- a/hw/virtio/vhost.c
76daa3
+++ b/hw/virtio/vhost.c
76daa3
@@ -736,14 +736,20 @@ static void vhost_iommu_region_add(MemoryListener *listener,
76daa3
     struct vhost_dev *dev = container_of(listener, struct vhost_dev,
76daa3
                                          iommu_listener);
76daa3
     struct vhost_iommu *iommu;
76daa3
+    Int128 end;
76daa3
 
76daa3
     if (!memory_region_is_iommu(section->mr)) {
76daa3
         return;
76daa3
     }
76daa3
 
76daa3
     iommu = g_malloc0(sizeof(*iommu));
76daa3
-    iommu->n.notify = vhost_iommu_unmap_notify;
76daa3
-    iommu->n.notifier_flags = IOMMU_NOTIFIER_UNMAP;
76daa3
+    end = int128_add(int128_make64(section->offset_within_region),
76daa3
+                     section->size);
76daa3
+    end = int128_sub(end, int128_one());
76daa3
+    iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify,
76daa3
+                        IOMMU_NOTIFIER_UNMAP,
76daa3
+                        section->offset_within_region,
76daa3
+                        int128_get64(end));
76daa3
     iommu->mr = section->mr;
76daa3
     iommu->iommu_offset = section->offset_within_address_space -
76daa3
                           section->offset_within_region;
76daa3
@@ -765,7 +771,8 @@ static void vhost_iommu_region_del(MemoryListener *listener,
76daa3
     }
76daa3
 
76daa3
     QLIST_FOREACH(iommu, &dev->iommu_list, iommu_next) {
76daa3
-        if (iommu->mr == section->mr) {
76daa3
+        if (iommu->mr == section->mr &&
76daa3
+            iommu->n.start == section->offset_within_region) {
76daa3
             memory_region_unregister_iommu_notifier(iommu->mr,
76daa3
                                                     &iommu->n);
76daa3
             QLIST_REMOVE(iommu, iommu_next);
76daa3
diff --git a/include/exec/memory.h b/include/exec/memory.h
76daa3
index f20b191..0840c89 100644
76daa3
--- a/include/exec/memory.h
76daa3
+++ b/include/exec/memory.h
76daa3
@@ -77,13 +77,30 @@ typedef enum {
76daa3
 
76daa3
 #define IOMMU_NOTIFIER_ALL (IOMMU_NOTIFIER_MAP | IOMMU_NOTIFIER_UNMAP)
76daa3
 
76daa3
+struct IOMMUNotifier;
76daa3
+typedef void (*IOMMUNotify)(struct IOMMUNotifier *notifier,
76daa3
+                            IOMMUTLBEntry *data);
76daa3
+
76daa3
 struct IOMMUNotifier {
76daa3
-    void (*notify)(struct IOMMUNotifier *notifier, IOMMUTLBEntry *data);
76daa3
+    IOMMUNotify notify;
76daa3
     IOMMUNotifierFlag notifier_flags;
76daa3
+    /* Notify for address space range start <= addr <= end */
76daa3
+    hwaddr start;
76daa3
+    hwaddr end;
76daa3
     QLIST_ENTRY(IOMMUNotifier) node;
76daa3
 };
76daa3
 typedef struct IOMMUNotifier IOMMUNotifier;
76daa3
 
76daa3
+static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn,
76daa3
+                                       IOMMUNotifierFlag flags,
76daa3
+                                       hwaddr start, hwaddr end)
76daa3
+{
76daa3
+    n->notify = fn;
76daa3
+    n->notifier_flags = flags;
76daa3
+    n->start = start;
76daa3
+    n->end = end;
76daa3
+}
76daa3
+
76daa3
 /* New-style MMIO accessors can indicate that the transaction failed.
76daa3
  * A zero (MEMTX_OK) response means success; anything else is a failure
76daa3
  * of some kind. The memory subsystem will bitwise-OR together results
76daa3
diff --git a/memory.c b/memory.c
76daa3
index 4c95aaf..75ac595 100644
76daa3
--- a/memory.c
76daa3
+++ b/memory.c
76daa3
@@ -1606,6 +1606,7 @@ void memory_region_register_iommu_notifier(MemoryRegion *mr,
76daa3
 
76daa3
     /* We need to register for at least one bitfield */
76daa3
     assert(n->notifier_flags != IOMMU_NOTIFIER_NONE);
76daa3
+    assert(n->start <= n->end);
76daa3
     QLIST_INSERT_HEAD(&mr->iommu_notify, n, node);
76daa3
     memory_region_update_iommu_notify_flags(mr);
76daa3
 }
76daa3
@@ -1667,6 +1668,14 @@ void memory_region_notify_iommu(MemoryRegion *mr,
76daa3
     }
76daa3
 
76daa3
     QLIST_FOREACH(iommu_notifier, &mr->iommu_notify, node) {
76daa3
+        /*
76daa3
+         * Skip the notification if the notification does not overlap
76daa3
+         * with registered range.
76daa3
+         */
76daa3
+        if (iommu_notifier->start > entry.iova + entry.addr_mask + 1 ||
76daa3
+            iommu_notifier->end < entry.iova) {
76daa3
+            continue;
76daa3
+        }
76daa3
         if (iommu_notifier->notifier_flags & request_flags) {
76daa3
             iommu_notifier->notify(iommu_notifier, &entry);
76daa3
         }
76daa3
-- 
76daa3
1.8.3.1
76daa3