[PATCH v2 12/14] iommu/riscv: Implement irq_compose_msi_msg for IMSIC remapping
Andrew Jones <[email protected]> Fri, 24 Jul 2026 17:12:16 +0200
| Newsgroups | dev.linux.lists.iommu,org.infradead.lists.linux-riscv,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
Wire up irq_compose_msi_msg() on the IOMMU-IR irq chip to look up the pre-mapped IOVA for the target IMSIC page and rewrite the composed message to use it, redirecting MSI writes through the IOMMU instead of landing at the raw IMSIC physical address. The lookup is O(1), since the "extract" function used to build the table's index is also used here, which is necessary since the number of IMSICs may eventually be in the hundreds and irq_compose_msi_msg() may run in atomic context. domain->msi_iova is read here without domain->mutex, since compose can run in atomic context, and RCU only protects info->domain itself. Pair smp_store_release()/smp_load_acquire() on domain->msi_iova to ensure it's observably filled before publishing its pointer. That guarantees a non-NULL domain->msi_iova observed here is safe to use. The WARN_ON_ONCE(!msi_iova) is defensive: a live paging domain should not reach compose without a populated table. Signed-off-by: Andrew Jones <[email protected]> --- drivers/iommu/riscv/iommu-ir.c | 50 +++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c index bb1bc4c2bcec..eb2b7953055e 100644 --- a/drivers/iommu/riscv/iommu-ir.c +++ b/drivers/iommu/riscv/iommu-ir.c @@ -91,7 +91,9 @@ static int riscv_iommu_ir_build_msi_iova(struct riscv_iommu_domain *domain, stru goto err_free; } - domain->msi_iova = msi_iova; + /* Pair with the smp_load_acquire() in riscv_iommu_ir_compose_msi_msg() */ + smp_store_release(&domain->msi_iova, msi_iova); + return 0; err_free: @@ -99,12 +101,50 @@ static int riscv_iommu_ir_build_msi_iova(struct riscv_iommu_domain *domain, stru return ret; } +static void riscv_iommu_ir_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct riscv_iommu_info *info = data->domain->host_data; + struct riscv_iommu_domain *domain; + dma_addr_t *msi_iova; + struct msi_desc *desc; + phys_addr_t pa; + size_t idx; + + BUG_ON(irq_chip_compose_msi_msg(data->parent_data, msg)); + + desc = irq_data_get_msi_desc(data); + if (WARN_ON_ONCE(!desc)) + return; + + guard(rcu)(); + + domain = rcu_dereference(info->domain); + if (!domain) { + msi_desc_set_iommu_msi_iova(desc, 0, 0); + return; + } + + /* Pair with the smp_store_release() in riscv_iommu_ir_build_msi_iova() */ + msi_iova = smp_load_acquire(&domain->msi_iova); + if (WARN_ON_ONCE(!msi_iova)) { + msi_desc_set_iommu_msi_iova(desc, 0, 0); + return; + } + + pa = ((u64)msg->address_hi << 32) | msg->address_lo; + idx = riscv_iommu_ir_msi_iova_idx(pa); + + msi_desc_set_iommu_msi_iova(desc, msi_iova[idx], IMSIC_MMIO_PAGE_SHIFT); + msi_msg_set_addr(desc, msg, pa); +} + static struct irq_chip riscv_iommu_ir_irq_chip = { .name = "IOMMU-IR", .irq_ack = irq_chip_ack_parent, .irq_mask = irq_chip_mask_parent, .irq_unmask = irq_chip_unmask_parent, .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_compose_msi_msg = riscv_iommu_ir_compose_msi_msg, }; static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain, @@ -240,10 +280,12 @@ int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struc guard(mutex)(&domain->mutex); - if (domain->msi_iova) + if (domain->msi_iova) { kfree(msi_iova); - else - domain->msi_iova = msi_iova; + } else { + /* Pair with the smp_load_acquire() in riscv_iommu_ir_compose_msi_msg() */ + smp_store_release(&domain->msi_iova, msi_iova); + } return 0; } -- 2.43.0