[PATCH v2 11/14] iommu/riscv: Copy MSI IOVA table when replacing an iommufd domain
Andrew Jones <[email protected]> Fri, 24 Jul 2026 17:12:15 +0200
| Newsgroups | dev.linux.lists.iommu,org.infradead.lists.linux-riscv,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- 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