[PATCH v2 11/14] iommu/riscv: Copy MSI IOVA table when replacing an iommufd domain

Andrew Jones andrew.jones at oss.qualcomm.com
Fri Jul 24 08:12:15 PDT 2026


iommufd may replace one paging domain with another on a device while
leaving the device otherwise operational, e.g. when converting a
device's HWPT. If the old domain already had a populated MSI IOVA
table then the new domain must inherit that table rather than wait
to build its own, since the MSI IOVAs are only built on the next
irq_domain_alloc_irqs(), which does not happen on a domain
replacement.

Only copy from an old domain that is a genuine RISC-V paging domain;
iommu.c already has riscv_iommu_paging_domain_ops in scope to check
this, so do the check there and pass NULL down on a mismatch. This
keeps riscv_iommu_ir_attach_paging_domain() simple: a non-NULL old is
always safe to cast with iommu_domain_to_riscv().

Only iommufd cookie domains are copied from and to, since VFIO type1
and DMA API domains build their own tables from irq_domain_alloc_irqs()
before any device is attached, and are never replaced while a device is
live.

Signed-off-by: Andrew Jones <andrew.jones at oss.qualcomm.com>
---
 drivers/iommu/riscv/iommu-ir.c | 29 +++++++++++++++++++++++++++++
 drivers/iommu/riscv/iommu.c    |  5 ++++-
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index 39ca4c89a2a3..bb1bc4c2bcec 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -216,6 +216,35 @@ void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_inf
 int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
 					struct iommu_domain *old)
 {
+	struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
+	struct riscv_iommu_domain *old_domain = old ? iommu_domain_to_riscv(old) : NULL;
+	dma_addr_t *msi_iova;
+
+	if (!old_domain)
+		return 0;
+
+	if (old_domain->domain.cookie_type != IOMMU_COOKIE_IOMMUFD ||
+	    iommu_domain->cookie_type != IOMMU_COOKIE_IOMMUFD)
+		return 0;
+
+	scoped_guard(mutex, &old_domain->mutex) {
+		if (!old_domain->msi_iova)
+			return 0;
+
+		msi_iova = kmemdup(old_domain->msi_iova,
+				   riscv_iommu_ir_msi_iova_count() * sizeof(*msi_iova),
+				   GFP_KERNEL);
+		if (!msi_iova)
+			return -ENOMEM;
+	}
+
+	guard(mutex)(&domain->mutex);
+
+	if (domain->msi_iova)
+		kfree(msi_iova);
+	else
+		domain->msi_iova = msi_iova;
+
 	return 0;
 }
 
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 5631af2c9ab1..d3b5d985887e 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1264,11 +1264,14 @@ static bool riscv_iommu_pt_supported(struct riscv_iommu_device *iommu, int pgd_m
 	return false;
 }
 
+static const struct iommu_domain_ops riscv_iommu_paging_domain_ops;
+
 static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
 					    struct device *dev,
 					    struct iommu_domain *old)
 {
 	struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
+	bool old_is_paging = old && old->ops == &riscv_iommu_paging_domain_ops;
 	struct riscv_iommu_device *iommu = dev_to_iommu(dev);
 	struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
 	struct pt_iommu_riscv_64_hw_info pt_info;
@@ -1288,7 +1291,7 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
 	if (riscv_iommu_bond_link(domain, dev))
 		return -ENOMEM;
 
-	ret = riscv_iommu_ir_attach_paging_domain(iommu_domain, dev, old);
+	ret = riscv_iommu_ir_attach_paging_domain(iommu_domain, dev, old_is_paging ? old : NULL);
 	if (ret) {
 		riscv_iommu_bond_unlink(domain, dev);
 		return ret;
-- 
2.43.0




More information about the linux-riscv mailing list