5d360b
From bbd8cc516329f84b70d38a75820f36f2ecd0abda Mon Sep 17 00:00:00 2001
5d360b
From: Alex Williamson <alex.williamson@redhat.com>
5d360b
Date: Fri, 29 Sep 2017 21:46:14 +0200
5d360b
Subject: [PATCH 15/27] vfio: Enable sparse mmap capability
5d360b
5d360b
RH-Author: Alex Williamson <alex.williamson@redhat.com>
5d360b
Message-id: <20170929214614.16765.48627.stgit@gimli.home>
5d360b
Patchwork-id: 76773
5d360b
O-Subject: [RHEL-7.5 qemu-kvm PATCH 15/16] vfio: Enable sparse mmap capability
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: b53b0f696b10828f6393155f44a352c019e673fd
5d360b
RHEL: Roll in required linux-headers update
5d360b
5d360b
The sparse mmap capability in a vfio region info allows vfio to tell
5d360b
us which sub-areas of a region may be mmap'd.  Thus rather than
5d360b
assuming a single mmap covers the entire region and later frobbing it
5d360b
ourselves for things like the PCI MSI-X vector table, we can read that
5d360b
directly from vfio.
5d360b
5d360b
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
5d360b
Reviewed-by: Gerd Hoffmann <kraxel@redhat.com>
5d360b
Tested-by: Gerd Hoffmann <kraxel@redhat.com>
5d360b
Signed-off-by: Miroslav Rezanina <mrezanin@redhat.com>
5d360b
---
5d360b
 hw/misc/vfio.c             | 67 +++++++++++++++++++++++++++++++++++++++++++---
5d360b
 linux-headers/linux/vfio.h | 53 +++++++++++++++++++++++++++++++++++-
5d360b
 trace-events               |  2 ++
5d360b
 3 files changed, 117 insertions(+), 5 deletions(-)
5d360b
5d360b
diff --git a/hw/misc/vfio.c b/hw/misc/vfio.c
5d360b
index d634531..a27698b 100644
5d360b
--- a/hw/misc/vfio.c
5d360b
+++ b/hw/misc/vfio.c
5d360b
@@ -2602,6 +2602,54 @@ static void vfio_unmap_bar(VFIOPCIDevice *vdev, int nr)
5d360b
     vfio_region_finalize(&bar->region);
5d360b
 }
5d360b
 
5d360b
+static struct vfio_info_cap_header *
5d360b
+vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id)
5d360b
+{
5d360b
+    struct vfio_info_cap_header *hdr;
5d360b
+    void *ptr = info;
5d360b
+
5d360b
+    if (!(info->flags & VFIO_REGION_INFO_FLAG_CAPS)) {
5d360b
+        return NULL;
5d360b
+    }
5d360b
+
5d360b
+    for (hdr = ptr + info->cap_offset; hdr != ptr; hdr = ptr + hdr->next) {
5d360b
+        if (hdr->id == id) {
5d360b
+            return hdr;
5d360b
+        }
5d360b
+    }
5d360b
+
5d360b
+    return NULL;
5d360b
+}
5d360b
+
5d360b
+static void 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
+
5d360b
+    hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP);
5d360b
+    if (!hdr) {
5d360b
+        return;
5d360b
+    }
5d360b
+
5d360b
+    sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header);
5d360b
+
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
+
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
+    }
5d360b
+}
5d360b
+
5d360b
 static int vfio_region_setup(Object *obj, VFIODevice *vbasedev,
5d360b
                              VFIORegion *region, int index, const char *name)
5d360b
 {
5d360b
@@ -2628,11 +2676,14 @@ 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
-            region->nr_mmaps = 1;
5d360b
-            region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
5d360b
+            vfio_setup_region_sparse_mmaps(region, info);
5d360b
 
5d360b
-            region->mmaps[0].offset = 0;
5d360b
-            region->mmaps[0].size = region->size;
5d360b
+            if (!region->nr_mmaps) {
5d360b
+                region->nr_mmaps = 1;
5d360b
+                region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
5d360b
+                region->mmaps[0].offset = 0;
5d360b
+                region->mmaps[0].size = region->size;
5d360b
+            }
5d360b
         }
5d360b
     }
5d360b
 
5d360b
@@ -3796,6 +3847,7 @@ static int vfio_get_region_info(VFIODevice *vbasedev, int index,
5d360b
     *info = g_malloc0(argsz);
5d360b
 
5d360b
     (*info)->index = index;
5d360b
+retry:
5d360b
     (*info)->argsz = argsz;
5d360b
 
5d360b
     if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) {
5d360b
@@ -3803,6 +3855,13 @@ static int vfio_get_region_info(VFIODevice *vbasedev, int index,
5d360b
         return -errno;
5d360b
     }
5d360b
 
5d360b
+    if ((*info)->argsz > argsz) {
5d360b
+        argsz = (*info)->argsz;
5d360b
+        *info = g_realloc(*info, argsz);
5d360b
+
5d360b
+        goto retry;
5d360b
+    }
5d360b
+
5d360b
     return 0;
5d360b
 }
5d360b
 
5d360b
diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h
5d360b
index d197fd4..8995a34 100644
5d360b
--- a/linux-headers/linux/vfio.h
5d360b
+++ b/linux-headers/linux/vfio.h
5d360b
@@ -38,6 +38,33 @@
5d360b
 #define VFIO_TYPE	(';')
5d360b
 #define VFIO_BASE	100
5d360b
 
5d360b
+/*
5d360b
+ * For extension of INFO ioctls, VFIO makes use of a capability chain
5d360b
+ * designed after PCI/e capabilities.  A flag bit indicates whether
5d360b
+ * this capability chain is supported and a field defined in the fixed
5d360b
+ * structure defines the offset of the first capability in the chain.
5d360b
+ * This field is only valid when the corresponding bit in the flags
5d360b
+ * bitmap is set.  This offset field is relative to the start of the
5d360b
+ * INFO buffer, as is the next field within each capability header.
5d360b
+ * The id within the header is a shared address space per INFO ioctl,
5d360b
+ * while the version field is specific to the capability id.  The
5d360b
+ * contents following the header are specific to the capability id.
5d360b
+ */
5d360b
+struct vfio_info_cap_header {
5d360b
+	__u16	id;		/* Identifies capability */
5d360b
+	__u16	version;	/* Version specific to the capability ID */
5d360b
+	__u32	next;		/* Offset of next capability */
5d360b
+};
5d360b
+
5d360b
+/*
5d360b
+ * Callers of INFO ioctls passing insufficiently sized buffers will see
5d360b
+ * the capability chain flag bit set, a zero value for the first capability
5d360b
+ * offset (if available within the provided argsz), and argsz will be
5d360b
+ * updated to report the necessary buffer size.  For compatibility, the
5d360b
+ * INFO ioctl will not report error in this case, but the capability chain
5d360b
+ * will not be available.
5d360b
+ */
5d360b
+
5d360b
 /* -------- IOCTLs for VFIO file descriptor (/dev/vfio/vfio) -------- */
5d360b
 
5d360b
 /**
5d360b
@@ -171,13 +198,37 @@ struct vfio_region_info {
5d360b
 #define VFIO_REGION_INFO_FLAG_READ	(1 << 0) /* Region supports read */
5d360b
 #define VFIO_REGION_INFO_FLAG_WRITE	(1 << 1) /* Region supports write */
5d360b
 #define VFIO_REGION_INFO_FLAG_MMAP	(1 << 2) /* Region supports mmap */
5d360b
+#define VFIO_REGION_INFO_FLAG_CAPS	(1 << 3) /* Info supports caps */
5d360b
 	__u32	index;		/* Region index */
5d360b
-	__u32	resv;		/* Reserved for alignment */
5d360b
+	__u32	cap_offset;	/* Offset within info struct of first cap */
5d360b
 	__u64	size;		/* Region size (bytes) */
5d360b
 	__u64	offset;		/* Region offset from start of device fd */
5d360b
 };
5d360b
 #define VFIO_DEVICE_GET_REGION_INFO	_IO(VFIO_TYPE, VFIO_BASE + 8)
5d360b
 
5d360b
+/*
5d360b
+ * The sparse mmap capability allows finer granularity of specifying areas
5d360b
+ * within a region with mmap support.  When specified, the user should only
5d360b
+ * mmap the offset ranges specified by the areas array.  mmaps outside of the
5d360b
+ * areas specified may fail (such as the range covering a PCI MSI-X table) or
5d360b
+ * may result in improper device behavior.
5d360b
+ *
5d360b
+ * The structures below define version 1 of this capability.
5d360b
+ */
5d360b
+#define VFIO_REGION_INFO_CAP_SPARSE_MMAP        1
5d360b
+
5d360b
+struct vfio_region_sparse_mmap_area {
5d360b
+	__u64	offset;	/* Offset of mmap'able area within region */
5d360b
+	__u64	size;	/* Size of mmap'able area */
5d360b
+};
5d360b
+
5d360b
+struct vfio_region_info_cap_sparse_mmap {
5d360b
+	struct vfio_info_cap_header header;
5d360b
+	__u32	nr_areas;
5d360b
+	__u32	reserved;
5d360b
+	struct vfio_region_sparse_mmap_area areas[];
5d360b
+};
5d360b
+
5d360b
 /**
5d360b
  * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9,
5d360b
  *				    struct vfio_irq_info)
5d360b
diff --git a/trace-events b/trace-events
5d360b
index cc62b0b..fa2618d 100644
5d360b
--- a/trace-events
5d360b
+++ b/trace-events
5d360b
@@ -1164,3 +1164,5 @@ vfio_region_mmap(const char *name, unsigned long offset, unsigned long end) "Reg
5d360b
 vfio_region_exit(const char *name, int index) "Device %s, region %d"
5d360b
 vfio_region_finalize(const char *name, int index) "Device %s, region %d"
5d360b
 vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps enabled: %d"
5d360b
+vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries"
5d360b
+vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]"
5d360b
-- 
5d360b
1.8.3.1
5d360b