[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