Add a test that does iommufd hwpt replace while a DMA is ongoing. This
verifies the hitless replace of IOMMU domain without disrupting the DMA.
Note that the new domain is attached after mapping the required DMA
memory at the same IOVA in the new domain.
Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
---
tools/testing/selftests/vfio/Makefile | 1 +
.../vfio/vfio_iommufd_hwpt_replace_test.c | 151 ++++++++++++++++++
2 files changed, 152 insertions(+)
create mode 100644 tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c
diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile
index 3c796ca99a50..09a1e57cc77d 100644
--- a/tools/testing/selftests/vfio/Makefile
+++ b/tools/testing/selftests/vfio/Makefile
@@ -1,5 +1,6 @@
CFLAGS = $(KHDR_INCLUDES)
TEST_GEN_PROGS += vfio_dma_mapping_test
+TEST_GEN_PROGS += vfio_iommufd_hwpt_replace_test
TEST_GEN_PROGS += vfio_iommufd_setup_test
TEST_GEN_PROGS += vfio_pci_device_test
TEST_GEN_PROGS += vfio_pci_device_init_perf_test
diff --git a/tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c b/tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c
new file mode 100644
index 000000000000..efef3233494f
--- /dev/null
+++ b/tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+
+#include <linux/sizes.h>
+#include <linux/vfio.h>
+
+#include <libvfio.h>
+
+#include "kselftest_harness.h"
+
+static const char *device_bdf;
+
+static void region_setup(struct iommu *iommu,
+ struct iova_allocator *iova_allocator,
+ struct dma_region *region, u64 size)
+{
+ const int flags = MAP_SHARED | MAP_ANONYMOUS;
+ const int prot = PROT_READ | PROT_WRITE;
+ void *vaddr;
+
+ vaddr = mmap(NULL, size, prot, flags, -1, 0);
+ VFIO_ASSERT_NE(vaddr, MAP_FAILED);
+
+ region->vaddr = vaddr;
+ region->iova = iova_allocator_alloc(iova_allocator, size);
+ region->size = size;
+
+ iommu_map(iommu, region);
+}
+
+static void region_teardown(struct iommu *iommu, struct dma_region *region)
+{
+ iommu_unmap(iommu, region);
+ VFIO_ASSERT_EQ(munmap(region->vaddr, region->size), 0);
+}
+
+FIXTURE(vfio_iommufd_replace_hwpt_test) {
+ struct iommu *iommu;
+ struct vfio_pci_device *device;
+ struct iova_allocator *iova_allocator;
+ struct dma_region memcpy_region;
+ void *vaddr;
+
+ u64 size;
+ void *src;
+ void *dst;
+ iova_t src_iova;
+ iova_t dst_iova;
+};
+
+FIXTURE_SETUP(vfio_iommufd_replace_hwpt_test)
+{
+ struct vfio_pci_driver *driver;
+
+ self->iommu = iommu_init("iommufd");
+ self->device = vfio_pci_device_init(device_bdf, self->iommu);
+ self->iova_allocator = iova_allocator_init(self->iommu);
+
+ driver = &self->device->driver;
+
+ region_setup(self->iommu, self->iova_allocator, &self->memcpy_region, SZ_1G);
+ region_setup(self->iommu, self->iova_allocator, &driver->region, SZ_2M);
+
+ if (driver->ops)
+ vfio_pci_driver_init(self->device);
+
+ self->size = self->memcpy_region.size / 2;
+ self->src = self->memcpy_region.vaddr;
+ self->dst = self->src + self->size;
+
+ self->src_iova = to_iova(self->device, self->src);
+ self->dst_iova = to_iova(self->device, self->dst);
+}
+
+FIXTURE_TEARDOWN(vfio_iommufd_replace_hwpt_test)
+{
+ struct vfio_pci_driver *driver = &self->device->driver;
+
+ if (driver->ops)
+ vfio_pci_driver_remove(self->device);
+
+ region_teardown(self->iommu, &self->memcpy_region);
+ region_teardown(self->iommu, &driver->region);
+
+ iova_allocator_cleanup(self->iova_allocator);
+ vfio_pci_device_cleanup(self->device);
+ iommu_cleanup(self->iommu);
+}
+
+FIXTURE_VARIANT(vfio_iommufd_replace_hwpt_test) {
+ bool replace_hwpt;
+};
+
+FIXTURE_VARIANT_ADD(vfio_iommufd_replace_hwpt_test, domain_replace) {
+ .replace_hwpt = true,
+};
+
+FIXTURE_VARIANT_ADD(vfio_iommufd_replace_hwpt_test, noreplace) {
+ .replace_hwpt = false,
+};
+
+TEST_F(vfio_iommufd_replace_hwpt_test, memcpy)
+{
+ struct dma_region memcpy_region, driver_region;
+ struct iommu *iommu2;
+
+ if (self->device->driver.ops) {
+ memset(self->src, 'x', self->size);
+ memset(self->dst, 'y', self->size);
+
+ vfio_pci_driver_memcpy_start(self->device,
+ self->src_iova,
+ self->dst_iova,
+ self->size,
+ 100);
+ }
+
+ if (variant->replace_hwpt) {
+ iommu2 = iommufd_iommu_init(self->iommu->iommufd,
+ self->device->dev_id);
+
+ memcpy_region = self->memcpy_region;
+ driver_region = self->device->driver.region;
+
+ iommu_map(iommu2, &memcpy_region);
+ iommu_map(iommu2, &driver_region);
+
+ vfio_pci_device_attach_iommu(self->device, iommu2);
+ }
+
+ if (self->device->driver.ops) {
+ ASSERT_EQ(0, vfio_pci_driver_memcpy_wait(self->device));
+ ASSERT_EQ(0, memcmp(self->src, self->dst, self->size));
+ }
+
+ if (variant->replace_hwpt) {
+ vfio_pci_device_attach_iommu(self->device, self->iommu);
+
+ iommu_unmap(iommu2, &memcpy_region);
+ iommu_unmap(iommu2, &driver_region);
+ iommu_cleanup(iommu2);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ device_bdf = vfio_selftests_get_bdf(&argc, argv);
+
+ return test_harness_run(argc, argv);
+}
--
2.52.0.351.gbe84eed79e-goog
Hi Samiullah,
kernel test robot noticed the following build warnings:
[auto build test WARNING on 6cd6c12031130a349a098dbeb19d8c3070d2dfbe]
url: https://github.com/intel-lab-lkp/linux/commits/Samiullah-Khawaja/iommu-vt-d-Allow-replacing-no_pasid-iommu_domain/20260108-041955
base: 6cd6c12031130a349a098dbeb19d8c3070d2dfbe
patch link: https://lore.kernel.org/r/20260107201800.2486137-4-skhawaja%40google.com
patch subject: [PATCH 3/3] vfio: selftests: Add iommufd hwpt replace test
config: i386-allnoconfig-bpf (https://download.01.org/0day-ci/archive/20260109/202601090106.YNZaDPcd-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260109/202601090106.YNZaDPcd-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601090106.YNZaDPcd-lkp@intel.com/
All warnings (new ones prefixed by >>):
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:32:37: note: expanded from macro 'VFIO_ASSERT_EQ'
32 | #define VFIO_ASSERT_EQ(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, ==, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:4: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
In file included from tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:8:
In file included from tools/testing/selftests/vfio/lib/include/libvfio.h:6:
tools/testing/selftests/vfio/lib/include/libvfio/iommu.h:51:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
51 | VFIO_ASSERT_EQ(__iommu_unmap(iommu, region, NULL), 0);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:32:37: note: expanded from macro 'VFIO_ASSERT_EQ'
32 | #define VFIO_ASSERT_EQ(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, ==, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:22: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
In file included from tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:8:
In file included from tools/testing/selftests/vfio/lib/include/libvfio.h:6:
tools/testing/selftests/vfio/lib/include/libvfio/iommu.h:58:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
58 | VFIO_ASSERT_EQ(__iommu_unmap_all(iommu, NULL), 0);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:32:37: note: expanded from macro 'VFIO_ASSERT_EQ'
32 | #define VFIO_ASSERT_EQ(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, ==, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:4: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
In file included from tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:8:
In file included from tools/testing/selftests/vfio/lib/include/libvfio.h:6:
tools/testing/selftests/vfio/lib/include/libvfio/iommu.h:58:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
58 | VFIO_ASSERT_EQ(__iommu_unmap_all(iommu, NULL), 0);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:32:37: note: expanded from macro 'VFIO_ASSERT_EQ'
32 | #define VFIO_ASSERT_EQ(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, ==, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:22: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
In file included from tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:8:
In file included from tools/testing/selftests/vfio/lib/include/libvfio.h:8:
tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h:80:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
80 | VFIO_ASSERT_NE(r, -1, "F_GETFL failed for fd %d\n", fd);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:33:37: note: expanded from macro 'VFIO_ASSERT_NE'
33 | #define VFIO_ASSERT_NE(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, !=, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:4: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
In file included from tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:8:
In file included from tools/testing/selftests/vfio/lib/include/libvfio.h:8:
tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h:80:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
80 | VFIO_ASSERT_NE(r, -1, "F_GETFL failed for fd %d\n", fd);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:33:37: note: expanded from macro 'VFIO_ASSERT_NE'
33 | #define VFIO_ASSERT_NE(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, !=, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:22: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
In file included from tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:8:
In file included from tools/testing/selftests/vfio/lib/include/libvfio.h:8:
tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h:83:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
83 | VFIO_ASSERT_NE(r, -1, "F_SETFL O_NONBLOCK failed for fd %d\n", fd);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:33:37: note: expanded from macro 'VFIO_ASSERT_NE'
33 | #define VFIO_ASSERT_NE(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, !=, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:4: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
In file included from tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:8:
In file included from tools/testing/selftests/vfio/lib/include/libvfio.h:8:
tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h:83:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
83 | VFIO_ASSERT_NE(r, -1, "F_SETFL O_NONBLOCK failed for fd %d\n", fd);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:33:37: note: expanded from macro 'VFIO_ASSERT_NE'
33 | #define VFIO_ASSERT_NE(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, !=, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:22: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
>> tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:23:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
23 | VFIO_ASSERT_NE(vaddr, MAP_FAILED);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:33:37: note: expanded from macro 'VFIO_ASSERT_NE'
33 | #define VFIO_ASSERT_NE(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, !=, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:4: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
>> tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:23:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
23 | VFIO_ASSERT_NE(vaddr, MAP_FAILED);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:33:37: note: expanded from macro 'VFIO_ASSERT_NE'
33 | #define VFIO_ASSERT_NE(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, !=, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:22: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:35:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
35 | VFIO_ASSERT_EQ(munmap(region->vaddr, region->size), 0);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:32:37: note: expanded from macro 'VFIO_ASSERT_EQ'
32 | #define VFIO_ASSERT_EQ(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, ==, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:4: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c:35:2: warning: format specifies type 'unsigned long' but the argument has type 'u64' (aka 'unsigned long long') [-Wformat]
35 | VFIO_ASSERT_EQ(munmap(region->vaddr, region->size), 0);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:32:37: note: expanded from macro 'VFIO_ASSERT_EQ'
32 | #define VFIO_ASSERT_EQ(_a, _b, ...) VFIO_ASSERT_OP(_a, _b, ==, ##__VA_ARGS__)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/testing/selftests/vfio/lib/include/libvfio/assert.h:27:22: note: expanded from macro 'VFIO_ASSERT_OP'
26 | fprintf(stderr, " Observed: %#lx %s %#lx\n", \
| ~~~~
27 | (u64)__lhs, #_op, (u64)__rhs); \
| ^~~~~~~~~~
14 warnings generated.
vim +23 tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c
13
14 static void region_setup(struct iommu *iommu,
15 struct iova_allocator *iova_allocator,
16 struct dma_region *region, u64 size)
17 {
18 const int flags = MAP_SHARED | MAP_ANONYMOUS;
19 const int prot = PROT_READ | PROT_WRITE;
20 void *vaddr;
21
22 vaddr = mmap(NULL, size, prot, flags, -1, 0);
> 23 VFIO_ASSERT_NE(vaddr, MAP_FAILED);
24
25 region->vaddr = vaddr;
26 region->iova = iova_allocator_alloc(iova_allocator, size);
27 region->size = size;
28
29 iommu_map(iommu, region);
30 }
31
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.