9ae3a8
From a33e922436f708fe4881da4b6f363c49db5af581 Mon Sep 17 00:00:00 2001
9ae3a8
From: Alex Williamson <alex.williamson@redhat.com>
9ae3a8
Date: Fri, 29 Sep 2017 21:46:02 +0200
9ae3a8
Subject: [PATCH 14/27] vfio: Generalize region support
9ae3a8
9ae3a8
RH-Author: Alex Williamson <alex.williamson@redhat.com>
9ae3a8
Message-id: <20170929214601.16765.68107.stgit@gimli.home>
9ae3a8
Patchwork-id: 76772
9ae3a8
O-Subject: [RHEL-7.5 qemu-kvm PATCH 14/16] vfio: Generalize region support
9ae3a8
Bugzilla: 1494181
9ae3a8
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
9ae3a8
RH-Acked-by: Auger Eric <eric.auger@redhat.com>
9ae3a8
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
9ae3a8
9ae3a8
Upstream: db0da029a1853d46c90a6c0790ce6ca77fd46ea3
9ae3a8
RHEL: MemoryRegions still destroyed from exitfn, so finalize is called
9ae3a8
      immediately after exit with memory_region_destroy().
9ae3a8
9ae3a8
Both platform and PCI vfio drivers create a "slow", I/O memory region
9ae3a8
with one or more mmap memory regions overlayed when supported by the
9ae3a8
device. Generalize this to a set of common helpers in the core that
9ae3a8
pulls the region info from vfio, fills the region data, configures
9ae3a8
slow mapping, and adds helpers for comleting the mmap, enable/disable,
9ae3a8
and teardown.  This can be immediately used by the PCI MSI-X code,
9ae3a8
which needs to mmap around the MSI-X vector table.
9ae3a8
9ae3a8
This also changes VFIORegion.mem to be dynamically allocated because
9ae3a8
otherwise we don't know how the caller has allocated VFIORegion and
9ae3a8
therefore don't know whether to unreference it to destroy the
9ae3a8
MemoryRegion or not.
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 | 360 +++++++++++++++++++++++++++++++++++++++------------------
9ae3a8
 trace-events   |   9 ++
9ae3a8
 2 files changed, 258 insertions(+), 111 deletions(-)
9ae3a8
9ae3a8
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
9ae3a8
index 57a0065..d634531 100644
9ae3a8
--- a/hw/misc/vfio.c
9ae3a8
+++ b/hw/misc/vfio.c
9ae3a8
@@ -39,6 +39,7 @@
9ae3a8
 #include "qemu/range.h"
9ae3a8
 #include "sysemu/kvm.h"
9ae3a8
 #include "sysemu/sysemu.h"
9ae3a8
+#include "trace.h"
9ae3a8
 
9ae3a8
 /* #define DEBUG_VFIO */
9ae3a8
 #ifdef DEBUG_VFIO
9ae3a8
@@ -84,14 +85,21 @@ typedef struct VFIOQuirk {
9ae3a8
     } data;
9ae3a8
 } VFIOQuirk;
9ae3a8
 
9ae3a8
+typedef struct VFIOMmap {
9ae3a8
+    MemoryRegion mem;
9ae3a8
+    void *mmap;
9ae3a8
+    off_t offset;
9ae3a8
+    size_t size;
9ae3a8
+} VFIOMmap;
9ae3a8
+
9ae3a8
 typedef struct VFIORegion {
9ae3a8
     struct VFIODevice *vbasedev;
9ae3a8
     off_t fd_offset; /* offset of region within device fd */
9ae3a8
-    MemoryRegion mem; /* slow, read/write access */
9ae3a8
-    MemoryRegion mmap_mem; /* direct mapped access */
9ae3a8
-    void *mmap;
9ae3a8
+    MemoryRegion *mem; /* slow, read/write access */
9ae3a8
     size_t size;
9ae3a8
     uint32_t flags; /* VFIO region flags (rd/wr/mmap) */
9ae3a8
+    uint32_t nr_mmaps;
9ae3a8
+    VFIOMmap *mmaps;
9ae3a8
     uint8_t nr; /* cache the region number for debug */
9ae3a8
 } VFIORegion;
9ae3a8
 
9ae3a8
@@ -294,6 +302,9 @@ static void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr,
9ae3a8
 static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled);
9ae3a8
 static int vfio_get_region_info(VFIODevice *vbasedev, int index,
9ae3a8
                                 struct vfio_region_info **info);
9ae3a8
+static void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
9ae3a8
+static void vfio_region_exit(VFIORegion *region);
9ae3a8
+static void vfio_region_finalize(VFIORegion *region);
9ae3a8
 
9ae3a8
 /*
9ae3a8
  * Common VFIO interrupt disable
9ae3a8
@@ -1681,7 +1692,7 @@ static void vfio_probe_ati_bar4_window_quirk(VFIOPCIDevice *vdev, int nr)
9ae3a8
     memory_region_init_io(&quirk->mem,
9ae3a8
                           &vfio_generic_window_quirk, quirk,
9ae3a8
                           "vfio-ati-bar4-window-quirk", 8);
9ae3a8
-    memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
9ae3a8
+    memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
9ae3a8
                           quirk->data.base_offset, &quirk->mem, 1);
9ae3a8
 
9ae3a8
     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
9ae3a8
@@ -1714,7 +1725,7 @@ static void vfio_probe_ati_bar2_4000_quirk(VFIOPCIDevice *vdev, int nr)
9ae3a8
     memory_region_init_io(&quirk->mem, &vfio_generic_quirk, quirk,
9ae3a8
                           "vfio-ati-bar2-4000-quirk",
9ae3a8
                           TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
9ae3a8
-    memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
9ae3a8
+    memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
9ae3a8
                           quirk->data.address_match & TARGET_PAGE_MASK,
9ae3a8
                           &quirk->mem, 1);
9ae3a8
 
9ae3a8
@@ -1939,7 +1950,7 @@ static void vfio_probe_nvidia_bar5_window_quirk(VFIOPCIDevice *vdev, int nr)
9ae3a8
     memory_region_init_io(&quirk->mem,
9ae3a8
                           &vfio_nvidia_bar5_window_quirk, quirk,
9ae3a8
                           "vfio-nvidia-bar5-window-quirk", 16);
9ae3a8
-    memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
9ae3a8
+    memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
9ae3a8
                                         0, &quirk->mem, 1);
9ae3a8
 
9ae3a8
     QLIST_INSERT_HEAD(&vdev->bars[nr].quirks, quirk, next);
9ae3a8
@@ -1977,7 +1988,7 @@ static void vfio_probe_nvidia_bar0_88000_quirk(VFIOPCIDevice *vdev, int nr)
9ae3a8
     memory_region_init_io(&quirk->mem, &vfio_generic_quirk,
9ae3a8
                           quirk, "vfio-nvidia-bar0-88000-quirk",
9ae3a8
                           TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
9ae3a8
-    memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
9ae3a8
+    memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
9ae3a8
                           quirk->data.address_match & TARGET_PAGE_MASK,
9ae3a8
                           &quirk->mem, 1);
9ae3a8
 
9ae3a8
@@ -2015,7 +2026,7 @@ static void vfio_probe_nvidia_bar0_1800_quirk(VFIOPCIDevice *vdev, int nr)
9ae3a8
     memory_region_init_io(&quirk->mem, &vfio_generic_quirk, quirk,
9ae3a8
                           "vfio-nvidia-bar0-1800-quirk",
9ae3a8
                           TARGET_PAGE_ALIGN(quirk->data.address_mask + 1));
9ae3a8
-    memory_region_add_subregion_overlap(&vdev->bars[nr].region.mem,
9ae3a8
+    memory_region_add_subregion_overlap(vdev->bars[nr].region.mem,
9ae3a8
                           quirk->data.address_match & TARGET_PAGE_MASK,
9ae3a8
                           &quirk->mem, 1);
9ae3a8
 
9ae3a8
@@ -2070,7 +2081,7 @@ static void vfio_bar_quirk_teardown(VFIOPCIDevice *vdev, int nr)
9ae3a8
 
9ae3a8
     while (!QLIST_EMPTY(&bar->quirks)) {
9ae3a8
         VFIOQuirk *quirk = QLIST_FIRST(&bar->quirks);
9ae3a8
-        memory_region_del_subregion(&bar->region.mem, &quirk->mem);
9ae3a8
+        memory_region_del_subregion(bar->region.mem, &quirk->mem);
9ae3a8
         memory_region_destroy(&quirk->mem);
9ae3a8
         QLIST_REMOVE(quirk, next);
9ae3a8
         g_free(quirk);
9ae3a8
@@ -2384,6 +2395,74 @@ static int vfio_setup_msi(VFIOPCIDevice *vdev, int pos)
9ae3a8
     return 0;
9ae3a8
 }
9ae3a8
 
9ae3a8
+static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
9ae3a8
+{
9ae3a8
+    off_t start, end;
9ae3a8
+    VFIORegion *region = &vdev->bars[vdev->msix->table_bar].region;
9ae3a8
+
9ae3a8
+    /*
9ae3a8
+     * We expect to find a single mmap covering the whole BAR, anything else
9ae3a8
+     * means it's either unsupported or already setup.
9ae3a8
+     */
9ae3a8
+    if (region->nr_mmaps != 1 || region->mmaps[0].offset ||
9ae3a8
+        region->size != region->mmaps[0].size) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    /* MSI-X table start and end aligned to host page size */
9ae3a8
+    start = vdev->msix->table_offset & TARGET_PAGE_MASK;
9ae3a8
+    end = TARGET_PAGE_ALIGN((uint64_t)vdev->msix->table_offset +
9ae3a8
+                            (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE));
9ae3a8
+
9ae3a8
+    /*
9ae3a8
+     * Does the MSI-X table cover the beginning of the BAR?  The whole BAR?
9ae3a8
+     * NB - Host page size is necessarily a power of two and so is the PCI
9ae3a8
+     * BAR (not counting EA yet), therefore if we have host page aligned
9ae3a8
+     * @start and @end, then any remainder of the BAR before or after those
9ae3a8
+     * must be at least host page sized and therefore mmap'able.
9ae3a8
+     */
9ae3a8
+    if (!start) {
9ae3a8
+        if (end >= region->size) {
9ae3a8
+            region->nr_mmaps = 0;
9ae3a8
+            g_free(region->mmaps);
9ae3a8
+            region->mmaps = NULL;
9ae3a8
+            trace_vfio_msix_fixup(vdev->vbasedev.name,
9ae3a8
+                                  vdev->msix->table_bar, 0, 0);
9ae3a8
+        } else {
9ae3a8
+            region->mmaps[0].offset = end;
9ae3a8
+            region->mmaps[0].size = region->size - end;
9ae3a8
+            trace_vfio_msix_fixup(vdev->vbasedev.name,
9ae3a8
+                              vdev->msix->table_bar, region->mmaps[0].offset,
9ae3a8
+                              region->mmaps[0].offset + region->mmaps[0].size);
9ae3a8
+        }
9ae3a8
+
9ae3a8
+    /* Maybe it's aligned at the end of the BAR */
9ae3a8
+    } else if (end >= region->size) {
9ae3a8
+        region->mmaps[0].size = start;
9ae3a8
+        trace_vfio_msix_fixup(vdev->vbasedev.name,
9ae3a8
+                              vdev->msix->table_bar, region->mmaps[0].offset,
9ae3a8
+                              region->mmaps[0].offset + region->mmaps[0].size);
9ae3a8
+
9ae3a8
+    /* Otherwise it must split the BAR */
9ae3a8
+    } else {
9ae3a8
+        region->nr_mmaps = 2;
9ae3a8
+        region->mmaps = g_renew(VFIOMmap, region->mmaps, 2);
9ae3a8
+
9ae3a8
+        memcpy(&region->mmaps[1], &region->mmaps[0], sizeof(VFIOMmap));
9ae3a8
+
9ae3a8
+        region->mmaps[0].size = start;
9ae3a8
+        trace_vfio_msix_fixup(vdev->vbasedev.name,
9ae3a8
+                              vdev->msix->table_bar, region->mmaps[0].offset,
9ae3a8
+                              region->mmaps[0].offset + region->mmaps[0].size);
9ae3a8
+
9ae3a8
+        region->mmaps[1].offset = end;
9ae3a8
+        region->mmaps[1].size = region->size - end;
9ae3a8
+        trace_vfio_msix_fixup(vdev->vbasedev.name,
9ae3a8
+                              vdev->msix->table_bar, region->mmaps[1].offset,
9ae3a8
+                              region->mmaps[1].offset + region->mmaps[1].size);
9ae3a8
+    }
9ae3a8
+}
9ae3a8
+
9ae3a8
 /*
9ae3a8
  * We don't have any control over how pci_add_capability() inserts
9ae3a8
  * capabilities into the chain.  In order to setup MSI-X we need a
9ae3a8
@@ -2461,6 +2540,8 @@ static int vfio_early_setup_msix(VFIOPCIDevice *vdev)
9ae3a8
         }
9ae3a8
     }
9ae3a8
 
9ae3a8
+    vfio_pci_fixup_msix_region(vdev);
9ae3a8
+
9ae3a8
     return 0;
9ae3a8
 }
9ae3a8
 
9ae3a8
@@ -2469,9 +2550,9 @@ static int vfio_setup_msix(VFIOPCIDevice *vdev, int pos)
9ae3a8
     int ret;
9ae3a8
 
9ae3a8
     ret = msix_init(&vdev->pdev, vdev->msix->entries,
9ae3a8
-                    &vdev->bars[vdev->msix->table_bar].region.mem,
9ae3a8
+                    vdev->bars[vdev->msix->table_bar].region.mem,
9ae3a8
                     vdev->msix->table_bar, vdev->msix->table_offset,
9ae3a8
-                    &vdev->bars[vdev->msix->pba_bar].region.mem,
9ae3a8
+                    vdev->bars[vdev->msix->pba_bar].region.mem,
9ae3a8
                     vdev->msix->pba_bar, vdev->msix->pba_offset, pos);
9ae3a8
     if (ret < 0) {
9ae3a8
         if (ret == -ENOTSUP) {
9ae3a8
@@ -2490,8 +2571,8 @@ static void vfio_teardown_msi(VFIOPCIDevice *vdev)
9ae3a8
 
9ae3a8
     if (vdev->msix) {
9ae3a8
         msix_uninit(&vdev->pdev,
9ae3a8
-                    &vdev->bars[vdev->msix->table_bar].region.mem,
9ae3a8
-                    &vdev->bars[vdev->msix->pba_bar].region.mem);
9ae3a8
+                    vdev->bars[vdev->msix->table_bar].region.mem,
9ae3a8
+                    vdev->bars[vdev->msix->pba_bar].region.mem);
9ae3a8
     }
9ae3a8
 }
9ae3a8
 
9ae3a8
@@ -2503,16 +2584,7 @@ static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled)
9ae3a8
     int i;
9ae3a8
 
9ae3a8
     for (i = 0; i < PCI_ROM_SLOT; i++) {
9ae3a8
-        VFIOBAR *bar = &vdev->bars[i];
9ae3a8
-
9ae3a8
-        if (!bar->region.size) {
9ae3a8
-            continue;
9ae3a8
-        }
9ae3a8
-
9ae3a8
-        memory_region_set_enabled(&bar->region.mmap_mem, enabled);
9ae3a8
-        if (vdev->msix && vdev->msix->table_bar == i) {
9ae3a8
-            memory_region_set_enabled(&vdev->msix->mmap_mem, enabled);
9ae3a8
-        }
9ae3a8
+        vfio_region_mmaps_set_enabled(&vdev->bars[i].region, enabled);
9ae3a8
     }
9ae3a8
 }
9ae3a8
 
9ae3a8
@@ -2526,65 +2598,171 @@ static void vfio_unmap_bar(VFIOPCIDevice *vdev, int nr)
9ae3a8
 
9ae3a8
     vfio_bar_quirk_teardown(vdev, nr);
9ae3a8
 
9ae3a8
-    memory_region_del_subregion(&bar->region.mem, &bar->region.mmap_mem);
9ae3a8
-    munmap(bar->region.mmap, memory_region_size(&bar->region.mmap_mem));
9ae3a8
-    memory_region_destroy(&bar->region.mmap_mem);
9ae3a8
+    vfio_region_exit(&bar->region);
9ae3a8
+    vfio_region_finalize(&bar->region);
9ae3a8
+}
9ae3a8
+
9ae3a8
+static int vfio_region_setup(Object *obj, VFIODevice *vbasedev,
9ae3a8
+                             VFIORegion *region, int index, const char *name)
9ae3a8
+{
9ae3a8
+    struct vfio_region_info *info;
9ae3a8
+    int ret;
9ae3a8
+
9ae3a8
+    ret = vfio_get_region_info(vbasedev, index, &info;;
9ae3a8
+    if (ret) {
9ae3a8
+        return ret;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    region->vbasedev = vbasedev;
9ae3a8
+    region->flags = info->flags;
9ae3a8
+    region->size = info->size;
9ae3a8
+    region->fd_offset = info->offset;
9ae3a8
+    region->nr = index;
9ae3a8
 
9ae3a8
-    if (vdev->msix && vdev->msix->table_bar == nr) {
9ae3a8
-        memory_region_del_subregion(&bar->region.mem, &vdev->msix->mmap_mem);
9ae3a8
-        munmap(vdev->msix->mmap, memory_region_size(&vdev->msix->mmap_mem));
9ae3a8
-        memory_region_destroy(&vdev->msix->mmap_mem);
9ae3a8
+    if (region->size) {
9ae3a8
+        region->mem = g_new0(MemoryRegion, 1);
9ae3a8
+        memory_region_init_io(region->mem, &vfio_region_ops,
9ae3a8
+                              region, name, region->size);
9ae3a8
+
9ae3a8
+        if (VFIO_ALLOW_MMAP &&
9ae3a8
+            region->flags & VFIO_REGION_INFO_FLAG_MMAP &&
9ae3a8
+            !(region->size & ~TARGET_PAGE_MASK)) {
9ae3a8
+
9ae3a8
+            region->nr_mmaps = 1;
9ae3a8
+            region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
9ae3a8
+
9ae3a8
+            region->mmaps[0].offset = 0;
9ae3a8
+            region->mmaps[0].size = region->size;
9ae3a8
+        }
9ae3a8
     }
9ae3a8
 
9ae3a8
-    memory_region_destroy(&bar->region.mem);
9ae3a8
+    g_free(info);
9ae3a8
+
9ae3a8
+    trace_vfio_region_setup(vbasedev->name, index, name,
9ae3a8
+                            region->flags, region->fd_offset, region->size);
9ae3a8
+    return 0;
9ae3a8
 }
9ae3a8
 
9ae3a8
-static int vfio_mmap_region(Object *obj, VFIORegion *region,
9ae3a8
-                            MemoryRegion *mem, MemoryRegion *submem,
9ae3a8
-                            void **map, size_t size, off_t offset,
9ae3a8
-                            const char *name)
9ae3a8
+static int vfio_region_mmap(VFIORegion *region)
9ae3a8
 {
9ae3a8
-    int ret = 0;
9ae3a8
-    VFIODevice *vbasedev = region->vbasedev;
9ae3a8
+    int i, prot = 0;
9ae3a8
+    char *name;
9ae3a8
+
9ae3a8
+    if (!region->mem) {
9ae3a8
+        return 0;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0;
9ae3a8
+    prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
9ae3a8
+
9ae3a8
+    for (i = 0; i < region->nr_mmaps; i++) {
9ae3a8
+        region->mmaps[i].mmap = mmap(NULL, region->mmaps[i].size, prot,
9ae3a8
+                                     MAP_SHARED, region->vbasedev->fd,
9ae3a8
+                                     region->fd_offset +
9ae3a8
+                                     region->mmaps[i].offset);
9ae3a8
+        if (region->mmaps[i].mmap == MAP_FAILED) {
9ae3a8
+            int ret = -errno;
9ae3a8
 
9ae3a8
-    if (VFIO_ALLOW_MMAP && size && region->flags &
9ae3a8
-        VFIO_REGION_INFO_FLAG_MMAP) {
9ae3a8
-        int prot = 0;
9ae3a8
+            trace_vfio_region_mmap_fault(memory_region_name(region->mem), i,
9ae3a8
+                                         region->fd_offset +
9ae3a8
+                                         region->mmaps[i].offset,
9ae3a8
+                                         region->fd_offset +
9ae3a8
+                                         region->mmaps[i].offset +
9ae3a8
+                                         region->mmaps[i].size - 1, ret);
9ae3a8
 
9ae3a8
-        if (region->flags & VFIO_REGION_INFO_FLAG_READ) {
9ae3a8
-            prot |= PROT_READ;
9ae3a8
+            region->mmaps[i].mmap = NULL;
9ae3a8
+
9ae3a8
+            for (i--; i >= 0; i--) {
9ae3a8
+                memory_region_del_subregion(region->mem, &region->mmaps[i].mem);
9ae3a8
+                munmap(region->mmaps[i].mmap, region->mmaps[i].size);
9ae3a8
+                memory_region_destroy(&region->mmaps[i].mem);
9ae3a8
+                region->mmaps[i].mmap = NULL;
9ae3a8
+            }
9ae3a8
+
9ae3a8
+            return ret;
9ae3a8
         }
9ae3a8
 
9ae3a8
-        if (region->flags & VFIO_REGION_INFO_FLAG_WRITE) {
9ae3a8
-            prot |= PROT_WRITE;
9ae3a8
+        name = g_strdup_printf("%s mmaps[%d]",
9ae3a8
+                               memory_region_name(region->mem), i);
9ae3a8
+        memory_region_init_ram_ptr(&region->mmaps[i].mem,
9ae3a8
+                                   name, region->mmaps[i].size,
9ae3a8
+                                   region->mmaps[i].mmap);
9ae3a8
+        g_free(name);
9ae3a8
+        memory_region_set_skip_dump(&region->mmaps[i].mem);
9ae3a8
+        memory_region_add_subregion(region->mem, region->mmaps[i].offset,
9ae3a8
+                                    &region->mmaps[i].mem);
9ae3a8
+
9ae3a8
+        trace_vfio_region_mmap(memory_region_name(&region->mmaps[i].mem),
9ae3a8
+                               region->mmaps[i].offset,
9ae3a8
+                               region->mmaps[i].offset +
9ae3a8
+                               region->mmaps[i].size - 1);
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    return 0;
9ae3a8
+}
9ae3a8
+
9ae3a8
+static void vfio_region_exit(VFIORegion *region)
9ae3a8
+{
9ae3a8
+    int i;
9ae3a8
+
9ae3a8
+    if (!region->mem) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    for (i = 0; i < region->nr_mmaps; i++) {
9ae3a8
+        if (region->mmaps[i].mmap) {
9ae3a8
+            memory_region_del_subregion(region->mem, &region->mmaps[i].mem);
9ae3a8
         }
9ae3a8
+    }
9ae3a8
+
9ae3a8
+    trace_vfio_region_exit(region->vbasedev->name, region->nr);
9ae3a8
+}
9ae3a8
+
9ae3a8
+static void vfio_region_finalize(VFIORegion *region)
9ae3a8
+{
9ae3a8
+    int i;
9ae3a8
+
9ae3a8
+    if (!region->mem) {
9ae3a8
+        return;
9ae3a8
+    }
9ae3a8
 
9ae3a8
-        *map = mmap(NULL, size, prot, MAP_SHARED,
9ae3a8
-                    vbasedev->fd, region->fd_offset + offset);
9ae3a8
-        if (*map == MAP_FAILED) {
9ae3a8
-            *map = NULL;
9ae3a8
-            ret = -errno;
9ae3a8
-            goto empty_region;
9ae3a8
+    for (i = 0; i < region->nr_mmaps; i++) {
9ae3a8
+        if (region->mmaps[i].mmap) {
9ae3a8
+            munmap(region->mmaps[i].mmap, region->mmaps[i].size);
9ae3a8
+            memory_region_destroy(&region->mmaps[i].mem);
9ae3a8
         }
9ae3a8
+    }
9ae3a8
 
9ae3a8
-        memory_region_init_ram_ptr(submem, name, size, *map);
9ae3a8
-        memory_region_set_skip_dump(submem);
9ae3a8
-    } else {
9ae3a8
-empty_region:
9ae3a8
-        /* Create a zero sized sub-region to make cleanup easy. */
9ae3a8
-        memory_region_init(submem, name, 0);
9ae3a8
+    memory_region_destroy(region->mem);
9ae3a8
+
9ae3a8
+    g_free(region->mem);
9ae3a8
+    g_free(region->mmaps);
9ae3a8
+
9ae3a8
+    trace_vfio_region_finalize(region->vbasedev->name, region->nr);
9ae3a8
+}
9ae3a8
+
9ae3a8
+static void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled)
9ae3a8
+{
9ae3a8
+    int i;
9ae3a8
+
9ae3a8
+    if (!region->mem) {
9ae3a8
+        return;
9ae3a8
     }
9ae3a8
 
9ae3a8
-    memory_region_add_subregion(mem, offset, submem);
9ae3a8
+    for (i = 0; i < region->nr_mmaps; i++) {
9ae3a8
+        if (region->mmaps[i].mmap) {
9ae3a8
+            memory_region_set_enabled(&region->mmaps[i].mem, enabled);
9ae3a8
+        }
9ae3a8
+    }
9ae3a8
 
9ae3a8
-    return ret;
9ae3a8
+    trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem),
9ae3a8
+                                        enabled);
9ae3a8
 }
9ae3a8
 
9ae3a8
 static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
9ae3a8
 {
9ae3a8
     VFIOBAR *bar = &vdev->bars[nr];
9ae3a8
     uint64_t size = bar->region.size;
9ae3a8
-    char name[64];
9ae3a8
     uint32_t pci_bar;
9ae3a8
     uint8_t type;
9ae3a8
     int ret;
9ae3a8
@@ -2594,8 +2772,6 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
9ae3a8
         return;
9ae3a8
     }
9ae3a8
 
9ae3a8
-    snprintf(name, sizeof(name), "VFIO %s BAR %d", vdev->vbasedev.name, nr);
9ae3a8
-
9ae3a8
     /* Determine what type of BAR this is for registration */
9ae3a8
     ret = pread(vdev->vbasedev.fd, &pci_bar, sizeof(pci_bar),
9ae3a8
                 vdev->config_offset + PCI_BASE_ADDRESS_0 + (4 * nr));
9ae3a8
@@ -2610,40 +2786,11 @@ static void vfio_map_bar(VFIOPCIDevice *vdev, int nr)
9ae3a8
     type = pci_bar & (bar->ioport ? ~PCI_BASE_ADDRESS_IO_MASK :
9ae3a8
                                     ~PCI_BASE_ADDRESS_MEM_MASK);
9ae3a8
 
9ae3a8
-    /* A "slow" read/write mapping underlies all BARs */
9ae3a8
-    memory_region_init_io(&bar->region.mem, &vfio_region_ops,
9ae3a8
-                          bar, name, size);
9ae3a8
-    pci_register_bar(&vdev->pdev, nr, type, &bar->region.mem);
9ae3a8
-
9ae3a8
-    /*
9ae3a8
-     * We can't mmap areas overlapping the MSIX vector table, so we
9ae3a8
-     * potentially insert a direct-mapped subregion before and after it.
9ae3a8
-     */
9ae3a8
-    if (vdev->msix && vdev->msix->table_bar == nr) {
9ae3a8
-        size = vdev->msix->table_offset & TARGET_PAGE_MASK;
9ae3a8
-    }
9ae3a8
-
9ae3a8
-    strncat(name, " mmap", sizeof(name) - strlen(name) - 1);
9ae3a8
-    if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem,
9ae3a8
-                      &bar->region.mmap_mem, &bar->region.mmap,
9ae3a8
-                      size, 0, name)) {
9ae3a8
-        error_report("%s unsupported. Performance may be slow", name);
9ae3a8
-    }
9ae3a8
-
9ae3a8
-    if (vdev->msix && vdev->msix->table_bar == nr) {
9ae3a8
-        uint64_t start;
9ae3a8
+    pci_register_bar(&vdev->pdev, nr, type, bar->region.mem);
9ae3a8
 
9ae3a8
-        start = TARGET_PAGE_ALIGN((uint64_t)vdev->msix->table_offset +
9ae3a8
-                                  (vdev->msix->entries * PCI_MSIX_ENTRY_SIZE));
9ae3a8
-
9ae3a8
-        size = start < bar->region.size ? bar->region.size - start : 0;
9ae3a8
-        strncat(name, " msix-hi", sizeof(name) - strlen(name) - 1);
9ae3a8
-        /* VFIOMSIXInfo contains another MemoryRegion for this mapping */
9ae3a8
-        if (vfio_mmap_region(OBJECT(vdev), &bar->region, &bar->region.mem,
9ae3a8
-                          &vdev->msix->mmap_mem,
9ae3a8
-                          &vdev->msix->mmap, size, start, name)) {
9ae3a8
-            error_report("%s unsupported. Performance may be slow", name);
9ae3a8
-        }
9ae3a8
+    if (vfio_region_mmap(&bar->region)) {
9ae3a8
+        error_report("Failed to mmap %s BAR %d. Performance may be slow",
9ae3a8
+                     vdev->vbasedev.name, nr);
9ae3a8
     }
9ae3a8
 
9ae3a8
     vfio_bar_quirk_setup(vdev, nr);
9ae3a8
@@ -3531,25 +3678,18 @@ static int vfio_get_device(VFIOGroup *group, const char *name,
9ae3a8
     }
9ae3a8
 
9ae3a8
     for (i = VFIO_PCI_BAR0_REGION_INDEX; i < VFIO_PCI_ROM_REGION_INDEX; i++) {
9ae3a8
-        ret = vfio_get_region_info(&vdev->vbasedev, i, &reg_info);
9ae3a8
+        char *name = g_strdup_printf("%s BAR %d", vdev->vbasedev.name, i);
9ae3a8
+
9ae3a8
+        ret = vfio_region_setup(OBJECT(vdev), &vdev->vbasedev,
9ae3a8
+                                &vdev->bars[i].region, i, name);
9ae3a8
+        g_free(name);
9ae3a8
+
9ae3a8
         if (ret) {
9ae3a8
             error_report("vfio: Error getting region %d info: %m", i);
9ae3a8
             goto error;
9ae3a8
         }
9ae3a8
 
9ae3a8
-        DPRINTF("Device %s region %d:\n", name, i);
9ae3a8
-        DPRINTF("  size: 0x%lx, offset: 0x%lx, flags: 0x%lx\n",
9ae3a8
-                (unsigned long)reg_info->size, (unsigned long)reg_info->offset,
9ae3a8
-                (unsigned long)reg_info->flags);
9ae3a8
-
9ae3a8
-        vdev->bars[i].region.vbasedev = &vdev->vbasedev;
9ae3a8
-        vdev->bars[i].region.flags = reg_info->flags;
9ae3a8
-        vdev->bars[i].region.size = reg_info->size;
9ae3a8
-        vdev->bars[i].region.fd_offset = reg_info->offset;
9ae3a8
-        vdev->bars[i].region.nr = i;
9ae3a8
         QLIST_INIT(&vdev->bars[i].quirks);
9ae3a8
-
9ae3a8
-        g_free(reg_info);
9ae3a8
     }
9ae3a8
 
9ae3a8
     ret = vfio_get_region_info(&vdev->vbasedev,
9ae3a8
@@ -3644,10 +3784,8 @@ static void vfio_put_device(VFIOPCIDevice *vdev)
9ae3a8
     DPRINTF("vfio_put_device: close vdev->vbasedev.fd\n");
9ae3a8
     close(vdev->vbasedev.fd);
9ae3a8
     g_free(vdev->vbasedev.name);
9ae3a8
-    if (vdev->msix) {
9ae3a8
-        g_free(vdev->msix);
9ae3a8
-        vdev->msix = NULL;
9ae3a8
-    }
9ae3a8
+    g_free(vdev->msix);
9ae3a8
+
9ae3a8
 }
9ae3a8
 
9ae3a8
 static int vfio_get_region_info(VFIODevice *vbasedev, int index,
9ae3a8
diff --git a/trace-events b/trace-events
9ae3a8
index 6cd46e9..cc62b0b 100644
9ae3a8
--- a/trace-events
9ae3a8
+++ b/trace-events
9ae3a8
@@ -1155,3 +1155,12 @@ kvm_run_exit(int cpu_index, uint32_t reason) "cpu_index %d, reason %d"
9ae3a8
 # qom/object.c
9ae3a8
 object_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)"
9ae3a8
 object_class_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)"
9ae3a8
+
9ae3a8
+# hw/misc/vfio.c
9ae3a8
+vfio_msix_fixup(const char *name, int bar, uint64_t start, uint64_t end) " (%s) MSI-X region %d mmap fixup [0x%"PRIx64" - 0x%"PRIx64"]"
9ae3a8
+vfio_region_setup(const char *dev, int index, const char *name, unsigned long flags, unsigned long offset, unsigned long size) "Device %s, region %d \"%s\", flags: %lx, offset: %lx, size: %lx"
9ae3a8
+vfio_region_mmap_fault(const char *name, int index, unsigned long offset, unsigned long size, int fault) "Region %s mmaps[%d], [%lx - %lx], fault: %d"
9ae3a8
+vfio_region_mmap(const char *name, unsigned long offset, unsigned long end) "Region %s [%lx - %lx]"
9ae3a8
+vfio_region_exit(const char *name, int index) "Device %s, region %d"
9ae3a8
+vfio_region_finalize(const char *name, int index) "Device %s, region %d"
9ae3a8
+vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps enabled: %d"
9ae3a8
-- 
9ae3a8
1.8.3.1
9ae3a8