[PATCH v4 15/21] iommu/riscv: Remap IMSIC targets during MSI composition
From: Andrew Jones
Date: Thu Aug 20 2026 - 17:45:04 EST
Translate composed IMSIC target addresses through the attached paging
domain's pre-mapped IOVA table. This keeps affinity updates on the
interrupt hierarchy while directing device MSI writes through the IOMMU.
MSI composition may run in atomic context, so publish each fully built
table with release ordering and consume it with acquire ordering under
RCU. This permits lockless lookup without exposing incomplete table
state.
When no translated table is available, retain the physical IMSIC target
for domains that do not use MSI address translation.
Signed-off-by: Andrew Jones <andrew.jones@xxxxxxxxxxxxxxxx>
---
drivers/iommu/riscv/iommu-ir.c | 41 +++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index f61b65f29888..dacaefa62859 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -99,7 +99,8 @@ static int riscv_iommu_ir_build_msi_iova(struct riscv_iommu_domain *domain, stru
}
}
- domain->msi_iova = msi_iova;
+ /* Pair with smp_load_acquire() in riscv_iommu_ir_compose_msi_msg() */
+ smp_store_release(&domain->msi_iova, msi_iova);
return 0;
@@ -108,12 +109,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 smp_store_release() in riscv_iommu_ir_build_msi_iova() */
+ msi_iova = smp_load_acquire(&domain->msi_iova);
+ if (!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,
--
2.43.0