5d360b
From 9291ea1d33e70d9c01558da25ac6744ff2ef77ec Mon Sep 17 00:00:00 2001
5d360b
From: Alex Williamson <alex.williamson@redhat.com>
5d360b
Date: Fri, 29 Sep 2017 21:46:24 +0200
5d360b
Subject: [PATCH 16/27] vfio: Handle zero-length sparse mmap ranges
5d360b
5d360b
RH-Author: Alex Williamson <alex.williamson@redhat.com>
5d360b
Message-id: <20170929214624.16765.84023.stgit@gimli.home>
5d360b
Patchwork-id: 76774
5d360b
O-Subject: [RHEL-7.5 qemu-kvm PATCH 16/16] vfio: Handle zero-length sparse mmap ranges
5d360b
Bugzilla: 1494181
5d360b
RH-Acked-by: Paolo Bonzini <pbonzini@redhat.com>
5d360b
RH-Acked-by: Auger Eric <eric.auger@redhat.com>
5d360b
RH-Acked-by: Miroslav Rezanina <mrezanin@redhat.com>
5d360b
5d360b
Upstream: 24acf72b9a291cebfd05f2ecdf3a982ac01e6291
5d360b
5d360b
As reported in the link below, user has a PCI device with a 4KB BAR
5d360b
which contains the MSI-X table.  This seems to hit a corner case in
5d360b
the kernel where the region reports being mmap capable, but the sparse
5d360b
mmap information reports a zero sized range.  It's not entirely clear
5d360b
that the kernel is incorrect in doing this, but regardless, we need
5d360b
to handle it.  To do this, fill our mmap array only with non-zero
5d360b
sized sparse mmap entries and add an error return from the function
5d360b
so we can tell the difference between nr_mmaps being zero based on
5d360b
sparse mmap info vs lack of sparse mmap info.
5d360b
5d360b
NB, this doesn't actually change the behavior of the device, it only
5d360b
removes the scary "Failed to mmap ... Performance may be slow" error
5d360b
message.  We cannot currently create an mmap over the MSI-X table.
5d360b
5d360b
Link: http://lists.nongnu.org/archive/html/qemu-discuss/2016-10/msg00009.html
5d360b
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
5d360b
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
5d360b
---
5d360b
 hw/misc/vfio.c | 36 ++++++++++++++++++++++--------------
5d360b
 1 file changed, 22 insertions(+), 14 deletions(-)
5d360b
5d360b
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
5d360b
index a27698b..68ff949 100644
5d360b
--- a/hw/misc/vfio.c
5d360b
+++ b/hw/misc/vfio.c
5d360b
@@ -2621,16 +2621,16 @@ vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id)
5d360b
     return NULL;
5d360b
 }
5d360b
 
5d360b
-static void vfio_setup_region_sparse_mmaps(VFIORegion *region,
5d360b
-                                           struct vfio_region_info *info)
5d360b
+static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
5d360b
+                                          struct vfio_region_info *info)
5d360b
 {
5d360b
     struct vfio_info_cap_header *hdr;
5d360b
     struct vfio_region_info_cap_sparse_mmap *sparse;
5d360b
-    int i;
5d360b
+    int i, j;
5d360b
 
5d360b
     hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP);
5d360b
     if (!hdr) {
5d360b
-        return;
5d360b
+        return -ENODEV;
5d360b
     }
5d360b
 
5d360b
     sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header);
5d360b
@@ -2638,16 +2638,24 @@ static void vfio_setup_region_sparse_mmaps(VFIORegion *region,
5d360b
     trace_vfio_region_sparse_mmap_header(region->vbasedev->name,
5d360b
                                          region->nr, sparse->nr_areas);
5d360b
 
5d360b
-    region->nr_mmaps = sparse->nr_areas;
5d360b
-    region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
5d360b
+    region->mmaps = g_new0(VFIOMmap, sparse->nr_areas);
5d360b
 
5d360b
-    for (i = 0; i < region->nr_mmaps; i++) {
5d360b
-        region->mmaps[i].offset = sparse->areas[i].offset;
5d360b
-        region->mmaps[i].size = sparse->areas[i].size;
5d360b
-        trace_vfio_region_sparse_mmap_entry(i, region->mmaps[i].offset,
5d360b
-                                            region->mmaps[i].offset +
5d360b
-                                            region->mmaps[i].size);
5d360b
+    for (i = 0, j = 0; i < sparse->nr_areas; i++) {
5d360b
+        trace_vfio_region_sparse_mmap_entry(i, sparse->areas[i].offset,
5d360b
+                                            sparse->areas[i].offset +
5d360b
+                                            sparse->areas[i].size);
5d360b
+
5d360b
+        if (sparse->areas[i].size) {
5d360b
+            region->mmaps[j].offset = sparse->areas[i].offset;
5d360b
+            region->mmaps[j].size = sparse->areas[i].size;
5d360b
+            j++;
5d360b
+        }
5d360b
     }
5d360b
+
5d360b
+    region->nr_mmaps = j;
5d360b
+    region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap));
5d360b
+
5d360b
+    return 0;
5d360b
 }
5d360b
 
5d360b
 static int vfio_region_setup(Object *obj, VFIODevice *vbasedev,
5d360b
@@ -2676,9 +2684,9 @@ static int vfio_region_setup(Object *obj, VFIODevice *vbasedev,
5d360b
             region->flags & VFIO_REGION_INFO_FLAG_MMAP &&
5d360b
             !(region->size & ~TARGET_PAGE_MASK)) {
5d360b
 
5d360b
-            vfio_setup_region_sparse_mmaps(region, info);
5d360b
+            ret = vfio_setup_region_sparse_mmaps(region, info);
5d360b
 
5d360b
-            if (!region->nr_mmaps) {
5d360b
+            if (ret) {
5d360b
                 region->nr_mmaps = 1;
5d360b
                 region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
5d360b
                 region->mmaps[0].offset = 0;
5d360b
-- 
5d360b
1.8.3.1
5d360b