summary refs log tree commit diff stats
path: root/hw/vfio/helpers.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2024-10-25 13:35:13 +0100
committerPeter Maydell <peter.maydell@linaro.org>2024-10-25 13:35:13 +0100
commit94be8fd6926cb20c1aa8dd361c6bc10b39b9a376 (patch)
tree17bddae13bbbdce40753e6c309d2f906938c0ce4 /hw/vfio/helpers.c
parente17e57e862faf6e1f372385c18dcf6d3fd31158e (diff)
parent00b519c0bca0e933ed22e2e6f8bca6b23f41f950 (diff)
downloadfocaccia-qemu-94be8fd6926cb20c1aa8dd361c6bc10b39b9a376.tar.gz
focaccia-qemu-94be8fd6926cb20c1aa8dd361c6bc10b39b9a376.zip
Merge tag 'pull-vfio-20241024' of https://github.com/legoater/qemu into staging
vfio queue:

* Fixed size reported in vfio_state_pending_exact()
* Added support for PMD or PUD aligned mappings

# -----BEGIN PGP SIGNATURE-----
#
# iQIzBAABCAAdFiEEoPZlSPBIlev+awtgUaNDx8/77KEFAmcZ22wACgkQUaNDx8/7
# 7KHU5g/8Cr1487IJQb5cbpLu2Nviu3wjzhbCFFdbl99uLifdc0GK1P6fqDNQ7BVx
# 2vpZgJRXLTxlUSTpreFw4z6TH7/C4HoNiluQV4l0vxqG/Y9q68SJBpT9WENwXUyY
# +2laDmGQbUjDznxIFlmCgZZAssCIJNp0esNE9hvwkQCarZx9m+QQSSkeVHVWNFqX
# +zTd4v076Q9hi53+4e7FlqFKaFoa54IcZe3gz+GjY/IXMqCDNFw9e9xJxML+zSg3
# HZ4/YMQj+EsKX2gm460EYBmt13kd0wdtFzA1MNc7XcSlBlLk/WmezpEzHZRubiLs
# mbUZ68/cweJmrO0WatycWg9JwQ2q9FlKH1Acgun4Fcf8Zov5ovHuYAsWYbdGDbN1
# E7pY/XlUf6b7Vk+yAGTnKKRi6OguTEmVyRRFy/4V8TwvZNycbeOMebKilGQUGfKj
# iLWuzF6NilT4ZGo7sWnlLZWcmrxN57wJh77GlmcqiqguskB8WGdh/SZSVCkkzr3y
# PN3FGSTseNaxalcjECEFnfE8+bUShLei+I6fppTfqLBaLHJ72lRel0Cg07FS8oM4
# 3ev7etH7jFT5xET00DBamDXacgNtLqFqO6XIK3bFTkLmP0FFQi9u+bvy04IyTVCC
# gd9Zg2vhxp0mjuwtelB+i7yD3pmA2LWFkEzoShpkH/h38CnpoyQ=
# =+69I
# -----END PGP SIGNATURE-----
# gpg: Signature made Thu 24 Oct 2024 06:30:20 BST
# gpg:                using RSA key A0F66548F04895EBFE6B0B6051A343C7CFFBECA1
# gpg: Good signature from "Cédric Le Goater <clg@redhat.com>" [full]
# gpg:                 aka "Cédric Le Goater <clg@kaod.org>" [full]
# Primary key fingerprint: A0F6 6548 F048 95EB FE6B  0B60 51A3 43C7 CFFB ECA1

* tag 'pull-vfio-20241024' of https://github.com/legoater/qemu:
  vfio/helpers: Align mmaps
  vfio/helpers: Refactor vfio_region_mmap() error handling
  vfio/migration: Change trace formats from hex to decimal
  vfio/migration: Report only stop-copy size in vfio_state_pending_exact()

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/vfio/helpers.c')
-rw-r--r--hw/vfio/helpers.c66
1 files changed, 47 insertions, 19 deletions
diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c
index ea15c79db0..913796f437 100644
--- a/hw/vfio/helpers.c
+++ b/hw/vfio/helpers.c
@@ -27,6 +27,7 @@
 #include "trace.h"
 #include "qapi/error.h"
 #include "qemu/error-report.h"
+#include "qemu/units.h"
 #include "monitor/monitor.h"
 
 /*
@@ -395,7 +396,7 @@ static void vfio_subregion_unmap(VFIORegion *region, int index)
 
 int vfio_region_mmap(VFIORegion *region)
 {
-    int i, prot = 0;
+    int i, ret, prot = 0;
     char *name;
 
     if (!region->mem) {
@@ -406,27 +407,40 @@ int vfio_region_mmap(VFIORegion *region)
     prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0;
 
     for (i = 0; i < region->nr_mmaps; i++) {
-        region->mmaps[i].mmap = mmap(NULL, region->mmaps[i].size, prot,
-                                     MAP_SHARED, region->vbasedev->fd,
-                                     region->fd_offset +
-                                     region->mmaps[i].offset);
-        if (region->mmaps[i].mmap == MAP_FAILED) {
-            int ret = -errno;
+        size_t align = MIN(1ULL << ctz64(region->mmaps[i].size), 1 * GiB);
+        void *map_base, *map_align;
 
-            trace_vfio_region_mmap_fault(memory_region_name(region->mem), i,
-                                         region->fd_offset +
-                                         region->mmaps[i].offset,
-                                         region->fd_offset +
-                                         region->mmaps[i].offset +
-                                         region->mmaps[i].size - 1, ret);
-
-            region->mmaps[i].mmap = NULL;
+        /*
+         * Align the mmap for more efficient mapping in the kernel.  Ideally
+         * we'd know the PMD and PUD mapping sizes to use as discrete alignment
+         * intervals, but we don't.  As of Linux v6.12, the largest PUD size
+         * supporting huge pfnmap is 1GiB (ARCH_SUPPORTS_PUD_PFNMAP is only set
+         * on x86_64).  Align by power-of-two size, capped at 1GiB.
+         *
+         * NB. qemu_memalign() and friends actually allocate memory, whereas
+         * the region size here can exceed host memory, therefore we manually
+         * create an oversized anonymous mapping and clean it up for alignment.
+         */
+        map_base = mmap(0, region->mmaps[i].size + align, PROT_NONE,
+                        MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+        if (map_base == MAP_FAILED) {
+            ret = -errno;
+            goto no_mmap;
+        }
 
-            for (i--; i >= 0; i--) {
-                vfio_subregion_unmap(region, i);
-            }
+        map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align);
+        munmap(map_base, map_align - map_base);
+        munmap(map_align + region->mmaps[i].size,
+               align - (map_align - map_base));
 
-            return ret;
+        region->mmaps[i].mmap = mmap(map_align, region->mmaps[i].size, prot,
+                                     MAP_SHARED | MAP_FIXED,
+                                     region->vbasedev->fd,
+                                     region->fd_offset +
+                                     region->mmaps[i].offset);
+        if (region->mmaps[i].mmap == MAP_FAILED) {
+            ret = -errno;
+            goto no_mmap;
         }
 
         name = g_strdup_printf("%s mmaps[%d]",
@@ -446,6 +460,20 @@ int vfio_region_mmap(VFIORegion *region)
     }
 
     return 0;
+
+no_mmap:
+    trace_vfio_region_mmap_fault(memory_region_name(region->mem), i,
+                                 region->fd_offset + region->mmaps[i].offset,
+                                 region->fd_offset + region->mmaps[i].offset +
+                                 region->mmaps[i].size - 1, ret);
+
+    region->mmaps[i].mmap = NULL;
+
+    for (i--; i >= 0; i--) {
+        vfio_subregion_unmap(region, i);
+    }
+
+    return ret;
 }
 
 void vfio_region_unmap(VFIORegion *region)