* [RFC PATCH 1/3] vfio: selftests: Add support of creating iommus from iommufd
2026-09-25 1:13 [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries Samiullah Khawaja
@ 2026-09-25 1:13 ` Samiullah Khawaja
2026-09-25 1:13 ` [RFC PATCH 2/3] vfio: selftests: Add iommufd hwpt replace test Samiullah Khawaja
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Samiullah Khawaja @ 2026-09-25 1:13 UTC (permalink / raw)
To: Joerg Roedel, Will Deacon, Jason Gunthorpe
Cc: Samiullah Khawaja, Robin Murphy, Kevin Tian, Alex Williamson,
iommu, linux-arm-kernel, linux-kernel, Pasha Tatashin,
David Matlack, Lu Baolu, Pranjal Shrivastava
Add API to init a struct iommu using an already opened iommufd instance
and attach devices to it.
Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
---
.../vfio/lib/include/libvfio/iommu.h | 2 +
.../lib/include/libvfio/vfio_pci_device.h | 2 +
tools/testing/selftests/vfio/lib/iommu.c | 60 +++++++++++++++++--
.../selftests/vfio/lib/vfio_pci_device.c | 25 ++++++--
4 files changed, 80 insertions(+), 9 deletions(-)
diff --git a/tools/testing/selftests/vfio/lib/include/libvfio/iommu.h b/tools/testing/selftests/vfio/lib/include/libvfio/iommu.h
index e9a3386a4719..62f1e8affd2b 100644
--- a/tools/testing/selftests/vfio/lib/include/libvfio/iommu.h
+++ b/tools/testing/selftests/vfio/lib/include/libvfio/iommu.h
@@ -29,10 +29,12 @@ struct iommu {
int container_fd;
int iommufd;
u32 ioas_id;
+ u32 hwpt_id;
struct list_head dma_regions;
};
struct iommu *iommu_init(const char *iommu_mode);
+struct iommu *iommufd_iommu_init(int iommufd, u32 dev_id);
void iommu_cleanup(struct iommu *iommu);
int __iommu_map(struct iommu *iommu, struct dma_region *region);
diff --git a/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h b/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h
index e19bd94b8dd2..4dcc23bc9b78 100644
--- a/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h
+++ b/tools/testing/selftests/vfio/lib/include/libvfio/vfio_pci_device.h
@@ -19,6 +19,7 @@ struct vfio_pci_device {
const char *bdf;
int fd;
int group_fd;
+ u32 dev_id;
struct iommu *iommu;
@@ -82,6 +83,7 @@ static inline void vfio_pci_cmd_clear(struct vfio_pci_device *device, u16 bits)
vfio_pci_config_writew(device, PCI_COMMAND, cmd & ~bits);
}
+void vfio_pci_device_attach_iommu(struct vfio_pci_device *device, struct iommu *iommu);
void vfio_pci_irq_enable(struct vfio_pci_device *device, u32 index,
u32 vector, int count);
void vfio_pci_irq_disable(struct vfio_pci_device *device, u32 index);
diff --git a/tools/testing/selftests/vfio/lib/iommu.c b/tools/testing/selftests/vfio/lib/iommu.c
index b6f3c5c84e01..6d2d5c82813c 100644
--- a/tools/testing/selftests/vfio/lib/iommu.c
+++ b/tools/testing/selftests/vfio/lib/iommu.c
@@ -405,6 +405,18 @@ struct iommu_iova_range *iommu_iova_ranges(struct iommu *iommu, u32 *nranges)
return ranges;
}
+static u32 iommufd_hwpt_alloc(struct iommu *iommu, u32 dev_id)
+{
+ struct iommu_hwpt_alloc args = {
+ .size = sizeof(args),
+ .pt_id = iommu->ioas_id,
+ .dev_id = dev_id,
+ };
+
+ ioctl_assert(iommu->iommufd, IOMMU_HWPT_ALLOC, &args);
+ return args.out_hwpt_id;
+}
+
static u32 iommufd_ioas_alloc(int iommufd)
{
struct iommu_ioas_alloc args = {
@@ -415,17 +427,25 @@ static u32 iommufd_ioas_alloc(int iommufd)
return args.out_ioas_id;
}
-struct iommu *iommu_init(const char *iommu_mode)
+static struct iommu *iommu_alloc(const char *iommu_mode)
{
- const char *container_path;
struct iommu *iommu;
- int version;
iommu = calloc_assert(1, sizeof(*iommu));
INIT_LIST_HEAD(&iommu->dma_regions);
iommu->mode = lookup_iommu_mode(iommu_mode);
+ return iommu;
+}
+
+struct iommu *iommu_init(const char *iommu_mode)
+{
+ const char *container_path;
+ struct iommu *iommu;
+ int version;
+
+ iommu = iommu_alloc(iommu_mode);
container_path = iommu->mode->container_path;
if (container_path) {
@@ -449,10 +469,42 @@ struct iommu *iommu_init(const char *iommu_mode)
return iommu;
}
+struct iommu *iommufd_iommu_init(int iommufd, u32 dev_id)
+{
+ struct iommu *iommu;
+
+ iommu = iommu_alloc("iommufd");
+
+ iommu->iommufd = dup(iommufd);
+ VFIO_ASSERT_GT(iommu->iommufd, 0);
+
+ iommu->ioas_id = iommufd_ioas_alloc(iommu->iommufd);
+ iommu->hwpt_id = iommufd_hwpt_alloc(iommu, dev_id);
+
+ return iommu;
+}
+
+static void iommufd_cleanup(struct iommu *iommu)
+{
+ struct iommu_destroy args = {
+ .size = sizeof(args),
+ };
+
+ if (iommu->hwpt_id) {
+ args.id = iommu->hwpt_id;
+ ioctl_assert(iommu->iommufd, IOMMU_DESTROY, &args);
+ }
+
+ args.id = iommu->ioas_id;
+ ioctl_assert(iommu->iommufd, IOMMU_DESTROY, &args);
+
+ VFIO_ASSERT_EQ(close(iommu->iommufd), 0);
+}
+
void iommu_cleanup(struct iommu *iommu)
{
if (iommu->iommufd)
- VFIO_ASSERT_EQ(close(iommu->iommufd), 0);
+ iommufd_cleanup(iommu);
else
VFIO_ASSERT_EQ(close(iommu->container_fd), 0);
diff --git a/tools/testing/selftests/vfio/lib/vfio_pci_device.c b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
index 774d1e90ec0e..81d3879d064f 100644
--- a/tools/testing/selftests/vfio/lib/vfio_pci_device.c
+++ b/tools/testing/selftests/vfio/lib/vfio_pci_device.c
@@ -469,15 +469,15 @@ int __vfio_device_bind_iommufd(int device_fd, int iommufd, const char *vf_token)
if (ioctl(device_fd, VFIO_DEVICE_BIND_IOMMUFD, &args))
return -errno;
- return 0;
+ return args.out_devid;
}
-static void vfio_device_bind_iommufd(int device_fd, int iommufd,
- const char *vf_token)
+int vfio_device_bind_iommufd(int device_fd, int iommufd, const char *vf_token)
{
int ret = __vfio_device_bind_iommufd(device_fd, iommufd, vf_token);
- VFIO_ASSERT_EQ(ret, 0, "Failed VFIO_DEVICE_BIND_IOMMUFD ioctl\n");
+ VFIO_ASSERT_GE(ret, 0, "Failed VFIO_DEVICE_BIND_IOMMUFD ioctl\n");
+ return ret;
}
static void vfio_device_attach_iommufd_pt(int device_fd, u32 pt_id)
@@ -503,10 +503,25 @@ static void vfio_pci_iommufd_setup(struct vfio_pci_device *device,
const char *bdf, const char *vf_token)
{
vfio_pci_cdev_open(device, bdf);
- vfio_device_bind_iommufd(device->fd, device->iommu->iommufd, vf_token);
+ device->dev_id = vfio_device_bind_iommufd(device->fd, device->iommu->iommufd, vf_token);
vfio_device_attach_iommufd_pt(device->fd, device->iommu->ioas_id);
}
+void vfio_pci_device_attach_iommu(struct vfio_pci_device *device, struct iommu *iommu)
+{
+ u32 pt_id = iommu->ioas_id;
+
+ /* Only iommufd supports changing struct iommu attachments */
+ VFIO_ASSERT_TRUE(iommu->iommufd);
+
+ if (iommu->hwpt_id)
+ pt_id = iommu->hwpt_id;
+
+ VFIO_ASSERT_NE(pt_id, 0);
+ vfio_device_attach_iommufd_pt(device->fd, pt_id);
+ device->iommu = iommu;
+}
+
struct vfio_pci_device *vfio_pci_device_alloc(const char *bdf, struct iommu *iommu)
{
struct vfio_pci_device *device;
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 6+ messages in thread* [RFC PATCH 2/3] vfio: selftests: Add iommufd hwpt replace test
2026-09-25 1:13 [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries Samiullah Khawaja
2026-09-25 1:13 ` [RFC PATCH 1/3] vfio: selftests: Add support of creating iommus from iommufd Samiullah Khawaja
@ 2026-09-25 1:13 ` Samiullah Khawaja
2026-09-25 1:13 ` [RFC PATCH 3/3] iommu/arm-smmu-v3: Add support for hitless replace of S1 domains Samiullah Khawaja
2026-09-25 21:44 ` [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries David Matlack
3 siblings, 0 replies; 6+ messages in thread
From: Samiullah Khawaja @ 2026-09-25 1:13 UTC (permalink / raw)
To: Joerg Roedel, Will Deacon, Jason Gunthorpe
Cc: Samiullah Khawaja, Robin Murphy, Kevin Tian, Alex Williamson,
iommu, linux-arm-kernel, linux-kernel, Pasha Tatashin,
David Matlack, Lu Baolu, Pranjal Shrivastava
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 | 171 ++++++++++++++++++
2 files changed, 172 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 2c32c48db509..01d0e808871b 100644
--- a/tools/testing/selftests/vfio/Makefile
+++ b/tools/testing/selftests/vfio/Makefile
@@ -8,6 +8,7 @@ else
CFLAGS = $(KHDR_INCLUDES)
TEST_GEN_PROGS += vfio_dma_mapping_test
TEST_GEN_PROGS += vfio_dma_mapping_mmio_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..3ec243aae626
--- /dev/null
+++ b/tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <string.h>
+#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;
+
+ 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_2M);
+ 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) {
+ u32 attaches;
+};
+
+FIXTURE_VARIANT_ADD(vfio_iommufd_replace_hwpt_test, domain_replace) {
+ .attaches = 1000,
+};
+
+FIXTURE_VARIANT_ADD(vfio_iommufd_replace_hwpt_test, noreplace) {
+ .attaches = 0,
+};
+
+TEST_F_TIMEOUT(vfio_iommufd_replace_hwpt_test, memcpy, 120)
+{
+ struct dma_region memcpy_region = {}, driver_region = {};
+ struct vfio_pci_driver *driver = &self->device->driver;
+ struct iommu *iommu2 = NULL;
+ u64 size = 0;
+ int ret;
+ u32 i;
+
+ if (variant->attaches) {
+ 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);
+ }
+
+ if (driver->ops) {
+ memset(self->src, 'x', self->size);
+ memset(self->dst, 'y', self->size);
+
+ size = min_t(u64, driver->max_memcpy_size, self->size);
+
+ /*
+ * Fill the device's queue. memcpy_start() returns with
+ * max_memcpy_count transfers still outstanding.
+ */
+ vfio_pci_driver_memcpy_start(self->device,
+ self->src_iova,
+ self->dst_iova,
+ size, driver->max_memcpy_count);
+ }
+
+ /*
+ * Replace the domain repeatedly while that transfer is in flight. Since
+ * domain replacement is a race with the translation requests, it needs
+ * to be done multiple times to capture the small window where iommu
+ * faults can occur.
+ *
+ * The loop ends on the original domain, so the teardown below is safe.
+ */
+ for (i = 0; i < variant->attaches; i++) {
+ vfio_pci_device_attach_iommu(self->device, iommu2);
+ vfio_pci_device_attach_iommu(self->device, self->iommu);
+ }
+
+ if (driver->ops) {
+ ret = vfio_pci_driver_memcpy_wait(self->device);
+
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(0, memcmp(self->src, self->dst, size));
+ }
+
+ if (iommu2) {
+ 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.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 6+ messages in thread* [RFC PATCH 3/3] iommu/arm-smmu-v3: Add support for hitless replace of S1 domains
2026-09-25 1:13 [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries Samiullah Khawaja
2026-09-25 1:13 ` [RFC PATCH 1/3] vfio: selftests: Add support of creating iommus from iommufd Samiullah Khawaja
2026-09-25 1:13 ` [RFC PATCH 2/3] vfio: selftests: Add iommufd hwpt replace test Samiullah Khawaja
@ 2026-09-25 1:13 ` Samiullah Khawaja
2026-09-25 21:44 ` [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries David Matlack
3 siblings, 0 replies; 6+ messages in thread
From: Samiullah Khawaja @ 2026-09-25 1:13 UTC (permalink / raw)
To: Joerg Roedel, Will Deacon, Jason Gunthorpe
Cc: Samiullah Khawaja, Robin Murphy, Kevin Tian, Alex Williamson,
iommu, linux-arm-kernel, linux-kernel, Pasha Tatashin,
David Matlack, Lu Baolu, Pranjal Shrivastava
S1 domains are programmed by CD entries in the CD table. During a new
domain attach these are updated using write_cd_entry. Since ASID and
TTB0 are in different 64-bit words, the write_cd_entry considers it a
non-hitless update and sets V=0. This means such an update is disruptive
and would generate translation faults if there are ongoing DMAs.
Replace the CD entries by using an unused temporary ASID in following
sequence to allow hitless replacement of CD entry,
- Update the CD entry with temporary ASID.
- Update the CD entry with the target TTB0.
- Update the CD entry with the target ASID.
Signed-off-by: Samiullah Khawaja <skhawaja@google.com>
---
drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 66 ++++++++++++++++++++-
1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
index 5732f3ba0122..59f4b8cf89f6 100644
--- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
+++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
@@ -1641,6 +1641,68 @@ void arm_smmu_write_cd_entry(struct arm_smmu_master *master, int ssid,
arm_smmu_write_entry(&cd_writer.writer, cdptr->data, target->data);
}
+/* Invalidate every stage-1 TLB entry tagged with @asid. */
+static void arm_smmu_tlb_inv_asid(struct arm_smmu_device *smmu, u16 asid)
+{
+ enum arm_smmu_cmdq_opcode op = (smmu->features & ARM_SMMU_FEAT_E2H) ?
+ CMDQ_OP_TLBI_EL2_ASID :
+ CMDQ_OP_TLBI_NH_ASID;
+
+ arm_smmu_cmdq_issue_cmd_with_sync(smmu,
+ arm_smmu_make_cmd_tlbi(op, asid, 0));
+}
+
+static void arm_smmu_cd_set_asid(struct arm_smmu_cd *cd, u16 asid)
+{
+ cd->data[0] &= ~cpu_to_le64(CTXDESC_CD_0_ASID);
+ cd->data[0] |= cpu_to_le64(FIELD_PREP(CTXDESC_CD_0_ASID, asid));
+}
+
+static void arm_smmu_replace_cd_entry(struct arm_smmu_master *master, int ssid,
+ struct arm_smmu_cd *cdptr,
+ const struct arm_smmu_cd *target)
+{
+ struct arm_smmu_device *smmu = master->smmu;
+ struct arm_smmu_cd stage;
+ u32 tmp_asid;
+
+ lockdep_assert_held(&arm_smmu_asid_lock);
+
+ /*
+ * Nothing is translating through an invalid CD, and an update that
+ * invalidates one cannot be made hitless anyway.
+ */
+ if (!(cdptr->data[0] & cpu_to_le64(CTXDESC_CD_0_V)) ||
+ !(target->data[0] & cpu_to_le64(CTXDESC_CD_0_V)))
+ goto write_directly;
+
+ if (xa_alloc(&arm_smmu_asid_xa, &tmp_asid, XA_ZERO_ENTRY,
+ XA_LIMIT(1, (1 << smmu->asid_bits) - 1), GFP_KERNEL))
+ goto write_directly;
+
+ /* Copy the existing cd entry and update only the ASID. */
+ stage = *cdptr;
+ arm_smmu_cd_set_asid(&stage, tmp_asid);
+ arm_smmu_write_cd_entry(master, ssid, cdptr, &stage);
+
+ /* Copy the target cd entry and update ASID to the temporary ASID. */
+ stage = *target;
+ arm_smmu_cd_set_asid(&stage, tmp_asid);
+ arm_smmu_write_cd_entry(master, ssid, cdptr, &stage);
+
+ arm_smmu_tlb_inv_asid(smmu, tmp_asid);
+
+ /* Update to the target cd entry as it should be hitless now. */
+ arm_smmu_write_cd_entry(master, ssid, cdptr, target);
+
+ arm_smmu_tlb_inv_asid(smmu, tmp_asid);
+ xa_erase(&arm_smmu_asid_xa, tmp_asid);
+ return;
+
+write_directly:
+ arm_smmu_write_cd_entry(master, ssid, cdptr, target);
+}
+
void arm_smmu_make_s1_cd(struct arm_smmu_cd *target,
struct arm_smmu_master *master,
struct arm_smmu_domain *smmu_domain)
@@ -3664,8 +3726,8 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev,
struct arm_smmu_cd target_cd;
arm_smmu_make_s1_cd(&target_cd, master, smmu_domain);
- arm_smmu_write_cd_entry(master, IOMMU_NO_PASID, cdptr,
- &target_cd);
+ arm_smmu_replace_cd_entry(master, IOMMU_NO_PASID, cdptr,
+ &target_cd);
arm_smmu_make_cdtable_ste(&target, master, state.ats_enabled,
STRTAB_STE_1_S1DSS_SSID0);
arm_smmu_install_ste_for_dev(master, &target);
--
2.56.0.rc1.315.gc6ed9934b7-goog
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries
2026-09-25 1:13 [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries Samiullah Khawaja
` (2 preceding siblings ...)
2026-09-25 1:13 ` [RFC PATCH 3/3] iommu/arm-smmu-v3: Add support for hitless replace of S1 domains Samiullah Khawaja
@ 2026-09-25 21:44 ` David Matlack
2026-09-25 22:27 ` Samiullah Khawaja
3 siblings, 1 reply; 6+ messages in thread
From: David Matlack @ 2026-09-25 21:44 UTC (permalink / raw)
To: Samiullah Khawaja
Cc: Joerg Roedel, Will Deacon, Jason Gunthorpe, Robin Murphy,
Kevin Tian, Alex Williamson, iommu, linux-arm-kernel,
linux-kernel, Pasha Tatashin, Lu Baolu, Pranjal Shrivastava
On 2026-09-25 01:13 AM, Samiullah Khawaja wrote:
> This patch series adds support for replacing IOMMU domains with S1 page
> tables hitlessly, that is without disrupting the ongoing DMAs. This is
> one of the dependencies of the Liveupdate IOMMU support for Arm SMMUv3.
>
> S1 domains are programmed by CD entries in the CD table. During a new
> domain attach these are updated using arm_smmu_write_cd_entry(). Since
> ASID and TTB0 are in different 64-bit words, arm_smmu_write_cd_entry()
> considers it a non-hitless update and sets V=0. This means such an
> update is disruptive and would generate translation faults if there are
> ongoing DMAs.
>
> Replace the CD entries by using an unused temporary ASID in the
> following sequence to allow hitless replacement of CD entry,
>
> - Update the CD entry with temporary ASID.
> - Update the CD entry with the target TTB0.
> - Invalidate the temporary ASID.
> - Update the CD entry with target ASID.
> - Invalidate and release the temporary ASID.
>
> The series adds a vfio selftest that triggers a continuous DMA and does
> the hwpt replace while the DMA is ongoing. Since domain replacement is a
> race with the translation requests, the test does it multiple times to
> capture the small window where IOMMU faults can occur.
>
> Without this change we can observe the SMMU faults in the kernel logs
> and with it the SMMU faults do not occur.
>
> This is only tested on qemu with the emulated SMMUv3 and an out of tree
> NVMe vfio selftest driver, not on real hardware. Following are not
> covered and can be done later,
Out of curiousity, does the in-tree VFIO selftests driver for IGB not
work for this testing scenario?
^ permalink raw reply [flat|nested] 6+ messages in thread* Re: [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries
2026-09-25 21:44 ` [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries David Matlack
@ 2026-09-25 22:27 ` Samiullah Khawaja
0 siblings, 0 replies; 6+ messages in thread
From: Samiullah Khawaja @ 2026-09-25 22:27 UTC (permalink / raw)
To: David Matlack
Cc: Joerg Roedel, Will Deacon, Jason Gunthorpe, Robin Murphy,
Kevin Tian, Alex Williamson, iommu, linux-arm-kernel,
linux-kernel, Pasha Tatashin, Lu Baolu, Pranjal Shrivastava
On Fri, Sep 25, 2026 at 09:44:05PM +0000, David Matlack wrote:
>On 2026-09-25 01:13 AM, Samiullah Khawaja wrote:
>> This patch series adds support for replacing IOMMU domains with S1 page
>> tables hitlessly, that is without disrupting the ongoing DMAs. This is
>> one of the dependencies of the Liveupdate IOMMU support for Arm SMMUv3.
>>
>> S1 domains are programmed by CD entries in the CD table. During a new
>> domain attach these are updated using arm_smmu_write_cd_entry(). Since
>> ASID and TTB0 are in different 64-bit words, arm_smmu_write_cd_entry()
>> considers it a non-hitless update and sets V=0. This means such an
>> update is disruptive and would generate translation faults if there are
>> ongoing DMAs.
>>
>> Replace the CD entries by using an unused temporary ASID in the
>> following sequence to allow hitless replacement of CD entry,
>>
>> - Update the CD entry with temporary ASID.
>> - Update the CD entry with the target TTB0.
>> - Invalidate the temporary ASID.
>> - Update the CD entry with target ASID.
>> - Invalidate and release the temporary ASID.
>>
>> The series adds a vfio selftest that triggers a continuous DMA and does
>> the hwpt replace while the DMA is ongoing. Since domain replacement is a
>> race with the translation requests, the test does it multiple times to
>> capture the small window where IOMMU faults can occur.
>>
>> Without this change we can observe the SMMU faults in the kernel logs
>> and with it the SMMU faults do not occur.
>>
>> This is only tested on qemu with the emulated SMMUv3 and an out of tree
>> NVMe vfio selftest driver, not on real hardware. Following are not
>> covered and can be done later,
>
>Out of curiousity, does the in-tree VFIO selftests driver for IGB not
>work for this testing scenario?
I tried with the IGB driver first, but I couldn't get the DMAs to work.
Its probably my setup, will recheck.
Sami
^ permalink raw reply [flat|nested] 6+ messages in thread