All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/4] vfio-user: support multiple FDs backing region mmaps
@ 2026-10-14 21:10 Naman Gulati
  2026-10-14 21:10 ` [PATCH v5 1/4] vfio-user: add multi-fd region mmap capability Naman Gulati
                   ` (3 more replies)
  0 siblings, 4 replies; 13+ messages in thread
From: Naman Gulati @ 2026-10-14 21:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: John Levon, Thanos Makatos, Cédric Le Goater,
	Alex Williamson, Pierrick Bouvier, Matt Evans, Naman Gulati

This series extends the vfio-user protocol specification to allow
servers to pass multiple file descriptors for different sparse memory
areas to be mmap'ed within a single device region.

This extension is intended for exposing paravirtualized PCI devices via
a vfio-user server that may have multiple unique sub-regions in a BAR
backed by some mmap'able buffers sourced from distinct file descriptors.

This change introduces a new capability:
  VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS

Unlike VFIO_REGION_INFO_CAP_SPARSE_MMAP (which maps all sparse areas
from a single open file descriptor), this new capability defines an
array of sparse mmap areas where each area contains an index into the
array of file descriptors.

When all sparse areas share a single file descriptor, servers may
continue to emit VFIO_REGION_INFO_CAP_SPARSE_MMAP and send exactly one
file descriptor in SCM_RIGHTS for backwards compatibility with older
clients.

The extensions in this series also lays the groundwork to make use of
the in-development series [1] extending vfio-pci to return distinct
dmabuf FDs for discrete regions of a device's BAR. A vfio-user server
may choose to return dmabuf FDs for various regions of a device's BAR to
map a vfio passthrough device into the guest, in order to retain control
of the device and be able to revoke mappings passed to the VM.

libvfio-user server side changes WIP on github [2].


[1] https://lore.kernel.org/all/20260715174737.15287-1-matt@ozlabs.org
[2] https://github.com/NamanGulati/libvfio-user/tree/multi-region-fd

---
Changes from v4:
  - Set .setup_sparse_mmaps = vfio_default_setup_sparse_mmaps in
    vfio_device_io_ops_ioctl. (Cedric)
  - Return -EINVAL in vfio_user_device_io_get_region_info() when
    VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS is present without received
    FDs. (Cedric)
  - Dropped redundant VFIO_USER_MAX_REGIONS bounds checks in
    vfio_user_device_io_get_region_info() and
    vfio_user_device_io_setup_sparse_mmaps(). (Thanos)
  - Validated sparse_fds->nr_areas against info->argsz and switched to
    g_try_new0() for region->mmaps. (Thanos)
  - Included area info in invalid area error message and added comments
    for recv_fds resets in hw/vfio-user/proxy.c. (Thanos)
  - Link to v4: https://lore.kernel.org/qemu-devel/20261009175314.1823347-1-namangulati@google.com

Changes from v3:
  - Added "sparse_mmap_fds" capability to VFIO_USER_VERSION so servers
    know whether the client supports
    VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS. (Thanos)
  - Changed VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS capability ID to 0x8001. (Thanos)
  - Clarified and enforced that VFIO_REGION_INFO_CAP_SPARSE_MMAP and
    VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS are mutually exclusive per region,
    number of FDs in SCM_RIGHTS must not exceed nr_areas, zero-sized areas
    are not permitted, offset + size must not exceed the region size, and
    areas must not overlap. (Thanos)
  - Link to v3: https://lore.kernel.org/qemu-devel/20261007000833.2445641-1-namangulati@google.com

Changes from v2:
  - As suggested by Cedric: kept core VFIO changes minimal by only
    extending VFIOMMmap to hold each mmap subregion's fd & fd_offset.
  - Moved multi-FD management and capability parsing into hw/vfio-user,
    and using VFIOUserProxy to hold region multi-fds.
  - Resend: rebased to head.
  - Link to v2: https://lore.kernel.org/qemu-devel/20260826050039.2068138-1-namangulati@google.com

Changes from RFC:
  - Refactored the get_region_info callback to keep vbasedev constant
    and take in a VFIORegionFDs* out parameter to store the received fds.
  - Link to RFC: https://lore.kernel.org/qemu-devel/20260811233930.401797-1-namangulati@google.com
---

Naman Gulati (4):
  vfio-user: add multi-fd region mmap capability
  vfio: add 'fd' and 'fd_offset' to VFIOMmap
  vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps
  vfio-user: support multiple FDs for sparse mmap regions

 docs/interop/vfio-user.rst    |  86 ++++++++++++++++++++++--
 hw/vfio-user/protocol.h       |  35 ++++++++--
 hw/vfio-user/proxy.h          |   2 +
 hw/vfio/vfio-region.h         |   9 ++-
 include/hw/vfio/vfio-device.h |  16 +++++
 hw/vfio-user/device.c         | 121 ++++++++++++++++++++++++++++++++--
 hw/vfio-user/proxy.c          |  52 +++++++++++++--
 hw/vfio/device.c              |   1 +
 hw/vfio/pci.c                 |   2 +
 hw/vfio/region.c              |  45 ++++++++++---
 10 files changed, 337 insertions(+), 32 deletions(-)

-- 
2.56.0.385.gd3acb90ef8-goog


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v5 1/4] vfio-user: add multi-fd region mmap capability
  2026-10-14 21:10 [PATCH v5 0/4] vfio-user: support multiple FDs backing region mmaps Naman Gulati
@ 2026-10-14 21:10 ` Naman Gulati
  2026-10-20 15:37   ` John Levon
  2026-10-14 21:10 ` [PATCH v5 2/4] vfio: add 'fd' and 'fd_offset' to VFIOMmap Naman Gulati
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 13+ messages in thread
From: Naman Gulati @ 2026-10-14 21:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: John Levon, Thanos Makatos, Cédric Le Goater,
	Alex Williamson, Pierrick Bouvier, Matt Evans, Naman Gulati

Extend the vfio-user protocol specification to allow servers to pass
multiple file descriptors for different sparse memory areas within a
single device region with capability
VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS (0x8001), negotiated via the
"sparse_mmap_fds" capability in VFIO_USER_VERSION.

VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS defines an array of sparse mmap
areas where each area contains:
- offset: Start of the area within the device region (BAR).
- fd_offset: Start of the mapping within the backing file descriptor.
- size: Size of the mappable area.
- fd_index: Zero-based index into the array of file descriptors
  received in the SCM_RIGHTS ancillary data of the
  VFIO_USER_DEVICE_GET_REGION_INFO reply message.

Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
Signed-off-by: Naman Gulati <namangulati@google.com>
---
 docs/interop/vfio-user.rst | 86 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 82 insertions(+), 4 deletions(-)

diff --git a/docs/interop/vfio-user.rst b/docs/interop/vfio-user.rst
index 1c92f442d2..b49781a755 100644
--- a/docs/interop/vfio-user.rst
+++ b/docs/interop/vfio-user.rst
@@ -125,7 +125,13 @@ Sparse Regions
 A region can be memory-mappable in whole or in part. When only a subset of a
 region can be mapped by the client, a ``VFIO_REGION_INFO_CAP_SPARSE_MMAP``
 capability is included in the region info reply. This capability describes
-which portions can be mapped by the client.
+which portions can be mapped by the client. When sparse mmap areas are backed
+by multiple distinct file descriptors, a
+``VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS`` capability is included in the region
+info reply instead. ``VFIO_REGION_INFO_CAP_SPARSE_MMAP`` and
+``VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS`` cannot both be specified for the same
+region; however, different regions of a device may independently use either
+capability.
 
 .. Note::
    For example, in a virtual NVMe controller, sparse regions can be used so
@@ -536,6 +542,10 @@ Capabilities:
 | write_multiple     | boolean | ``VFIO_USER_REGION_WRITE_MULTI`` messages     |
 |                    |         | are supported if the value is ``true``.       |
 +--------------------+---------+-----------------------------------------------+
+| sparse_mmap_fds    | boolean | ``VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS``      |
+|                    |         | capability is supported if the value is       |
+|                    |         | ``true``.                                     |
++--------------------+---------+-----------------------------------------------+
 
 The ``twin_socket`` capability object holds these name/value pairs:
 
@@ -859,9 +869,11 @@ Reply
   * ``VFIO_REGION_INFO_FLAG_READ`` allows client read access to the region.
   * ``VFIO_REGION_INFO_FLAG_WRITE`` allows client write access to the region.
   * ``VFIO_REGION_INFO_FLAG_MMAP`` specifies the client can mmap() the region.
-    When this flag is set, the reply will include a file descriptor in its
-    meta-data. On ``AF_UNIX`` sockets, the file descriptors will be passed as
-    ``SCM_RIGHTS`` type ancillary data.
+    When this flag is set, the reply will include one or more file descriptors
+    in its meta-data (up to the number of sparse mmap areas when
+    ``VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS`` is present, or a single file
+    descriptor otherwise). On ``AF_UNIX`` sockets, the file descriptors will be
+    passed as ``SCM_RIGHTS`` type ancillary data.
   * ``VFIO_REGION_INFO_FLAG_CAPS`` indicates additional capabilities found in the
     reply.
 
@@ -950,6 +962,72 @@ The VFIO sparse mmap area is defined in ``<linux/vfio.h>`` (``struct
 vfio_region_info_cap_sparse_mmap``).
 
 
+VFIO sparse mmap fds cap header
+"""""""""""""""""""""""""""""""
+
++----------------------+--------------------------------------+
+| Name                 | Value                                |
++======================+======================================+
+| id                   | VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS |
++----------------------+--------------------------------------+
+| version              | 0x1                                  |
++----------------------+--------------------------------------+
+| next                 | <next>                               |
++----------------------+--------------------------------------+
+| sparse mmap fds info | VFIO region info sparse mmap fds     |
++----------------------+--------------------------------------+
+
+This capability is defined when a region supports direct access by the client
+via mmap() where different sparse mmap areas are backed by distinct file
+descriptors in the ``SCM_RIGHTS`` ancillary data array. The server may only
+include this capability if the client advertised ``sparse_mmap_fds`` as
+``true`` in ``VFIO_USER_VERSION``. Capability ID
+``VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS`` is defined as ``0x8001`` to avoid
+clashing with kernel VFIO capabilities.
+
+VFIO region info cap sparse mmap fds
+""""""""""""""""""""""""""""""""""""
+
++-----------+--------+------+
+| Name      | Offset | Size |
++===========+========+======+
+| nr_areas  | 0      | 4    |
++-----------+--------+------+
+| reserved  | 4      | 4    |
++-----------+--------+------+
+| offset    | 8      | 8    |
++-----------+--------+------+
+| fd_offset | 16     | 8    |
++-----------+--------+------+
+| size      | 24     | 8    |
++-----------+--------+------+
+| fd_index  | 32     | 4    |
++-----------+--------+------+
+| pad       | 36     | 4    |
++-----------+--------+------+
+| ...       |        |      |
++-----------+--------+------+
+
+* *nr_areas* is the number of sparse mmap areas in the region.
+* *offset*, *fd_offset*, *size*, and *fd_index* describe a single area that can be
+  mapped by the client. There will be *nr_areas* tuples of offset, fd_offset, size,
+  fd_index, and pad. Sparse mmap areas must not overlap.
+* *offset* specifies the start of the area within the device region (BAR).
+* *fd_offset* specifies the start of the mapping within the backing file descriptor
+  referenced by *fd_index*.
+* *size* specifies the size of the area. Zero-sized areas (*size* equal to 0)
+  are not permitted, and *offset* + *size* must not exceed the region *size*.
+* *fd_index* is the zero-based index into the array of file descriptors
+  received in the ``SCM_RIGHTS`` ancillary data of the
+  ``VFIO_USER_DEVICE_GET_REGION_INFO`` reply message. The number of file
+  descriptors sent in ``SCM_RIGHTS`` must be at least 1 and must not exceed
+  *nr_areas*.
+
+If all sparse mmap areas are backed by a single file descriptor, the server
+may instead send ``VFIO_REGION_INFO_CAP_SPARSE_MMAP`` and exactly one file
+descriptor in ``SCM_RIGHTS`` for compatibility with older clients.
+
+
 ``VFIO_USER_DEVICE_GET_REGION_IO_FDS``
 --------------------------------------
 
-- 
2.56.0.385.gd3acb90ef8-goog



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 2/4] vfio: add 'fd' and 'fd_offset' to VFIOMmap
  2026-10-14 21:10 [PATCH v5 0/4] vfio-user: support multiple FDs backing region mmaps Naman Gulati
  2026-10-14 21:10 ` [PATCH v5 1/4] vfio-user: add multi-fd region mmap capability Naman Gulati
@ 2026-10-14 21:10 ` Naman Gulati
  2026-10-15  7:39   ` Cédric Le Goater
  2026-10-20 15:37   ` John Levon
  2026-10-14 21:10 ` [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps Naman Gulati
  2026-10-14 21:10 ` [PATCH v5 4/4] vfio-user: support multiple FDs for sparse mmap regions Naman Gulati
  3 siblings, 2 replies; 13+ messages in thread
From: Naman Gulati @ 2026-10-14 21:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: John Levon, Thanos Makatos, Cédric Le Goater,
	Alex Williamson, Pierrick Bouvier, Matt Evans, Naman Gulati

Allow each sparse mmap subregion to specify its own file descriptor and
file offset for the mmap call.

Add 'fd' (set to -1 when the region fd should be used) and 'fd_offset'
to VFIOMmap, and update vfio_region_mmap() to use them.

Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
Signed-off-by: Naman Gulati <namangulati@google.com>
---
 hw/vfio/vfio-region.h |  4 +++-
 hw/vfio/pci.c         |  2 ++
 hw/vfio/region.c      | 26 +++++++++++++++++++-------
 3 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/hw/vfio/vfio-region.h b/hw/vfio/vfio-region.h
index 58b236f113..9f6a5cf7d5 100644
--- a/hw/vfio/vfio-region.h
+++ b/hw/vfio/vfio-region.h
@@ -14,8 +14,10 @@
 typedef struct VFIOMmap {
     MemoryRegion mem;
     void *mmap;
-    off_t offset;
+    off_t offset;       /* offset within the region */
     size_t size;
+    int fd;             /* per-mmap fd, or -1 to use the region fd */
+    uint64_t fd_offset; /* offset within fd for the mmap call */
 } VFIOMmap;
 
 typedef struct VFIODevice VFIODevice;
diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
index 428ab2f069..f851082114 100644
--- a/hw/vfio/pci.c
+++ b/hw/vfio/pci.c
@@ -1623,6 +1623,7 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
                                   vdev->msix->table_bar, 0, 0);
         } else {
             region->mmaps[0].offset = end;
+            region->mmaps[0].fd_offset += end;
             region->mmaps[0].size = region->size - end;
             trace_vfio_msix_fixup(vdev->vbasedev.name,
                               vdev->msix->table_bar, region->mmaps[0].offset,
@@ -1649,6 +1650,7 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
                               region->mmaps[0].offset + region->mmaps[0].size);
 
         region->mmaps[1].offset = end;
+        region->mmaps[1].fd_offset += end;
         region->mmaps[1].size = region->size - end;
         trace_vfio_msix_fixup(vdev->vbasedev.name,
                               vdev->msix->table_bar, region->mmaps[1].offset,
diff --git a/hw/vfio/region.c b/hw/vfio/region.c
index 54ad11a6c8..16efed2fce 100644
--- a/hw/vfio/region.c
+++ b/hw/vfio/region.c
@@ -189,6 +189,9 @@ static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
                                             sparse->areas[i].size - 1);
             region->mmaps[j].offset = sparse->areas[i].offset;
             region->mmaps[j].size = sparse->areas[i].size;
+            region->mmaps[j].fd = -1;
+            region->mmaps[j].fd_offset = region->fd_offset +
+                                         sparse->areas[i].offset;
             j++;
         }
     }
@@ -262,6 +265,8 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
                 region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
                 region->mmaps[0].offset = 0;
                 region->mmaps[0].size = region->size;
+                region->mmaps[0].fd = -1;
+                region->mmaps[0].fd_offset = region->fd_offset;
             } else if (ret) {
                 return ret;
             }
@@ -354,7 +359,6 @@ int vfio_region_mmap(VFIORegion *region)
     off_t map_offset = 0;
     size_t align;
     char *name;
-    int fd;
 
     if (!region->mem || !region->nr_mmaps) {
         return 0;
@@ -391,8 +395,6 @@ int vfio_region_mmap(VFIORegion *region)
         return ret;
     }
 
-    fd = vfio_device_get_region_fd(region->vbasedev, region->nr);
-
     map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align);
     munmap(map_base, map_align - map_base);
     munmap(map_align + region->size,
@@ -404,12 +406,22 @@ int vfio_region_mmap(VFIORegion *region)
      * offsets being in ascending order.
      */
     for (i = 0; i < region->nr_mmaps; i++) {
+        int fd = region->mmaps[i].fd;
+
+        if (fd < 0) {
+            fd = vfio_device_get_region_fd(region->vbasedev, region->nr);
+        }
+        if (fd < 0) {
+            ret = -EINVAL;
+            munmap(map_align + map_offset, region->size - map_offset);
+            goto no_mmap;
+        }
+
         munmap(map_align + map_offset, region->mmaps[i].offset - map_offset);
         region->mmaps[i].mmap = mmap(map_align + region->mmaps[i].offset,
                                      region->mmaps[i].size, prot,
                                      MAP_SHARED | MAP_FIXED, fd,
-                                     region->fd_offset +
-                                     region->mmaps[i].offset);
+                                     region->mmaps[i].fd_offset);
         if (region->mmaps[i].mmap == MAP_FAILED) {
             ret = -errno;
             /*
@@ -454,8 +466,8 @@ int vfio_region_mmap(VFIORegion *region)
 
 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].fd_offset,
+                                 region->mmaps[i].fd_offset +
                                  region->mmaps[i].size - 1, ret);
 
     region->mmaps[i].mmap = NULL;
-- 
2.56.0.385.gd3acb90ef8-goog



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps
  2026-10-14 21:10 [PATCH v5 0/4] vfio-user: support multiple FDs backing region mmaps Naman Gulati
  2026-10-14 21:10 ` [PATCH v5 1/4] vfio-user: add multi-fd region mmap capability Naman Gulati
  2026-10-14 21:10 ` [PATCH v5 2/4] vfio: add 'fd' and 'fd_offset' to VFIOMmap Naman Gulati
@ 2026-10-14 21:10 ` Naman Gulati
  2026-10-15  7:39   ` Cédric Le Goater
                     ` (2 more replies)
  2026-10-14 21:10 ` [PATCH v5 4/4] vfio-user: support multiple FDs for sparse mmap regions Naman Gulati
  3 siblings, 3 replies; 13+ messages in thread
From: Naman Gulati @ 2026-10-14 21:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: John Levon, Thanos Makatos, Cédric Le Goater,
	Alex Williamson, Pierrick Bouvier, Matt Evans, Naman Gulati

Add a setup_sparse_mmaps callback to VFIODeviceIOOps so transport
backends (such as vfio-user) can parse custom sparse mmap capabilities,
falling back to vfio_default_setup_sparse_mmaps() to process shared
VFIO mmap capabilities.

Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
Signed-off-by: Naman Gulati <namangulati@google.com>
---
 hw/vfio/vfio-region.h         |  5 +++++
 include/hw/vfio/vfio-device.h | 16 ++++++++++++++++
 hw/vfio/device.c              |  1 +
 hw/vfio/region.c              | 19 ++++++++++++++++---
 4 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/hw/vfio/vfio-region.h b/hw/vfio/vfio-region.h
index 9f6a5cf7d5..a0c9533f9f 100644
--- a/hw/vfio/vfio-region.h
+++ b/hw/vfio/vfio-region.h
@@ -46,4 +46,9 @@ void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
 void vfio_region_exit(VFIORegion *region);
 void vfio_region_finalize(VFIORegion *region);
 
+struct vfio_region_info;
+int vfio_default_setup_sparse_mmaps(VFIORegion *region,
+                                    struct vfio_region_info *info,
+                                    Error **errp);
+
 #endif /* HW_VFIO_REGION_H */
diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h
index 8472420d3f..3136e152f2 100644
--- a/include/hw/vfio/vfio-device.h
+++ b/include/hw/vfio/vfio-device.h
@@ -43,6 +43,7 @@ enum {
 
 typedef struct VFIODeviceOps VFIODeviceOps;
 typedef struct VFIODeviceIOOps VFIODeviceIOOps;
+typedef struct VFIORegion VFIORegion;
 typedef struct VFIOMigration VFIOMigration;
 
 typedef struct IOMMUFDBackend IOMMUFDBackend;
@@ -218,6 +219,21 @@ struct VFIODeviceIOOps {
     int (*get_region_info)(VFIODevice *vdev,
                            struct vfio_region_info *info, int *fd);
 
+    /**
+     * @setup_sparse_mmaps
+     *
+     * Parse sparse mmap capabilities for a region and initialize region->mmaps.
+     *
+     * @region: #VFIORegion to set up
+     * @info: region info struct containing capabilities
+     * @errp: pointer to Error*, to store an error if it happens
+     *
+     * Returns 0 on success, -ENODEV if not sparse, or negative error code.
+     */
+    int (*setup_sparse_mmaps)(VFIORegion *region,
+                              struct vfio_region_info *info,
+                              Error **errp);
+
     /**
      * @get_irq_info
      *
diff --git a/hw/vfio/device.c b/hw/vfio/device.c
index 4f11959633..94d6e7ce09 100644
--- a/hw/vfio/device.c
+++ b/hw/vfio/device.c
@@ -662,6 +662,7 @@ static VFIODeviceIOOps vfio_device_io_ops_ioctl = {
 
     .device_feature = vfio_device_io_device_feature,
     .get_region_info = vfio_device_io_get_region_info,
+    .setup_sparse_mmaps = vfio_default_setup_sparse_mmaps,
     .get_irq_info = vfio_device_io_get_irq_info,
     .set_irqs = vfio_device_io_set_irqs,
     .region_read = vfio_device_io_region_read,
diff --git a/hw/vfio/region.c b/hw/vfio/region.c
index 16efed2fce..94cfb708bf 100644
--- a/hw/vfio/region.c
+++ b/hw/vfio/region.c
@@ -162,9 +162,9 @@ static int vfio_mmap_compare_offset(const void *a, const void *b)
     return 0;
 }
 
-static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
-                                          struct vfio_region_info *info,
-                                          Error **errp)
+int vfio_default_setup_sparse_mmaps(VFIORegion *region,
+                                    struct vfio_region_info *info,
+                                    Error **errp)
 {
     struct vfio_info_cap_header *hdr;
     struct vfio_region_info_cap_sparse_mmap *sparse;
@@ -198,6 +198,19 @@ static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
 
     region->nr_mmaps = j;
     region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap));
+    return 0;
+}
+
+static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
+                                          struct vfio_region_info *info,
+                                          Error **errp)
+{
+    int ret, i;
+
+    ret = region->vbasedev->io_ops->setup_sparse_mmaps(region, info, errp);
+    if (ret) {
+        return ret;
+    }
 
     /*
      * Sort sparse mmaps by offset to ensure proper handling of gaps
-- 
2.56.0.385.gd3acb90ef8-goog



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH v5 4/4] vfio-user: support multiple FDs for sparse mmap regions
  2026-10-14 21:10 [PATCH v5 0/4] vfio-user: support multiple FDs backing region mmaps Naman Gulati
                   ` (2 preceding siblings ...)
  2026-10-14 21:10 ` [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps Naman Gulati
@ 2026-10-14 21:10 ` Naman Gulati
  2026-10-15  7:45   ` Cédric Le Goater
  2026-10-20 15:35   ` John Levon
  3 siblings, 2 replies; 13+ messages in thread
From: Naman Gulati @ 2026-10-14 21:10 UTC (permalink / raw)
  To: qemu-devel
  Cc: John Levon, Thanos Makatos, Cédric Le Goater,
	Alex Williamson, Pierrick Bouvier, Matt Evans, Naman Gulati

Add client support for capability VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS
(capability ID 0x8001) in vfio-user, and advertise support via the
"sparse_mmap_fds" capability in VFIO_USER_VERSION.

Update vfio_user_device_io_get_region_info() to receive multiple file
descriptors passed in SCM_RIGHTS ancillary data for a region, store them
in VFIOUserProxy, and implement setup_sparse_mmaps in
vfio_user_device_io_ops_sock to validate and populate per-area file
descriptors and fd_offset values.

Signed-off-by: Naman Gulati <namangulati@google.com>
---
 hw/vfio-user/protocol.h |  35 ++++++++++--
 hw/vfio-user/proxy.h    |   2 +
 hw/vfio-user/device.c   | 121 +++++++++++++++++++++++++++++++++++++---
 hw/vfio-user/proxy.c    |  52 +++++++++++++++--
 4 files changed, 193 insertions(+), 17 deletions(-)

diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h
index c478d1353f..b823689254 100644
--- a/hw/vfio-user/protocol.h
+++ b/hw/vfio-user/protocol.h
@@ -15,6 +15,8 @@
  * SPDX-License-Identifier: GPL-2.0-or-later
  */
 
+#define VFIO_USER_MAX_REGIONS 100
+
 typedef struct {
     uint16_t id;
     uint16_t command;
@@ -69,12 +71,13 @@ typedef struct {
 #define VFIO_USER_CAP           "capabilities"
 
 /* "capabilities" members */
-#define VFIO_USER_CAP_MAX_FDS   "max_msg_fds"
-#define VFIO_USER_CAP_MAX_XFER  "max_data_xfer_size"
-#define VFIO_USER_CAP_PGSIZES   "pgsizes"
-#define VFIO_USER_CAP_MAP_MAX   "max_dma_maps"
-#define VFIO_USER_CAP_MIGR      "migration"
-#define VFIO_USER_CAP_MULTI     "write_multiple"
+#define VFIO_USER_CAP_MAX_FDS           "max_msg_fds"
+#define VFIO_USER_CAP_MAX_XFER          "max_data_xfer_size"
+#define VFIO_USER_CAP_PGSIZES           "pgsizes"
+#define VFIO_USER_CAP_MAP_MAX           "max_dma_maps"
+#define VFIO_USER_CAP_MIGR              "migration"
+#define VFIO_USER_CAP_MULTI             "write_multiple"
+#define VFIO_USER_CAP_SPARSE_MMAP_FDS   "sparse_mmap_fds"
 
 /* "migration" members */
 #define VFIO_USER_CAP_PGSIZE            "pgsize"
@@ -166,6 +169,26 @@ typedef struct {
     uint64_t offset;
 } VFIOUserRegionInfo;
 
+/*
+ * VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS
+ */
+#define VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS 0x8001
+
+struct vfio_region_sparse_mmap_fd_area {
+    uint64_t offset;
+    uint64_t fd_offset;
+    uint64_t size;
+    uint32_t fd_index;
+    uint32_t pad;
+};
+
+struct vfio_region_info_cap_sparse_mmap_fds {
+    struct vfio_info_cap_header header;
+    uint32_t nr_areas;
+    uint32_t reserved;
+    struct vfio_region_sparse_mmap_fd_area areas[];
+};
+
 /*
  * VFIO_USER_DEVICE_GET_IRQ_INFO
  * imported from struct vfio_irq_info
diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h
index 7b97460cc5..4b9834a436 100644
--- a/hw/vfio-user/proxy.h
+++ b/hw/vfio-user/proxy.h
@@ -72,6 +72,7 @@ typedef struct VFIOUserProxy {
     AioContext *ctx;
     QEMUBH *req_bh;
     bool async_ops;
+    VFIOUserFDs *region_fds[VFIO_USER_MAX_REGIONS];
 
     /*
      * above only changed when BQL is held
@@ -111,6 +112,7 @@ bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp);
 
 VFIOUserFDs *vfio_user_getfds(int numfds);
 void vfio_user_putfds(VFIOUserMsg *msg);
+void vfio_user_free_fds(VFIOUserFDs *fds);
 
 void vfio_user_disable_posted_writes(VFIOUserProxy *proxy);
 
diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c
index 1a01e748cd..4fbf536a04 100644
--- a/hw/vfio-user/device.c
+++ b/hw/vfio-user/device.c
@@ -14,12 +14,14 @@
 
 #include "hw/vfio-user/device.h"
 #include "hw/vfio-user/trace.h"
+#include "hw/vfio/vfio-region.h"
+#include "hw/vfio/vfio-helpers.h"
+#include "hw/vfio/trace.h"
 
 /*
  * These are to defend against a malign server trying
  * to force us to run out of memory.
  */
-#define VFIO_USER_MAX_REGIONS   100
 #define VFIO_USER_MAX_IRQS      50
 
 bool vfio_user_get_device_info(VFIOUserProxy *proxy,
@@ -209,14 +211,17 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
                                                struct vfio_region_info *info,
                                                int *fd)
 {
-    VFIOUserFDs fds = { 0, 1, fd};
-    int ret;
+    int fds[VFIO_USER_MAX_MAX_FDS];
+    VFIOUserFDs user_fds = { 0, VFIO_USER_MAX_MAX_FDS, fds };
+    int i, ret;
+
+    *fd = -1;
 
     if (info->index > vbasedev->num_initial_regions) {
         return -EINVAL;
     }
 
-    ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds);
+    ret = vfio_user_get_region_info(vbasedev->proxy, info, &user_fds);
     if (ret) {
         return ret;
     }
@@ -225,10 +230,30 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
     if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) &&
         (info->cap_offset < sizeof(*info)
          || info->cap_offset + sizeof(struct vfio_info_cap_header) > info->argsz)) {
-        return -EINVAL;
+        ret = -EINVAL;
+    } else if (vfio_get_region_info_cap(info,
+                                        VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS)) {
+        if (user_fds.recv_fds > 0) {
+            VFIOUserFDs *saved = vfio_user_getfds(user_fds.recv_fds);
+
+            saved->recv_fds = user_fds.recv_fds;
+            memcpy(saved->fds, fds, user_fds.recv_fds * sizeof(int));
+            vfio_user_free_fds(vbasedev->proxy->region_fds[info->index]);
+            vbasedev->proxy->region_fds[info->index] = saved;
+            return 0;
+        }
+        ret = -EINVAL;
+    } else if (user_fds.recv_fds > 0) {
+        *fd = fds[0];
+        fds[0] = -1;
     }
 
-    return 0;
+    for (i = 0; i < user_fds.recv_fds; i++) {
+        if (fds[i] >= 0) {
+            close(fds[i]);
+        }
+    }
+    return ret;
 }
 
 static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev,
@@ -518,12 +543,94 @@ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index,
 /*
  * Socket-based io_ops
  */
+static int vfio_user_device_io_setup_sparse_mmaps(VFIORegion *region,
+                                                  struct vfio_region_info *info,
+                                                  Error **errp)
+{
+    struct vfio_info_cap_header *hdr;
+    struct vfio_region_info_cap_sparse_mmap_fds *sparse_fds;
+    VFIOUserFDs *user_fds;
+    size_t cap_offset;
+    int i;
+
+    hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS);
+    if (!hdr) {
+        return vfio_default_setup_sparse_mmaps(region, info, errp);
+    }
+
+    if (vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP)) {
+        error_setg(errp, "%s: region %d cannot specify both "
+                   "SPARSE_MMAP and SPARSE_MMAP_FDS", __func__, region->nr);
+        return -EINVAL;
+    }
+
+    sparse_fds = container_of(hdr, struct vfio_region_info_cap_sparse_mmap_fds,
+                              header);
+
+    cap_offset = (uintptr_t)hdr - (uintptr_t)info;
+    if (cap_offset > info->argsz ||
+        info->argsz - cap_offset < sizeof(*sparse_fds) ||
+        sparse_fds->nr_areas >
+            (info->argsz - cap_offset - sizeof(*sparse_fds)) /
+            sizeof(sparse_fds->areas[0])) {
+        error_setg(errp, "%s: invalid nr_areas %u in region %d",
+                   __func__, sparse_fds->nr_areas, region->nr);
+        return -EINVAL;
+    }
+
+    trace_vfio_region_sparse_mmap_header(region->vbasedev->name,
+                                         region->nr, sparse_fds->nr_areas);
+
+    user_fds = region->vbasedev->proxy->region_fds[region->nr];
+    if (!user_fds || user_fds->recv_fds > sparse_fds->nr_areas) {
+        error_setg(errp, "%s: invalid fd count %d for %u sparse mmap areas "
+                   "in region %d", __func__,
+                   user_fds ? user_fds->recv_fds : 0,
+                   sparse_fds->nr_areas, region->nr);
+        return -EINVAL;
+    }
+
+    region->mmaps = g_try_new0(VFIOMmap, sparse_fds->nr_areas);
+    if (!region->mmaps) {
+        error_setg(errp, "%s: failed to allocate %u mmaps for region %d",
+                   __func__, sparse_fds->nr_areas, region->nr);
+        return -ENOMEM;
+    }
+
+    for (i = 0; i < sparse_fds->nr_areas; i++) {
+        struct vfio_region_sparse_mmap_fd_area *area = &sparse_fds->areas[i];
+
+        if (area->size == 0 || area->offset > region->size ||
+            area->size > region->size - area->offset ||
+            area->fd_index >= user_fds->recv_fds) {
+            error_setg(errp, "%s: invalid sparse mmap area %d in region %d "
+                       "(offset=0x%"PRIx64" size=0x%"PRIx64
+                       " fd_offset=0x%"PRIx64" fd_index=%u)",
+                       __func__, i, region->nr, area->offset, area->size,
+                       area->fd_offset, area->fd_index);
+            g_free(region->mmaps);
+            region->mmaps = NULL;
+            return -EINVAL;
+        }
+
+        trace_vfio_region_sparse_mmap_entry(i, area->offset,
+                                            area->offset + area->size - 1);
+        region->mmaps[i].offset = area->offset;
+        region->mmaps[i].fd_offset = area->fd_offset;
+        region->mmaps[i].size = area->size;
+        region->mmaps[i].fd = user_fds->fds[area->fd_index];
+    }
+
+    region->nr_mmaps = sparse_fds->nr_areas;
+    return 0;
+}
+
 VFIODeviceIOOps vfio_user_device_io_ops_sock = {
     .device_feature = vfio_user_device_io_device_feature,
     .get_region_info = vfio_user_device_io_get_region_info,
+    .setup_sparse_mmaps = vfio_user_device_io_setup_sparse_mmaps,
     .get_irq_info = vfio_user_device_io_get_irq_info,
     .set_irqs = vfio_user_device_io_set_irqs,
     .region_read = vfio_user_device_io_region_read,
     .region_write = vfio_user_device_io_region_write,
-
 };
diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c
index 197aee07bf..ba29085321 100644
--- a/hw/vfio-user/proxy.c
+++ b/hw/vfio-user/proxy.c
@@ -363,6 +363,13 @@ static int vfio_user_recv_one(VFIOUserProxy *proxy, Error **errp)
         }
 
         memcpy(msg->fds->fds, fdp, numfds * sizeof(int));
+    } else if (msg->fds != NULL) {
+        /*
+         * recv_fds holds the capacity of msg->fds->fds on entry; reset it
+         * to 0 when the reply carries no FDs so the caller knows none were
+         * received.
+         */
+        msg->fds->recv_fds = 0;
     }
 
     /*
@@ -740,6 +747,11 @@ bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr,
 
     qemu_mutex_unlock(&proxy->lock);
 
+    if (!ok && fds != NULL) {
+        /* No reply was received; report 0 received FDs to the caller. */
+        fds->recv_fds = 0;
+    }
+
     return ok;
 }
 
@@ -867,17 +879,30 @@ void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error)
 }
 
 /*
- * Close FDs erroneously received in an incoming request.
+ * Close and free received FDs.
  */
-void vfio_user_putfds(VFIOUserMsg *msg)
+void vfio_user_free_fds(VFIOUserFDs *fds)
 {
-    VFIOUserFDs *fds = msg->fds;
     int i;
 
+    if (!fds) {
+        return;
+    }
+
     for (i = 0; i < fds->recv_fds; i++) {
-        close(fds->fds[i]);
+        if (fds->fds[i] >= 0) {
+            close(fds->fds[i]);
+        }
     }
     g_free(fds);
+}
+
+/*
+ * Close FDs erroneously received in an incoming request.
+ */
+void vfio_user_putfds(VFIOUserMsg *msg)
+{
+    vfio_user_free_fds(msg->fds);
     msg->fds = NULL;
 }
 
@@ -967,6 +992,7 @@ void vfio_user_set_handler(VFIODevice *vbasedev,
 void vfio_user_disconnect(VFIOUserProxy *proxy)
 {
     VFIOUserMsg *r1, *r2;
+    int i;
 
     qemu_mutex_lock(&proxy->lock);
 
@@ -1027,6 +1053,10 @@ void vfio_user_disconnect(VFIOUserProxy *proxy)
         vfio_user_iothread = NULL;
     }
 
+    for (i = 0; i < VFIO_USER_MAX_REGIONS; i++) {
+        vfio_user_free_fds(proxy->region_fds[i]);
+    }
+
     g_free(proxy->sockname);
     g_free(proxy);
 }
@@ -1214,6 +1244,18 @@ static bool check_multi(VFIOUserProxy *proxy, QObject *qobj, Error **errp)
     return true;
 }
 
+static bool check_sparse_mmap_fds(VFIOUserProxy *proxy, QObject *qobj,
+                                  Error **errp)
+{
+    QBool *qb = qobject_to(QBool, qobj);
+
+    if (qb == NULL) {
+        error_setg(errp, "malformed %s", VFIO_USER_CAP_SPARSE_MMAP_FDS);
+        return false;
+    }
+    return true;
+}
+
 static struct cap_entry caps_cap[] = {
     { VFIO_USER_CAP_MAX_FDS, check_max_fds },
     { VFIO_USER_CAP_MAX_XFER, check_max_xfer },
@@ -1221,6 +1263,7 @@ static struct cap_entry caps_cap[] = {
     { VFIO_USER_CAP_MAP_MAX, check_max_dma },
     { VFIO_USER_CAP_MIGR, check_migr },
     { VFIO_USER_CAP_MULTI, check_multi },
+    { VFIO_USER_CAP_SPARSE_MMAP_FDS, check_sparse_mmap_fds },
     { NULL }
 };
 
@@ -1280,6 +1323,7 @@ static GString *caps_json(void)
     qdict_put_int(capdict, VFIO_USER_CAP_PGSIZES, VFIO_USER_DEF_PGSIZE);
     qdict_put_int(capdict, VFIO_USER_CAP_MAP_MAX, VFIO_USER_DEF_MAP_MAX);
     qdict_put_bool(capdict, VFIO_USER_CAP_MULTI, true);
+    qdict_put_bool(capdict, VFIO_USER_CAP_SPARSE_MMAP_FDS, true);
 
     qdict_put_obj(dict, VFIO_USER_CAP, QOBJECT(capdict));
 
-- 
2.56.0.385.gd3acb90ef8-goog



^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 2/4] vfio: add 'fd' and 'fd_offset' to VFIOMmap
  2026-10-14 21:10 ` [PATCH v5 2/4] vfio: add 'fd' and 'fd_offset' to VFIOMmap Naman Gulati
@ 2026-10-15  7:39   ` Cédric Le Goater
  2026-10-20 15:37   ` John Levon
  1 sibling, 0 replies; 13+ messages in thread
From: Cédric Le Goater @ 2026-10-15  7:39 UTC (permalink / raw)
  To: Naman Gulati, qemu-devel
  Cc: John Levon, Thanos Makatos, Alex Williamson, Pierrick Bouvier,
	Matt Evans

On 10/14/26 23:10, Naman Gulati wrote:
> Allow each sparse mmap subregion to specify its own file descriptor and
> file offset for the mmap call.
> 
> Add 'fd' (set to -1 when the region fd should be used) and 'fd_offset'
> to VFIOMmap, and update vfio_region_mmap() to use them.
> 
> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
> Signed-off-by: Naman Gulati <namangulati@google.com>
> ---
>   hw/vfio/vfio-region.h |  4 +++-
>   hw/vfio/pci.c         |  2 ++
>   hw/vfio/region.c      | 26 +++++++++++++++++++-------
>   3 files changed, 24 insertions(+), 8 deletions(-)
> 
> diff --git a/hw/vfio/vfio-region.h b/hw/vfio/vfio-region.h
> index 58b236f113..9f6a5cf7d5 100644
> --- a/hw/vfio/vfio-region.h
> +++ b/hw/vfio/vfio-region.h
> @@ -14,8 +14,10 @@
>   typedef struct VFIOMmap {
>       MemoryRegion mem;
>       void *mmap;
> -    off_t offset;
> +    off_t offset;       /* offset within the region */
>       size_t size;
> +    int fd;             /* per-mmap fd, or -1 to use the region fd */
> +    uint64_t fd_offset; /* offset within fd for the mmap call */
>   } VFIOMmap;
>   
>   typedef struct VFIODevice VFIODevice;
> diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c
> index 428ab2f069..f851082114 100644
> --- a/hw/vfio/pci.c
> +++ b/hw/vfio/pci.c
> @@ -1623,6 +1623,7 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
>                                     vdev->msix->table_bar, 0, 0);
>           } else {
>               region->mmaps[0].offset = end;
> +            region->mmaps[0].fd_offset += end;
>               region->mmaps[0].size = region->size - end;
>               trace_vfio_msix_fixup(vdev->vbasedev.name,
>                                 vdev->msix->table_bar, region->mmaps[0].offset,
> @@ -1649,6 +1650,7 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev)
>                                 region->mmaps[0].offset + region->mmaps[0].size);
>   
>           region->mmaps[1].offset = end;
> +        region->mmaps[1].fd_offset += end;
>           region->mmaps[1].size = region->size - end;
>           trace_vfio_msix_fixup(vdev->vbasedev.name,
>                                 vdev->msix->table_bar, region->mmaps[1].offset,
> diff --git a/hw/vfio/region.c b/hw/vfio/region.c
> index 54ad11a6c8..16efed2fce 100644
> --- a/hw/vfio/region.c
> +++ b/hw/vfio/region.c
> @@ -189,6 +189,9 @@ static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
>                                               sparse->areas[i].size - 1);
>               region->mmaps[j].offset = sparse->areas[i].offset;
>               region->mmaps[j].size = sparse->areas[i].size;
> +            region->mmaps[j].fd = -1;
> +            region->mmaps[j].fd_offset = region->fd_offset +
> +                                         sparse->areas[i].offset;
>               j++;
>           }
>       }
> @@ -262,6 +265,8 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region,
>                   region->mmaps = g_new0(VFIOMmap, region->nr_mmaps);
>                   region->mmaps[0].offset = 0;
>                   region->mmaps[0].size = region->size;
> +                region->mmaps[0].fd = -1;
> +                region->mmaps[0].fd_offset = region->fd_offset;
>               } else if (ret) {
>                   return ret;
>               }
> @@ -354,7 +359,6 @@ int vfio_region_mmap(VFIORegion *region)
>       off_t map_offset = 0;
>       size_t align;
>       char *name;
> -    int fd;
>   
>       if (!region->mem || !region->nr_mmaps) {
>           return 0;
> @@ -391,8 +395,6 @@ int vfio_region_mmap(VFIORegion *region)
>           return ret;
>       }
>   
> -    fd = vfio_device_get_region_fd(region->vbasedev, region->nr);
> -
>       map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align);
>       munmap(map_base, map_align - map_base);
>       munmap(map_align + region->size,
> @@ -404,12 +406,22 @@ int vfio_region_mmap(VFIORegion *region)
>        * offsets being in ascending order.
>        */
>       for (i = 0; i < region->nr_mmaps; i++) {
> +        int fd = region->mmaps[i].fd;
> +
> +        if (fd < 0) {
> +            fd = vfio_device_get_region_fd(region->vbasedev, region->nr);
> +        }
> +        if (fd < 0) {
> +            ret = -EINVAL;
> +            munmap(map_align + map_offset, region->size - map_offset);
> +            goto no_mmap;
> +        }
> +
>           munmap(map_align + map_offset, region->mmaps[i].offset - map_offset);
>           region->mmaps[i].mmap = mmap(map_align + region->mmaps[i].offset,
>                                        region->mmaps[i].size, prot,
>                                        MAP_SHARED | MAP_FIXED, fd,
> -                                     region->fd_offset +
> -                                     region->mmaps[i].offset);
> +                                     region->mmaps[i].fd_offset);
>           if (region->mmaps[i].mmap == MAP_FAILED) {
>               ret = -errno;
>               /*
> @@ -454,8 +466,8 @@ int vfio_region_mmap(VFIORegion *region)
>   
>   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].fd_offset,
> +                                 region->mmaps[i].fd_offset +
>                                    region->mmaps[i].size - 1, ret);
>   
>       region->mmaps[i].mmap = NULL;

Reviewed-by: Cédric Le Goater <clg@redhat.com>

Thanks,

C.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps
  2026-10-14 21:10 ` [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps Naman Gulati
@ 2026-10-15  7:39   ` Cédric Le Goater
  2026-10-15  7:41   ` Cédric Le Goater
  2026-10-20 15:38   ` John Levon
  2 siblings, 0 replies; 13+ messages in thread
From: Cédric Le Goater @ 2026-10-15  7:39 UTC (permalink / raw)
  To: Naman Gulati, qemu-devel
  Cc: John Levon, Thanos Makatos, Alex Williamson, Pierrick Bouvier,
	Matt Evans

On 10/14/26 23:10, Naman Gulati wrote:
> Add a setup_sparse_mmaps callback to VFIODeviceIOOps so transport
> backends (such as vfio-user) can parse custom sparse mmap capabilities,
> falling back to vfio_default_setup_sparse_mmaps() to process shared
> VFIO mmap capabilities.
> 
> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
> Signed-off-by: Naman Gulati <namangulati@google.com>
> ---
>   hw/vfio/vfio-region.h         |  5 +++++
>   include/hw/vfio/vfio-device.h | 16 ++++++++++++++++
>   hw/vfio/device.c              |  1 +
>   hw/vfio/region.c              | 19 ++++++++++++++++---
>   4 files changed, 38 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/vfio/vfio-region.h b/hw/vfio/vfio-region.h
> index 9f6a5cf7d5..a0c9533f9f 100644
> --- a/hw/vfio/vfio-region.h
> +++ b/hw/vfio/vfio-region.h
> @@ -46,4 +46,9 @@ void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
>   void vfio_region_exit(VFIORegion *region);
>   void vfio_region_finalize(VFIORegion *region);
>   
> +struct vfio_region_info;
> +int vfio_default_setup_sparse_mmaps(VFIORegion *region,
> +                                    struct vfio_region_info *info,
> +                                    Error **errp);
> +
>   #endif /* HW_VFIO_REGION_H */
> diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h
> index 8472420d3f..3136e152f2 100644
> --- a/include/hw/vfio/vfio-device.h
> +++ b/include/hw/vfio/vfio-device.h
> @@ -43,6 +43,7 @@ enum {
>   
>   typedef struct VFIODeviceOps VFIODeviceOps;
>   typedef struct VFIODeviceIOOps VFIODeviceIOOps;
> +typedef struct VFIORegion VFIORegion;
>   typedef struct VFIOMigration VFIOMigration;
>   
>   typedef struct IOMMUFDBackend IOMMUFDBackend;
> @@ -218,6 +219,21 @@ struct VFIODeviceIOOps {
>       int (*get_region_info)(VFIODevice *vdev,
>                              struct vfio_region_info *info, int *fd);
>   
> +    /**
> +     * @setup_sparse_mmaps
> +     *
> +     * Parse sparse mmap capabilities for a region and initialize region->mmaps.
> +     *
> +     * @region: #VFIORegion to set up
> +     * @info: region info struct containing capabilities
> +     * @errp: pointer to Error*, to store an error if it happens
> +     *
> +     * Returns 0 on success, -ENODEV if not sparse, or negative error code.
> +     */
> +    int (*setup_sparse_mmaps)(VFIORegion *region,
> +                              struct vfio_region_info *info,
> +                              Error **errp);
> +
>       /**
>        * @get_irq_info
>        *
> diff --git a/hw/vfio/device.c b/hw/vfio/device.c
> index 4f11959633..94d6e7ce09 100644
> --- a/hw/vfio/device.c
> +++ b/hw/vfio/device.c
> @@ -662,6 +662,7 @@ static VFIODeviceIOOps vfio_device_io_ops_ioctl = {
>   
>       .device_feature = vfio_device_io_device_feature,
>       .get_region_info = vfio_device_io_get_region_info,
> +    .setup_sparse_mmaps = vfio_default_setup_sparse_mmaps,
>       .get_irq_info = vfio_device_io_get_irq_info,
>       .set_irqs = vfio_device_io_set_irqs,
>       .region_read = vfio_device_io_region_read,
> diff --git a/hw/vfio/region.c b/hw/vfio/region.c
> index 16efed2fce..94cfb708bf 100644
> --- a/hw/vfio/region.c
> +++ b/hw/vfio/region.c
> @@ -162,9 +162,9 @@ static int vfio_mmap_compare_offset(const void *a, const void *b)
>       return 0;
>   }
>   
> -static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
> -                                          struct vfio_region_info *info,
> -                                          Error **errp)
> +int vfio_default_setup_sparse_mmaps(VFIORegion *region,
> +                                    struct vfio_region_info *info,
> +                                    Error **errp)
>   {
>       struct vfio_info_cap_header *hdr;
>       struct vfio_region_info_cap_sparse_mmap *sparse;
> @@ -198,6 +198,19 @@ static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
>   
>       region->nr_mmaps = j;
>       region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap));
> +    return 0;
> +}
> +
> +static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
> +                                          struct vfio_region_info *info,
> +                                          Error **errp)
> +{
> +    int ret, i;
> +
> +    ret = region->vbasedev->io_ops->setup_sparse_mmaps(region, info, errp);
> +    if (ret) {
> +        return ret;
> +    }
>   
>       /*
>        * Sort sparse mmaps by offset to ensure proper handling of gaps

Reviewed-by: Cédric Le Goater <clg@redhat.com>

Thanks,

C.



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps
  2026-10-14 21:10 ` [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps Naman Gulati
  2026-10-15  7:39   ` Cédric Le Goater
@ 2026-10-15  7:41   ` Cédric Le Goater
  2026-10-20 15:38   ` John Levon
  2 siblings, 0 replies; 13+ messages in thread
From: Cédric Le Goater @ 2026-10-15  7:41 UTC (permalink / raw)
  To: Naman Gulati, qemu-devel
  Cc: John Levon, Thanos Makatos, Alex Williamson, Pierrick Bouvier,
	Matt Evans

On 10/14/26 23:10, Naman Gulati wrote:
> Add a setup_sparse_mmaps callback to VFIODeviceIOOps so transport
> backends (such as vfio-user) can parse custom sparse mmap capabilities,
> falling back to vfio_default_setup_sparse_mmaps() to process shared
> VFIO mmap capabilities.
> 
> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
> Signed-off-by: Naman Gulati <namangulati@google.com>
> ---
>   hw/vfio/vfio-region.h         |  5 +++++
>   include/hw/vfio/vfio-device.h | 16 ++++++++++++++++
>   hw/vfio/device.c              |  1 +
>   hw/vfio/region.c              | 19 ++++++++++++++++---
>   4 files changed, 38 insertions(+), 3 deletions(-)
> 
> diff --git a/hw/vfio/vfio-region.h b/hw/vfio/vfio-region.h
> index 9f6a5cf7d5..a0c9533f9f 100644
> --- a/hw/vfio/vfio-region.h
> +++ b/hw/vfio/vfio-region.h
> @@ -46,4 +46,9 @@ void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled);
>   void vfio_region_exit(VFIORegion *region);
>   void vfio_region_finalize(VFIORegion *region);
>   
> +struct vfio_region_info;
> +int vfio_default_setup_sparse_mmaps(VFIORegion *region,
> +                                    struct vfio_region_info *info,
> +                                    Error **errp);


If you resend, I'd prefer this routine to be named :

	vfio_region_setup_sparse_mmaps()

Thanks,

C.

> +
>   #endif /* HW_VFIO_REGION_H */
> diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h
> index 8472420d3f..3136e152f2 100644
> --- a/include/hw/vfio/vfio-device.h
> +++ b/include/hw/vfio/vfio-device.h
> @@ -43,6 +43,7 @@ enum {
>   
>   typedef struct VFIODeviceOps VFIODeviceOps;
>   typedef struct VFIODeviceIOOps VFIODeviceIOOps;
> +typedef struct VFIORegion VFIORegion;
>   typedef struct VFIOMigration VFIOMigration;
>   
>   typedef struct IOMMUFDBackend IOMMUFDBackend;
> @@ -218,6 +219,21 @@ struct VFIODeviceIOOps {
>       int (*get_region_info)(VFIODevice *vdev,
>                              struct vfio_region_info *info, int *fd);
>   
> +    /**
> +     * @setup_sparse_mmaps
> +     *
> +     * Parse sparse mmap capabilities for a region and initialize region->mmaps.
> +     *
> +     * @region: #VFIORegion to set up
> +     * @info: region info struct containing capabilities
> +     * @errp: pointer to Error*, to store an error if it happens
> +     *
> +     * Returns 0 on success, -ENODEV if not sparse, or negative error code.
> +     */
> +    int (*setup_sparse_mmaps)(VFIORegion *region,
> +                              struct vfio_region_info *info,
> +                              Error **errp);
> +
>       /**
>        * @get_irq_info
>        *
> diff --git a/hw/vfio/device.c b/hw/vfio/device.c
> index 4f11959633..94d6e7ce09 100644
> --- a/hw/vfio/device.c
> +++ b/hw/vfio/device.c
> @@ -662,6 +662,7 @@ static VFIODeviceIOOps vfio_device_io_ops_ioctl = {
>   
>       .device_feature = vfio_device_io_device_feature,
>       .get_region_info = vfio_device_io_get_region_info,
> +    .setup_sparse_mmaps = vfio_default_setup_sparse_mmaps,
>       .get_irq_info = vfio_device_io_get_irq_info,
>       .set_irqs = vfio_device_io_set_irqs,
>       .region_read = vfio_device_io_region_read,
> diff --git a/hw/vfio/region.c b/hw/vfio/region.c
> index 16efed2fce..94cfb708bf 100644
> --- a/hw/vfio/region.c
> +++ b/hw/vfio/region.c
> @@ -162,9 +162,9 @@ static int vfio_mmap_compare_offset(const void *a, const void *b)
>       return 0;
>   }
>   
> -static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
> -                                          struct vfio_region_info *info,
> -                                          Error **errp)
> +int vfio_default_setup_sparse_mmaps(VFIORegion *region,
> +                                    struct vfio_region_info *info,
> +                                    Error **errp)
>   {
>       struct vfio_info_cap_header *hdr;
>       struct vfio_region_info_cap_sparse_mmap *sparse;
> @@ -198,6 +198,19 @@ static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
>   
>       region->nr_mmaps = j;
>       region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap));
> +    return 0;
> +}
> +
> +static int vfio_setup_region_sparse_mmaps(VFIORegion *region,
> +                                          struct vfio_region_info *info,
> +                                          Error **errp)
> +{
> +    int ret, i;
> +
> +    ret = region->vbasedev->io_ops->setup_sparse_mmaps(region, info, errp);
> +    if (ret) {
> +        return ret;
> +    }
>   
>       /*
>        * Sort sparse mmaps by offset to ensure proper handling of gaps



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 4/4] vfio-user: support multiple FDs for sparse mmap regions
  2026-10-14 21:10 ` [PATCH v5 4/4] vfio-user: support multiple FDs for sparse mmap regions Naman Gulati
@ 2026-10-15  7:45   ` Cédric Le Goater
  2026-10-20 15:35   ` John Levon
  1 sibling, 0 replies; 13+ messages in thread
From: Cédric Le Goater @ 2026-10-15  7:45 UTC (permalink / raw)
  To: Naman Gulati, qemu-devel
  Cc: John Levon, Thanos Makatos, Alex Williamson, Pierrick Bouvier,
	Matt Evans

On 10/14/26 23:10, Naman Gulati wrote:
> Add client support for capability VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS
> (capability ID 0x8001) in vfio-user, and advertise support via the
> "sparse_mmap_fds" capability in VFIO_USER_VERSION.
> 
> Update vfio_user_device_io_get_region_info() to receive multiple file
> descriptors passed in SCM_RIGHTS ancillary data for a region, store them
> in VFIOUserProxy, and implement setup_sparse_mmaps in
> vfio_user_device_io_ops_sock to validate and populate per-area file
> descriptors and fd_offset values.
> 
> Signed-off-by: Naman Gulati <namangulati@google.com>
> ---
>   hw/vfio-user/protocol.h |  35 ++++++++++--
>   hw/vfio-user/proxy.h    |   2 +
>   hw/vfio-user/device.c   | 121 +++++++++++++++++++++++++++++++++++++---
>   hw/vfio-user/proxy.c    |  52 +++++++++++++++--
>   4 files changed, 193 insertions(+), 17 deletions(-)
> 
> diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h
> index c478d1353f..b823689254 100644
> --- a/hw/vfio-user/protocol.h
> +++ b/hw/vfio-user/protocol.h
> @@ -15,6 +15,8 @@
>    * SPDX-License-Identifier: GPL-2.0-or-later
>    */
>   
> +#define VFIO_USER_MAX_REGIONS 100
> +
>   typedef struct {
>       uint16_t id;
>       uint16_t command;
> @@ -69,12 +71,13 @@ typedef struct {
>   #define VFIO_USER_CAP           "capabilities"
>   
>   /* "capabilities" members */
> -#define VFIO_USER_CAP_MAX_FDS   "max_msg_fds"
> -#define VFIO_USER_CAP_MAX_XFER  "max_data_xfer_size"
> -#define VFIO_USER_CAP_PGSIZES   "pgsizes"
> -#define VFIO_USER_CAP_MAP_MAX   "max_dma_maps"
> -#define VFIO_USER_CAP_MIGR      "migration"
> -#define VFIO_USER_CAP_MULTI     "write_multiple"
> +#define VFIO_USER_CAP_MAX_FDS           "max_msg_fds"
> +#define VFIO_USER_CAP_MAX_XFER          "max_data_xfer_size"
> +#define VFIO_USER_CAP_PGSIZES           "pgsizes"
> +#define VFIO_USER_CAP_MAP_MAX           "max_dma_maps"
> +#define VFIO_USER_CAP_MIGR              "migration"
> +#define VFIO_USER_CAP_MULTI             "write_multiple"
> +#define VFIO_USER_CAP_SPARSE_MMAP_FDS   "sparse_mmap_fds"
>   
>   /* "migration" members */
>   #define VFIO_USER_CAP_PGSIZE            "pgsize"
> @@ -166,6 +169,26 @@ typedef struct {
>       uint64_t offset;
>   } VFIOUserRegionInfo;
>   
> +/*
> + * VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS
> + */
> +#define VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS 0x8001
> +
> +struct vfio_region_sparse_mmap_fd_area {
> +    uint64_t offset;
> +    uint64_t fd_offset;
> +    uint64_t size;
> +    uint32_t fd_index;
> +    uint32_t pad;
> +};
> +
> +struct vfio_region_info_cap_sparse_mmap_fds {
> +    struct vfio_info_cap_header header;
> +    uint32_t nr_areas;
> +    uint32_t reserved;
> +    struct vfio_region_sparse_mmap_fd_area areas[];
> +};
> +
>   /*
>    * VFIO_USER_DEVICE_GET_IRQ_INFO
>    * imported from struct vfio_irq_info
> diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h
> index 7b97460cc5..4b9834a436 100644
> --- a/hw/vfio-user/proxy.h
> +++ b/hw/vfio-user/proxy.h
> @@ -72,6 +72,7 @@ typedef struct VFIOUserProxy {
>       AioContext *ctx;
>       QEMUBH *req_bh;
>       bool async_ops;
> +    VFIOUserFDs *region_fds[VFIO_USER_MAX_REGIONS];
>   
>       /*
>        * above only changed when BQL is held
> @@ -111,6 +112,7 @@ bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp);
>   
>   VFIOUserFDs *vfio_user_getfds(int numfds);
>   void vfio_user_putfds(VFIOUserMsg *msg);
> +void vfio_user_free_fds(VFIOUserFDs *fds);
>   
>   void vfio_user_disable_posted_writes(VFIOUserProxy *proxy);
>   
> diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c
> index 1a01e748cd..4fbf536a04 100644
> --- a/hw/vfio-user/device.c
> +++ b/hw/vfio-user/device.c
> @@ -14,12 +14,14 @@
>   
>   #include "hw/vfio-user/device.h"
>   #include "hw/vfio-user/trace.h"
> +#include "hw/vfio/vfio-region.h"
> +#include "hw/vfio/vfio-helpers.h"
> +#include "hw/vfio/trace.h"
>   
>   /*
>    * These are to defend against a malign server trying
>    * to force us to run out of memory.
>    */
> -#define VFIO_USER_MAX_REGIONS   100
>   #define VFIO_USER_MAX_IRQS      50
>   
>   bool vfio_user_get_device_info(VFIOUserProxy *proxy,
> @@ -209,14 +211,17 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
>                                                  struct vfio_region_info *info,
>                                                  int *fd)
>   {
> -    VFIOUserFDs fds = { 0, 1, fd};
> -    int ret;
> +    int fds[VFIO_USER_MAX_MAX_FDS];
> +    VFIOUserFDs user_fds = { 0, VFIO_USER_MAX_MAX_FDS, fds };
> +    int i, ret;
> +
> +    *fd = -1;
>   
>       if (info->index > vbasedev->num_initial_regions) {
>           return -EINVAL;
>       }

info->index is validated above

> -    ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds);
> +    ret = vfio_user_get_region_info(vbasedev->proxy, info, &user_fds);


info is modified by vfio_user_get_region_info() :

   memcpy(info, &msgp->argsz, info->argsz);

without revalidation of the udpated fields and ...

>       if (ret) {
>           return ret;
>       }
> @@ -225,10 +230,30 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
>       if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) &&
>           (info->cap_offset < sizeof(*info)
>            || info->cap_offset + sizeof(struct vfio_info_cap_header) > info->argsz)) {
> -        return -EINVAL;
> +        ret = -EINVAL;
> +    } else if (vfio_get_region_info_cap(info,
> +                                        VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS)) {
> +        if (user_fds.recv_fds > 0) {
> +            VFIOUserFDs *saved = vfio_user_getfds(user_fds.recv_fds);
> +
> +            saved->recv_fds = user_fds.recv_fds;
> +            memcpy(saved->fds, fds, user_fds.recv_fds * sizeof(int));
> +            vfio_user_free_fds(vbasedev->proxy->region_fds[info->index]);
> +            vbasedev->proxy->region_fds[info->index] = saved;

info->index could be bogus. This is a pre-existing flaw. Would
be good to fix.

Thanks,

C.

> +            return 0;
> +        }
> +        ret = -EINVAL;
> +    } else if (user_fds.recv_fds > 0) {
> +        *fd = fds[0];
> +        fds[0] = -1;
>       }
>   
> -    return 0;
> +    for (i = 0; i < user_fds.recv_fds; i++) {
> +        if (fds[i] >= 0) {
> +            close(fds[i]);
> +        }
> +    }
> +    return ret;
>   }
>   
>   static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev,
> @@ -518,12 +543,94 @@ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index,
>   /*
>    * Socket-based io_ops
>    */
> +static int vfio_user_device_io_setup_sparse_mmaps(VFIORegion *region,
> +                                                  struct vfio_region_info *info,
> +                                                  Error **errp)
> +{
> +    struct vfio_info_cap_header *hdr;
> +    struct vfio_region_info_cap_sparse_mmap_fds *sparse_fds;
> +    VFIOUserFDs *user_fds;
> +    size_t cap_offset;
> +    int i;
> +
> +    hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS);
> +    if (!hdr) {
> +        return vfio_default_setup_sparse_mmaps(region, info, errp);
> +    }
> +
> +    if (vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP)) {
> +        error_setg(errp, "%s: region %d cannot specify both "
> +                   "SPARSE_MMAP and SPARSE_MMAP_FDS", __func__, region->nr);
> +        return -EINVAL;
> +    }
> +
> +    sparse_fds = container_of(hdr, struct vfio_region_info_cap_sparse_mmap_fds,
> +                              header);
> +
> +    cap_offset = (uintptr_t)hdr - (uintptr_t)info;
> +    if (cap_offset > info->argsz ||
> +        info->argsz - cap_offset < sizeof(*sparse_fds) ||
> +        sparse_fds->nr_areas >
> +            (info->argsz - cap_offset - sizeof(*sparse_fds)) /
> +            sizeof(sparse_fds->areas[0])) {
> +        error_setg(errp, "%s: invalid nr_areas %u in region %d",
> +                   __func__, sparse_fds->nr_areas, region->nr);
> +        return -EINVAL;
> +    }
> +
> +    trace_vfio_region_sparse_mmap_header(region->vbasedev->name,
> +                                         region->nr, sparse_fds->nr_areas);
> +
> +    user_fds = region->vbasedev->proxy->region_fds[region->nr];
> +    if (!user_fds || user_fds->recv_fds > sparse_fds->nr_areas) {
> +        error_setg(errp, "%s: invalid fd count %d for %u sparse mmap areas "
> +                   "in region %d", __func__,
> +                   user_fds ? user_fds->recv_fds : 0,
> +                   sparse_fds->nr_areas, region->nr);
> +        return -EINVAL;
> +    }
> +
> +    region->mmaps = g_try_new0(VFIOMmap, sparse_fds->nr_areas);
> +    if (!region->mmaps) {
> +        error_setg(errp, "%s: failed to allocate %u mmaps for region %d",
> +                   __func__, sparse_fds->nr_areas, region->nr);
> +        return -ENOMEM;
> +    }
> +
> +    for (i = 0; i < sparse_fds->nr_areas; i++) {
> +        struct vfio_region_sparse_mmap_fd_area *area = &sparse_fds->areas[i];
> +
> +        if (area->size == 0 || area->offset > region->size ||
> +            area->size > region->size - area->offset ||
> +            area->fd_index >= user_fds->recv_fds) {
> +            error_setg(errp, "%s: invalid sparse mmap area %d in region %d "
> +                       "(offset=0x%"PRIx64" size=0x%"PRIx64
> +                       " fd_offset=0x%"PRIx64" fd_index=%u)",
> +                       __func__, i, region->nr, area->offset, area->size,
> +                       area->fd_offset, area->fd_index);
> +            g_free(region->mmaps);
> +            region->mmaps = NULL;
> +            return -EINVAL;
> +        }
> +
> +        trace_vfio_region_sparse_mmap_entry(i, area->offset,
> +                                            area->offset + area->size - 1);
> +        region->mmaps[i].offset = area->offset;
> +        region->mmaps[i].fd_offset = area->fd_offset;
> +        region->mmaps[i].size = area->size;
> +        region->mmaps[i].fd = user_fds->fds[area->fd_index];
> +    }
> +
> +    region->nr_mmaps = sparse_fds->nr_areas;
> +    return 0;
> +}
> +
>   VFIODeviceIOOps vfio_user_device_io_ops_sock = {
>       .device_feature = vfio_user_device_io_device_feature,
>       .get_region_info = vfio_user_device_io_get_region_info,
> +    .setup_sparse_mmaps = vfio_user_device_io_setup_sparse_mmaps,
>       .get_irq_info = vfio_user_device_io_get_irq_info,
>       .set_irqs = vfio_user_device_io_set_irqs,
>       .region_read = vfio_user_device_io_region_read,
>       .region_write = vfio_user_device_io_region_write,
> -
>   };
> diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c
> index 197aee07bf..ba29085321 100644
> --- a/hw/vfio-user/proxy.c
> +++ b/hw/vfio-user/proxy.c
> @@ -363,6 +363,13 @@ static int vfio_user_recv_one(VFIOUserProxy *proxy, Error **errp)
>           }
>   
>           memcpy(msg->fds->fds, fdp, numfds * sizeof(int));
> +    } else if (msg->fds != NULL) {
> +        /*
> +         * recv_fds holds the capacity of msg->fds->fds on entry; reset it
> +         * to 0 when the reply carries no FDs so the caller knows none were
> +         * received.
> +         */
> +        msg->fds->recv_fds = 0;
>       }
>   
>       /*
> @@ -740,6 +747,11 @@ bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr,
>   
>       qemu_mutex_unlock(&proxy->lock);
>   
> +    if (!ok && fds != NULL) {
> +        /* No reply was received; report 0 received FDs to the caller. */
> +        fds->recv_fds = 0;
> +    }
> +
>       return ok;
>   }
>   
> @@ -867,17 +879,30 @@ void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error)
>   }
>   
>   /*
> - * Close FDs erroneously received in an incoming request.
> + * Close and free received FDs.
>    */
> -void vfio_user_putfds(VFIOUserMsg *msg)
> +void vfio_user_free_fds(VFIOUserFDs *fds)
>   {
> -    VFIOUserFDs *fds = msg->fds;
>       int i;
>   
> +    if (!fds) {
> +        return;
> +    }
> +
>       for (i = 0; i < fds->recv_fds; i++) {
> -        close(fds->fds[i]);
> +        if (fds->fds[i] >= 0) {
> +            close(fds->fds[i]);
> +        }
>       }
>       g_free(fds);
> +}
> +
> +/*
> + * Close FDs erroneously received in an incoming request.
> + */
> +void vfio_user_putfds(VFIOUserMsg *msg)
> +{
> +    vfio_user_free_fds(msg->fds);
>       msg->fds = NULL;
>   }
>   
> @@ -967,6 +992,7 @@ void vfio_user_set_handler(VFIODevice *vbasedev,
>   void vfio_user_disconnect(VFIOUserProxy *proxy)
>   {
>       VFIOUserMsg *r1, *r2;
> +    int i;
>   
>       qemu_mutex_lock(&proxy->lock);
>   
> @@ -1027,6 +1053,10 @@ void vfio_user_disconnect(VFIOUserProxy *proxy)
>           vfio_user_iothread = NULL;
>       }
>   
> +    for (i = 0; i < VFIO_USER_MAX_REGIONS; i++) {
> +        vfio_user_free_fds(proxy->region_fds[i]);
> +    }
> +
>       g_free(proxy->sockname);
>       g_free(proxy);
>   }
> @@ -1214,6 +1244,18 @@ static bool check_multi(VFIOUserProxy *proxy, QObject *qobj, Error **errp)
>       return true;
>   }
>   
> +static bool check_sparse_mmap_fds(VFIOUserProxy *proxy, QObject *qobj,
> +                                  Error **errp)
> +{
> +    QBool *qb = qobject_to(QBool, qobj);
> +
> +    if (qb == NULL) {
> +        error_setg(errp, "malformed %s", VFIO_USER_CAP_SPARSE_MMAP_FDS);
> +        return false;
> +    }
> +    return true;
> +}
> +
>   static struct cap_entry caps_cap[] = {
>       { VFIO_USER_CAP_MAX_FDS, check_max_fds },
>       { VFIO_USER_CAP_MAX_XFER, check_max_xfer },
> @@ -1221,6 +1263,7 @@ static struct cap_entry caps_cap[] = {
>       { VFIO_USER_CAP_MAP_MAX, check_max_dma },
>       { VFIO_USER_CAP_MIGR, check_migr },
>       { VFIO_USER_CAP_MULTI, check_multi },
> +    { VFIO_USER_CAP_SPARSE_MMAP_FDS, check_sparse_mmap_fds },
>       { NULL }
>   };
>   
> @@ -1280,6 +1323,7 @@ static GString *caps_json(void)
>       qdict_put_int(capdict, VFIO_USER_CAP_PGSIZES, VFIO_USER_DEF_PGSIZE);
>       qdict_put_int(capdict, VFIO_USER_CAP_MAP_MAX, VFIO_USER_DEF_MAP_MAX);
>       qdict_put_bool(capdict, VFIO_USER_CAP_MULTI, true);
> +    qdict_put_bool(capdict, VFIO_USER_CAP_SPARSE_MMAP_FDS, true);
>   
>       qdict_put_obj(dict, VFIO_USER_CAP, QOBJECT(capdict));
>   



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 4/4] vfio-user: support multiple FDs for sparse mmap regions
  2026-10-14 21:10 ` [PATCH v5 4/4] vfio-user: support multiple FDs for sparse mmap regions Naman Gulati
  2026-10-15  7:45   ` Cédric Le Goater
@ 2026-10-20 15:35   ` John Levon
  1 sibling, 0 replies; 13+ messages in thread
From: John Levon @ 2026-10-20 15:35 UTC (permalink / raw)
  To: Naman Gulati
  Cc: qemu-devel, Thanos Makatos, Cédric Le Goater,
	Alex Williamson, Pierrick Bouvier, Matt Evans

On Wed, Oct 14, 2026 at 09:10:41PM +0000, Naman Gulati wrote:

> diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h
> index c478d1353f..b823689254 100644
> --- a/hw/vfio-user/protocol.h
> +++ b/hw/vfio-user/protocol.h
> @@ -15,6 +15,8 @@
>   * SPDX-License-Identifier: GPL-2.0-or-later
>   */
>  
> +#define VFIO_USER_MAX_REGIONS 100
> +
>  typedef struct {
>      uint16_t id;
>      uint16_t command;
> @@ -69,12 +71,13 @@ typedef struct {
>  #define VFIO_USER_CAP           "capabilities"
>  
>  /* "capabilities" members */
> -#define VFIO_USER_CAP_MAX_FDS   "max_msg_fds"
> -#define VFIO_USER_CAP_MAX_XFER  "max_data_xfer_size"
> -#define VFIO_USER_CAP_PGSIZES   "pgsizes"
> -#define VFIO_USER_CAP_MAP_MAX   "max_dma_maps"
> -#define VFIO_USER_CAP_MIGR      "migration"
> -#define VFIO_USER_CAP_MULTI     "write_multiple"
> +#define VFIO_USER_CAP_MAX_FDS           "max_msg_fds"
> +#define VFIO_USER_CAP_MAX_XFER          "max_data_xfer_size"
> +#define VFIO_USER_CAP_PGSIZES           "pgsizes"
> +#define VFIO_USER_CAP_MAP_MAX           "max_dma_maps"
> +#define VFIO_USER_CAP_MIGR              "migration"
> +#define VFIO_USER_CAP_MULTI             "write_multiple"
> +#define VFIO_USER_CAP_SPARSE_MMAP_FDS   "sparse_mmap_fds"
>  
>  /* "migration" members */
>  #define VFIO_USER_CAP_PGSIZE            "pgsize"
> @@ -166,6 +169,26 @@ typedef struct {
>      uint64_t offset;
>  } VFIOUserRegionInfo;
>  
> +/*
> + * VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS
> + */
> +#define VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS 0x8001

I think a brief note on the "0x8000..." space WRT kernel vfio is worthwhile
here.

Otherwise seems good modulo the other comments.

Please do send out your libvfio-user PR for review as well.

thanks
john


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 2/4] vfio: add 'fd' and 'fd_offset' to VFIOMmap
  2026-10-14 21:10 ` [PATCH v5 2/4] vfio: add 'fd' and 'fd_offset' to VFIOMmap Naman Gulati
  2026-10-15  7:39   ` Cédric Le Goater
@ 2026-10-20 15:37   ` John Levon
  1 sibling, 0 replies; 13+ messages in thread
From: John Levon @ 2026-10-20 15:37 UTC (permalink / raw)
  To: Naman Gulati
  Cc: qemu-devel, Thanos Makatos, Cédric Le Goater,
	Alex Williamson, Pierrick Bouvier, Matt Evans

On Wed, Oct 14, 2026 at 09:10:39PM +0000, Naman Gulati wrote:

> Allow each sparse mmap subregion to specify its own file descriptor and
> file offset for the mmap call.
> 
> Add 'fd' (set to -1 when the region fd should be used) and 'fd_offset'
> to VFIOMmap, and update vfio_region_mmap() to use them.
> 
> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
> Signed-off-by: Naman Gulati <namangulati@google.com>

Reviewed-by: John Levon <john.levon@nutanix.com>

thanks
john


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 1/4] vfio-user: add multi-fd region mmap capability
  2026-10-14 21:10 ` [PATCH v5 1/4] vfio-user: add multi-fd region mmap capability Naman Gulati
@ 2026-10-20 15:37   ` John Levon
  0 siblings, 0 replies; 13+ messages in thread
From: John Levon @ 2026-10-20 15:37 UTC (permalink / raw)
  To: Naman Gulati
  Cc: qemu-devel, Thanos Makatos, Cédric Le Goater,
	Alex Williamson, Pierrick Bouvier, Matt Evans

On Wed, Oct 14, 2026 at 09:10:38PM +0000, Naman Gulati wrote:

> Extend the vfio-user protocol specification to allow servers to pass
> multiple file descriptors for different sparse memory areas within a
> single device region with capability
> VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS (0x8001), negotiated via the
> "sparse_mmap_fds" capability in VFIO_USER_VERSION.
> 
> VFIO_REGION_INFO_CAP_SPARSE_MMAP_FDS defines an array of sparse mmap
> areas where each area contains:
> - offset: Start of the area within the device region (BAR).
> - fd_offset: Start of the mapping within the backing file descriptor.
> - size: Size of the mappable area.
> - fd_index: Zero-based index into the array of file descriptors
>   received in the SCM_RIGHTS ancillary data of the
>   VFIO_USER_DEVICE_GET_REGION_INFO reply message.
> 
> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
> Signed-off-by: Naman Gulati <namangulati@google.com>

Reviewed-by: John Levon <john.levon@nutanix.com>

regards
john


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps
  2026-10-14 21:10 ` [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps Naman Gulati
  2026-10-15  7:39   ` Cédric Le Goater
  2026-10-15  7:41   ` Cédric Le Goater
@ 2026-10-20 15:38   ` John Levon
  2 siblings, 0 replies; 13+ messages in thread
From: John Levon @ 2026-10-20 15:38 UTC (permalink / raw)
  To: Naman Gulati
  Cc: qemu-devel, Thanos Makatos, Cédric Le Goater,
	Alex Williamson, Pierrick Bouvier, Matt Evans

On Wed, Oct 14, 2026 at 09:10:40PM +0000, Naman Gulati wrote:

> Add a setup_sparse_mmaps callback to VFIODeviceIOOps so transport
> backends (such as vfio-user) can parse custom sparse mmap capabilities,
> falling back to vfio_default_setup_sparse_mmaps() to process shared
> VFIO mmap capabilities.
> 
> Reviewed-by: Thanos Makatos <thanos.makatos@nutanix.com>
> Signed-off-by: Naman Gulati <namangulati@google.com>

Reviewed-by: John Levon <john.levon@nutanix.com>

regards
john


^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-10-20 15:39 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-14 21:10 [PATCH v5 0/4] vfio-user: support multiple FDs backing region mmaps Naman Gulati
2026-10-14 21:10 ` [PATCH v5 1/4] vfio-user: add multi-fd region mmap capability Naman Gulati
2026-10-20 15:37   ` John Levon
2026-10-14 21:10 ` [PATCH v5 2/4] vfio: add 'fd' and 'fd_offset' to VFIOMmap Naman Gulati
2026-10-15  7:39   ` Cédric Le Goater
2026-10-20 15:37   ` John Levon
2026-10-14 21:10 ` [PATCH v5 3/4] vfio: add setup_sparse_mmaps hook to VFIODeviceIOOps Naman Gulati
2026-10-15  7:39   ` Cédric Le Goater
2026-10-15  7:41   ` Cédric Le Goater
2026-10-20 15:38   ` John Levon
2026-10-14 21:10 ` [PATCH v5 4/4] vfio-user: support multiple FDs for sparse mmap regions Naman Gulati
2026-10-15  7:45   ` Cédric Le Goater
2026-10-20 15:35   ` John Levon

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.