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