mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] iommu/arm-smmu-v3: Add support for hitless replace of CD entries
@ 2026-09-25  1:13 Samiullah Khawaja
  2026-09-25  1:13 ` [RFC PATCH 1/3] vfio: selftests: Add support of creating iommus from iommufd Samiullah Khawaja
                   ` (3 more replies)
  0 siblings, 4 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

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,

- S2 domains have the same problem, S2VMID is in STE qword 2 and S2TTB is
  in qword 3. These need the same handling with a temporary VMID.
- ATS is not tested, the test environment runs with pci=noats.

Testing:

Without Fix:

  TAP version 13
  1..2
  # Starting 2 tests from 2 test cases.
  #  RUN           vfio_iommufd_replace_hwpt_test.domain_replace.memcpy ...
  [   33.267749] vfio-pci 0000:00:0a.0: enabling device (0000 -> 0002)
  [   33.270709] vfio-pci 0000:00:0a.0: resetting
  [   33.385682] vfio-pci 0000:00:0a.0: reset done
  [   37.128021] arm-smmu-v3 9050000.smmuv3: event 0x0a received:
  [   37.131885] arm-smmu-v3 9050000.smmuv3:      0x000000500000000a
  [   37.134585] arm-smmu-v3 9050000.smmuv3:      0x0000000000000000
  [   37.136961] arm-smmu-v3 9050000.smmuv3:      0x0000000000000000
  [   37.139631] arm-smmu-v3 9050000.smmuv3:      0x0000000000000000
  [   37.142188] arm-smmu-v3 9050000.smmuv3: event: C_BAD_CD client: 0000:00:0a.0 sid: 0x50 ssid: 0x0

  ...
  ...

With Fix:

  TAP version 13
  1..2
  # Starting 2 tests from 2 test cases.
  #  RUN           vfio_iommufd_replace_hwpt_test.domain_replace.memcpy ...
  [   33.418253] vfio-pci 0000:00:0a.0: enabling device (0000 -> 0002)
  [   33.421218] vfio-pci 0000:00:0a.0: resetting
  [   33.539750] vfio-pci 0000:00:0a.0: reset done
  #            OK  vfio_iommufd_replace_hwpt_test.domain_replace.memcpy
  ok 1 vfio_iommufd_replace_hwpt_test.domain_replace.memcpy
  #  RUN           vfio_iommufd_replace_hwpt_test.noreplace.memcpy ...
  [   88.416453] vfio-pci 0000:00:0a.0: resetting
  [   88.531989] vfio-pci 0000:00:0a.0: reset done
  #            OK  vfio_iommufd_replace_hwpt_test.noreplace.memcpy
  ok 2 vfio_iommufd_replace_hwpt_test.noreplace.memcpy

Looking forward to your feedback on this.

Dependencies:
The patch series includes a patch from the vfio selftest multi iommu
series as it is required for the hwpt_replace vfio selftest. It is part
of this series since the original code doesn't apply cleanly on the
base.
https://lore.kernel.org/all/20260505221518.619123-1-skhawaja@google.com/

Related Work:
Liveupdate IOMMU patch series:
https://lore.kernel.org/all/20260921004834.2601285-1-skhawaja@google.com/
Liveupdate IOMMU feature patchset breakdown:
https://docs.google.com/document/d/1enDn-uPE9U77U-xHEnzn6HHGKiePSAtMIP8EDU3NO0M

Samiullah Khawaja (3):
  vfio: selftests: Add support of creating iommus from iommufd
  vfio: selftests: Add iommufd hwpt replace test
  iommu/arm-smmu-v3: Add support for hitless replace of S1 domains

 drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c   |  66 ++++++-
 tools/testing/selftests/vfio/Makefile         |   1 +
 .../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 ++-
 .../vfio/vfio_iommufd_hwpt_replace_test.c     | 171 ++++++++++++++++++
 7 files changed, 316 insertions(+), 11 deletions(-)
 create mode 100644 tools/testing/selftests/vfio/vfio_iommufd_hwpt_replace_test.c


base-commit: 3d7783543c2646af69ad65825e810060494bea21
-- 
2.56.0.rc1.315.gc6ed9934b7-goog


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

* [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

end of thread, other threads:[~2026-09-25 22:27 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [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
2026-09-25 22:27   ` Samiullah Khawaja

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®