From 5a78db7f8099274550bdacdc1fc24943567ac615 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:27 -0400 Subject: [PATCH 01/12] vfio/container: Remap only populated parts in a section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there are multiple containers and unmap-all fails for some of them, we need to remap vaddr for the other containers for which unmap-all succeeded. When ram discard is enabled, we should only remap populated parts in a section instead of the whole section. Fixes: eba1f657cbb1 ("vfio/container: recover from unmap-all-vaddr failure") Signed-off-by: Zhenzhong Duan Reviewed-by: Steven Sistare Reviewed-by: David Hildenbrand Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-2-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 20 +++++++++++++++----- hw/vfio/listener.c | 4 ++-- include/hw/vfio/vfio-cpr.h | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index 80af7469d0..b4581e8f85 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -228,22 +228,32 @@ void vfio_cpr_giommu_remap(VFIOContainer *bcontainer, memory_region_iommu_replay(giommu->iommu_mr, &giommu->n); } +static int vfio_cpr_rdm_remap(MemoryRegionSection *section, void *opaque) +{ + RamDiscardListener *rdl = opaque; + + return rdl->notify_populate(rdl, section); +} + /* * In old QEMU, VFIO_DMA_UNMAP_FLAG_VADDR may fail on some mapping after * succeeding for others, so the latter have lost their vaddr. Call this - * to restore vaddr for a section with a RamDiscardManager. + * to restore vaddr for populated parts in a section with a RamDiscardManager. * - * The ram discard listener already exists. Call its populate function + * The ram discard listener already exists. Call its replay_populated function * directly, which calls vfio_legacy_cpr_dma_map. */ -bool vfio_cpr_ram_discard_register_listener(VFIOContainer *bcontainer, - MemoryRegionSection *section) +bool vfio_cpr_ram_discard_replay_populated(VFIOContainer *bcontainer, + MemoryRegionSection *section) { + RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); VFIORamDiscardListener *vrdl = vfio_find_ram_discard_listener(bcontainer, section); g_assert(vrdl); - return vrdl->listener.notify_populate(&vrdl->listener, section) == 0; + return ram_discard_manager_replay_populated(rdm, section, + vfio_cpr_rdm_remap, + &vrdl->listener) == 0; } int vfio_cpr_group_get_device_fd(int d, const char *name) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index c6bb58f520..1b6e5065a3 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -577,8 +577,8 @@ void vfio_container_region_add(VFIOContainer *bcontainer, if (!vfio_ram_discard_register_listener(bcontainer, section, &err)) { goto fail; } - } else if (!vfio_cpr_ram_discard_register_listener(bcontainer, - section)) { + } else if (!vfio_cpr_ram_discard_replay_populated(bcontainer, + section)) { error_setg(&err, "vfio_cpr_ram_discard_register_listener for %s failed", memory_region_name(section->mr)); diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 81f4e24e22..4606da500a 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -68,7 +68,7 @@ bool vfio_cpr_container_match(struct VFIOLegacyContainer *container, void vfio_cpr_giommu_remap(struct VFIOContainer *bcontainer, MemoryRegionSection *section); -bool vfio_cpr_ram_discard_register_listener( +bool vfio_cpr_ram_discard_replay_populated( struct VFIOContainer *bcontainer, MemoryRegionSection *section); void vfio_cpr_save_vector_fd(struct VFIOPCIDevice *vdev, const char *name, From 94230948960e56cb47e835266c7cd8df46da03a4 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:28 -0400 Subject: [PATCH 02/12] vfio/cpr-legacy: drop an erroneous assert MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_legacy_cpr_dma_map() is not only used in post_load on destination but also error recovery path on source side. Assert it for destination is wrong. Fixes: 7e9f21411302 ("vfio/container: restore DMA vaddr") Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-3-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index b4581e8f85..7184c93991 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -52,8 +52,6 @@ static int vfio_legacy_cpr_dma_map(const VFIOContainer *bcontainer, .size = size, }; - g_assert(cpr_is_incoming()); - if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map)) { return -errno; } From d59db04aed750ba4fc56f79cae99814334ec8285 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:29 -0400 Subject: [PATCH 03/12] vfio/iommufd: Set cpr.ioas_id on source side for CPR transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On source side, if there are more than one VFIO devices and they attach to same container, only the first device sets cpr.ioas_id, the others are bypassed. We should set it for each device, or else only first device works. Fixes: 4296ee07455e ("vfio/iommufd: reconstruct device") Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-4-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 68470d552e..63a16d4dc1 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -602,7 +602,6 @@ skip_ioas_alloc: container->be = vbasedev->iommufd; container->ioas_id = ioas_id; QLIST_INIT(&container->hwpt_list); - vbasedev->cpr.ioas_id = ioas_id; bcontainer = VFIO_IOMMU(container); vfio_address_space_insert(space, bcontainer); @@ -636,6 +635,8 @@ skip_ioas_alloc: bcontainer->initialized = true; found_container: + vbasedev->cpr.ioas_id = container->ioas_id; + ret = ioctl(devfd, VFIO_DEVICE_GET_INFO, &dev_info); if (ret) { error_setg_errno(errp, errno, "error getting device info"); From 8bf49fff0dfbb065ad65daa48d2e1a63ad2fd552 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:30 -0400 Subject: [PATCH 04/12] vfio/iommufd: Restore vbasedev's reference to hwpt after CPR transfer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After CPR transfer, if there are more than one VFIO devices, device is not added to hwpt->device_list and its reference to hwpt isn't restored on destination. We still need to call iommufd_cdev_attach_container() to restore it after a matching container is found, or else SIGSEV triggers. Fixes: 4296ee07455e ("vfio/iommufd: reconstruct device") Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-5-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 63a16d4dc1..24a99efa87 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -560,10 +560,9 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, continue; } - if (!cpr_is_incoming()) { + if (!cpr_is_incoming() || + (vbasedev->cpr.ioas_id == container->ioas_id)) { res = iommufd_cdev_attach_container(vbasedev, container, &err); - } else if (vbasedev->cpr.ioas_id == container->ioas_id) { - res = true; } else { continue; } From 725ec898039dfed0a669bf78b556529b51e248e8 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Sun, 28 Sep 2025 04:54:31 -0400 Subject: [PATCH 05/12] accel/kvm: Fix an erroneous check on coalesced_mmio_ring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to KVM uAPI, coalesced mmio page is KVM_COALESCED_MMIO_PAGE_OFFSET offset from kvm_run pages. For x86 it's 2 pages offset, for arm it's 1 page offset currently. We shouldn't presume it's hardcoded 1 page or else coalesced_mmio_ring will not be cleared in do_kvm_destroy_vcpu() in x86. Fixes: 7ed0919119b0 ("migration: close kvm after cpr") Cc: Markus Armbruster Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250928085432.40107-6-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- accel/kvm/kvm-all.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 56031925c4..f9254ae654 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -525,7 +525,8 @@ static int do_kvm_destroy_vcpu(CPUState *cpu) } /* If I am the CPU that created coalesced_mmio_ring, then discard it */ - if (s->coalesced_mmio_ring == (void *)cpu->kvm_run + PAGE_SIZE) { + if (s->coalesced_mmio_ring == + (void *)cpu->kvm_run + s->coalesced_mmio * PAGE_SIZE) { s->coalesced_mmio_ring = NULL; } From 962bcf0911e7f3601da0f07ba7da9824cb6a5ba5 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 9 Oct 2025 00:01:32 -0400 Subject: [PATCH 06/12] vfio/container: Support unmap all in one ioctl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VFIO type1 kernel uAPI supports unmapping whole address space in one call since commit c19650995374 ("vfio/type1: implement unmap all"). Use the unmap_all variant whenever it's supported in kernel. Opportunistically pass VFIOLegacyContainer pointer in low level function vfio_legacy_dma_unmap_one(). Co-developed-by: John Levon Signed-off-by: John Levon Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251009040134.334251-2-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/container-legacy.c | 36 ++++++++++++++----------- include/hw/vfio/vfio-container-legacy.h | 1 + 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/hw/vfio/container-legacy.c b/hw/vfio/container-legacy.c index a3615d7b5d..8e9639603e 100644 --- a/hw/vfio/container-legacy.c +++ b/hw/vfio/container-legacy.c @@ -121,14 +121,14 @@ unmap_exit: return ret; } -static int vfio_legacy_dma_unmap_one(const VFIOContainer *bcontainer, +static int vfio_legacy_dma_unmap_one(const VFIOLegacyContainer *container, hwaddr iova, uint64_t size, - IOMMUTLBEntry *iotlb) + uint32_t flags, IOMMUTLBEntry *iotlb) { - const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + const VFIOContainer *bcontainer = VFIO_IOMMU(container); struct vfio_iommu_type1_dma_unmap unmap = { .argsz = sizeof(unmap), - .flags = 0, + .flags = flags, .iova = iova, .size = size, }; @@ -170,25 +170,28 @@ static int vfio_legacy_dma_unmap(const VFIOContainer *bcontainer, hwaddr iova, uint64_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { + const VFIOLegacyContainer *container = VFIO_IOMMU_LEGACY(bcontainer); + uint32_t flags = 0; int ret; if (unmap_all) { - /* The unmap ioctl doesn't accept a full 64-bit span. */ - Int128 llsize = int128_rshift(int128_2_64(), 1); + if (container->unmap_all_supported) { + flags = VFIO_DMA_UNMAP_FLAG_ALL; + } else { + /* The unmap ioctl doesn't accept a full 64-bit span. */ + Int128 llsize = int128_rshift(int128_2_64(), 1); + size = int128_get64(llsize); - ret = vfio_legacy_dma_unmap_one(bcontainer, 0, int128_get64(llsize), - iotlb); + ret = vfio_legacy_dma_unmap_one(container, 0, size, flags, iotlb); + if (ret) { + return ret; + } - if (ret == 0) { - ret = vfio_legacy_dma_unmap_one(bcontainer, int128_get64(llsize), - int128_get64(llsize), iotlb); + iova = size; } - - } else { - ret = vfio_legacy_dma_unmap_one(bcontainer, iova, size, iotlb); } - return ret; + return vfio_legacy_dma_unmap_one(container, iova, size, flags, iotlb); } static int vfio_legacy_dma_map(const VFIOContainer *bcontainer, hwaddr iova, @@ -519,6 +522,9 @@ static bool vfio_legacy_setup(VFIOContainer *bcontainer, Error **errp) vfio_get_info_iova_range(info, bcontainer); + ret = ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UNMAP_ALL); + container->unmap_all_supported = !!ret; + vfio_get_iommu_info_migration(container, info); return true; } diff --git a/include/hw/vfio/vfio-container-legacy.h b/include/hw/vfio/vfio-container-legacy.h index 74a72df018..ffd594e80d 100644 --- a/include/hw/vfio/vfio-container-legacy.h +++ b/include/hw/vfio/vfio-container-legacy.h @@ -30,6 +30,7 @@ struct VFIOLegacyContainer { int fd; /* /dev/vfio/vfio, empowered by the attached groups */ unsigned iommu_type; + bool unmap_all_supported; QLIST_HEAD(, VFIOGroup) group_list; VFIOContainerCPR cpr; }; From b30823e5619ed5658d33e43abe1308195edb3e8b Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 9 Oct 2025 00:01:33 -0400 Subject: [PATCH 07/12] vfio/iommufd: Support unmap all in one ioctl() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IOMMUFD kernel uAPI supports unmapping whole address space in one call with [iova, size] set to [0, UINT64_MAX], this can simplify iommufd_cdev_unmap() a bit. See iommufd_ioas_unmap() in kernel for details. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251009040134.334251-3-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 24a99efa87..fc9cd9d22f 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -62,21 +62,8 @@ static int iommufd_cdev_unmap(const VFIOContainer *bcontainer, { const VFIOIOMMUFDContainer *container = VFIO_IOMMU_IOMMUFD(bcontainer); - /* unmap in halves */ if (unmap_all) { - Int128 llsize = int128_rshift(int128_2_64(), 1); - int ret; - - ret = iommufd_backend_unmap_dma(container->be, container->ioas_id, - 0, int128_get64(llsize)); - - if (ret == 0) { - ret = iommufd_backend_unmap_dma(container->be, container->ioas_id, - int128_get64(llsize), - int128_get64(llsize)); - } - - return ret; + size = UINT64_MAX; } /* TODO: Handle dma_unmap_bitmap with iotlb args (migration) */ From 271fec6f18492630df2e1b4599ba2de6eb1d0668 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 9 Oct 2025 00:01:34 -0400 Subject: [PATCH 08/12] vfio/listener: Add an assertion for unmap_all MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the maximum of iommu address space is 64bit. So when a maximum iommu memory section is deleted, it's in scope [0, 2^64). Add a assertion for that. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251009040134.334251-4-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 1b6e5065a3..2d7d3a4645 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -715,6 +715,7 @@ static void vfio_listener_region_del(MemoryListener *listener, bool unmap_all = false; if (int128_eq(llsize, int128_2_64())) { + assert(!iova); unmap_all = true; llsize = int128_zero(); } From 1118a4207b0c5287d942a18b1a446c2e29baea3a Mon Sep 17 00:00:00 2001 From: John Levon Date: Thu, 9 Oct 2025 16:02:06 +0200 Subject: [PATCH 09/12] docs/system/devices/vfio-user: fix formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The example QEMU argument was not rendering properly, as it was not indented. Signed-off-by: John Levon Fixes: c688cc165b ("docs: add vfio-user documentation") Reviewed-by: Peter Maydell Link: https://lore.kernel.org/qemu-devel/20251009140206.386249-1-john.levon@nutanix.com Reviewed-by: Cédric Le Goater Signed-off-by: Cédric Le Goater --- docs/system/devices/vfio-user.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/devices/vfio-user.rst b/docs/system/devices/vfio-user.rst index 30c2215f4e..e10a6d0822 100644 --- a/docs/system/devices/vfio-user.rst +++ b/docs/system/devices/vfio-user.rst @@ -20,7 +20,7 @@ Presuming a suitable ``vfio-user`` server has opened a socket at .. code-block:: console --device '{"driver": "vfio-user-pci","socket": {"path": "/tmp/vfio-user.sock", "type": "unix"}}' + --device '{"driver": "vfio-user-pci","socket": {"path": "/tmp/vfio-user.sock", "type": "unix"}}' See `libvfio-user `_ for further information. From 7ea3fd2b6c2543c60fb309265fa445fa8fdcc1ea Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 13 Oct 2025 09:35:35 -0600 Subject: [PATCH 10/12] MAINTAINERS: Update Alex Williamson's email address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to a personal email account as I'll be leaving Red Hat soon. Signed-off-by: Alex Williamson Signed-off-by: Alex Williamson Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20251013153543.3091169-1-alex.williamson@redhat.com Signed-off-by: Cédric Le Goater --- .mailmap | 1 + MAINTAINERS | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.mailmap b/.mailmap index 15bec72470..7f817d9f42 100644 --- a/.mailmap +++ b/.mailmap @@ -74,6 +74,7 @@ Aleksandar Markovic Aleksandar Rikalo Aleksandar Rikalo +Alex Williamson Alexander Graf Ani Sinha Anthony Liguori Anthony Liguori diff --git a/MAINTAINERS b/MAINTAINERS index 667acd933c..7a0809d82a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2285,7 +2285,7 @@ S: Maintained F: hw/usb/dev-serial.c VFIO -M: Alex Williamson +M: Alex Williamson M: Cédric Le Goater S: Supported F: hw/vfio/* @@ -2297,7 +2297,7 @@ F: migration/vfio-stub.c F: tests/functional/aarch64/test_device_passthrough.py vfio-igd -M: Alex Williamson +M: Alex Williamson M: Cédric Le Goater M: Tomita Moeko S: Supported From aaca725884b57c9245528a0afb3f32e078543faf Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 14 Oct 2025 17:12:26 +0200 Subject: [PATCH 11/12] vfio: rename field to "num_initial_regions" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We set VFIODevice::num_regions at initialization time, and do not otherwise refresh it. As it is valid in theory for a VFIO device to later increase the number of supported regions, rename the field to "num_initial_regions" to better reflect its semantics. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20251014151227.2298892-2-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 2 +- hw/vfio/ccw.c | 4 ++-- hw/vfio/device.c | 12 ++++++------ hw/vfio/iommufd.c | 3 ++- hw/vfio/pci.c | 4 ++-- include/hw/vfio/vfio-device.h | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 0609a7dc25..64ef35b320 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -134,7 +134,7 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, VFIOUserFDs fds = { 0, 1, fd}; int ret; - if (info->index > vbasedev->num_regions) { + if (info->index > vbasedev->num_initial_regions) { return -EINVAL; } diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 9560b8d851..4d9588e7aa 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -484,9 +484,9 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) * We always expect at least the I/O region to be present. We also * may have a variable number of regions governed by capabilities. */ - if (vdev->num_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) { + if (vdev->num_initial_regions < VFIO_CCW_CONFIG_REGION_INDEX + 1) { error_setg(errp, "vfio: too few regions (%u), expected at least %u", - vdev->num_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1); + vdev->num_initial_regions, VFIO_CCW_CONFIG_REGION_INDEX + 1); return false; } diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 64f8750389..52079f4cf5 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -257,7 +257,7 @@ int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type, { int i; - for (i = 0; i < vbasedev->num_regions; i++) { + for (i = 0; i < vbasedev->num_initial_regions; i++) { struct vfio_info_cap_header *hdr; struct vfio_region_info_cap_type *cap_type; @@ -466,7 +466,7 @@ void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainer *bcontainer, int i; vbasedev->num_irqs = info->num_irqs; - vbasedev->num_regions = info->num_regions; + vbasedev->num_initial_regions = info->num_regions; vbasedev->flags = info->flags; vbasedev->reset_works = !!(info->flags & VFIO_DEVICE_FLAGS_RESET); @@ -476,10 +476,10 @@ void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainer *bcontainer, QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next); vbasedev->reginfo = g_new0(struct vfio_region_info *, - vbasedev->num_regions); + vbasedev->num_initial_regions); if (vbasedev->use_region_fds) { - vbasedev->region_fds = g_new0(int, vbasedev->num_regions); - for (i = 0; i < vbasedev->num_regions; i++) { + vbasedev->region_fds = g_new0(int, vbasedev->num_initial_regions); + for (i = 0; i < vbasedev->num_initial_regions; i++) { vbasedev->region_fds[i] = -1; } } @@ -489,7 +489,7 @@ void vfio_device_unprepare(VFIODevice *vbasedev) { int i; - for (i = 0; i < vbasedev->num_regions; i++) { + for (i = 0; i < vbasedev->num_initial_regions; i++) { g_free(vbasedev->reginfo[i]); if (vbasedev->region_fds != NULL && vbasedev->region_fds[i] != -1) { close(vbasedev->region_fds[i]); diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index fc9cd9d22f..bb5775aa71 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -650,7 +650,8 @@ found_container: vfio_iommufd_cpr_register_device(vbasedev); trace_iommufd_cdev_device_info(vbasedev->name, devfd, vbasedev->num_irqs, - vbasedev->num_regions, vbasedev->flags); + vbasedev->num_initial_regions, + vbasedev->flags); return true; err_listener_register: diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 06b06afc2b..8b8bc5a421 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2975,9 +2975,9 @@ bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp) return false; } - if (vbasedev->num_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { + if (vbasedev->num_initial_regions < VFIO_PCI_CONFIG_REGION_INDEX + 1) { error_setg(errp, "unexpected number of io regions %u", - vbasedev->num_regions); + vbasedev->num_initial_regions); return false; } diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 7e9aed6d3c..0fe6c60ba2 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -74,7 +74,7 @@ typedef struct VFIODevice { VFIODeviceOps *ops; VFIODeviceIOOps *io_ops; unsigned int num_irqs; - unsigned int num_regions; + unsigned int num_initial_regions; unsigned int flags; VFIOMigration *migration; Error *migration_blocker; From ecbe424a63c9f860a901d6a4a75724b046abd796 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 14 Oct 2025 17:12:27 +0200 Subject: [PATCH 12/12] vfio: only check region info cache for initial regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is semantically valid for a VFIO device to increase the number of regions after initialization. In this case, we'd attempt to check for cached region info past the size of the ->reginfo array. Check for the region index and skip the cache in these cases. This also works around some VGPU use cases which appear to be a bug, where VFIO_DEVICE_QUERY_GFX_PLANE returns a region index beyond the reported ->num_regions. Fixes: 95cdb024 ("vfio: add region info cache") Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20251014151227.2298892-3-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/device.c | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 52079f4cf5..8b63e765ac 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -205,10 +205,19 @@ int vfio_device_get_region_info(VFIODevice *vbasedev, int index, int fd = -1; int ret; - /* check cache */ - if (vbasedev->reginfo[index] != NULL) { - *info = vbasedev->reginfo[index]; - return 0; + /* + * We only set up the region info cache for the initial number of regions. + * + * Since a VFIO device may later increase the number of regions then use + * such regions with an index past ->num_initial_regions, don't attempt to + * use the info cache in those cases. + */ + if (index < vbasedev->num_initial_regions) { + /* check cache */ + if (vbasedev->reginfo[index] != NULL) { + *info = vbasedev->reginfo[index]; + return 0; + } } *info = g_malloc0(argsz); @@ -236,10 +245,12 @@ retry: goto retry; } - /* fill cache */ - vbasedev->reginfo[index] = *info; - if (vbasedev->region_fds != NULL) { - vbasedev->region_fds[index] = fd; + if (index < vbasedev->num_initial_regions) { + /* fill cache */ + vbasedev->reginfo[index] = *info; + if (vbasedev->region_fds != NULL) { + vbasedev->region_fds[index] = fd; + } } return 0;