[RFC PATCH v1 0/8] iommu: Add contiguous MSI mapping support

Andrew Jones posted 8 patches 1 month ago
drivers/iommu/dma-iommu.c               | 116 ++++++---
drivers/iommu/dma-iommu.h               |  13 +-
drivers/iommu/iommu-priv.h              |   7 +-
drivers/iommu/iommu.c                   | 109 ++++++--
drivers/iommu/iommufd/device.c          |  20 +-
drivers/iommu/iommufd/driver.c          | 315 ++++++++++++++++++++----
drivers/iommu/iommufd/hw_pagetable.c    |   1 +
drivers/iommu/iommufd/io_pagetable.c    |   9 +-
drivers/iommu/iommufd/iommufd_private.h |  51 +++-
include/linux/iommu.h                   |   8 +
10 files changed, 528 insertions(+), 121 deletions(-)
[RFC PATCH v1 0/8] iommu: Add contiguous MSI mapping support
Posted by Andrew Jones 1 month ago
The RISC-V IOMMU MSI remapping series [1] needs to prepare mappings for
every possible IMSIC target before MSI composition may occur in atomic
context. The previous approach mapped each physical address separately
and kept a driver-owned PA-to-IOVA lookup table.

Jason Gunthorpe suggested passing the complete list of physical
addresses through the existing MSI preparation flow. This series
implements that idea as iommu_dma_prepare_msi_list(), which maps the
ordered list into one contiguous IOVA range and records its base and
granule shift in the MSI descriptor. The interrupt-remapping driver can
then derive the IOVA for each list entry without maintaining a separate
IOVA array.

Both DMA-IOMMU and iommufd backends are extended to support these list
mappings. The existing per-page mapping objects are retained, while
the first object identifies the complete range so identical lists can
reuse an existing mapping. iommufd allocates and installs a complete
range atomically and grows its software-MSI bitmaps beyond their
previous 64-entry limit.

This is being posted separately from the RISC-V MSI remapping work for
early RFC review of the common DMA-IOMMU and iommufd interfaces. The
RISC-V adaptation to iommu_dma_prepare_msi_list() is still in progress
and will be posted later.

LLM-based coding assistants were used during development for code
exploration, patch review, test execution, and drafting and editing
commit messages and this cover letter. I reviewed and finalized all
resulting code and text. Per-patch Assisted-by tags are omitted in
light of the ongoing discussion about simplifying coding-assistant
attribution[2].

Thanks,
drew

[1] https://lore.kernel.org/all/20260820214150.545737-1-andrew.jones@oss.qualcomm.com/
[2] https://lore.kernel.org/all/20260701-work-coding-assistants-v1-1-a20a94d1d606@kernel.org/

Andrew Jones (8):
  iommu/dma: Prepare MSI physical address lists
  iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap
  iommufd: Split software MSI map lookup and allocation
  iommufd: Bound software MSI mappings to the reserved range
  iommufd: Prepare software MSI maps for address lists
  iommufd: Install software MSI map ranges atomically
  iommufd: Prepare software MSI installation for address lists
  iommu/dma: Introduce iommu_dma_prepare_msi_list()

 drivers/iommu/dma-iommu.c               | 116 ++++++---
 drivers/iommu/dma-iommu.h               |  13 +-
 drivers/iommu/iommu-priv.h              |   7 +-
 drivers/iommu/iommu.c                   | 109 ++++++--
 drivers/iommu/iommufd/device.c          |  20 +-
 drivers/iommu/iommufd/driver.c          | 315 ++++++++++++++++++++----
 drivers/iommu/iommufd/hw_pagetable.c    |   1 +
 drivers/iommu/iommufd/io_pagetable.c    |   9 +-
 drivers/iommu/iommufd/iommufd_private.h |  51 +++-
 include/linux/iommu.h                   |   8 +
 10 files changed, 528 insertions(+), 121 deletions(-)

-- 
2.43.0
Re: [RFC PATCH v1 0/8] iommu: Add contiguous MSI mapping support
Posted by Andrew Jones 1 month ago
On Wed, Aug 26, 2026 at 04:52:54PM +0200, Andrew Jones wrote:
> The RISC-V IOMMU MSI remapping series [1] needs to prepare mappings for
> every possible IMSIC target before MSI composition may occur in atomic
> context. The previous approach mapped each physical address separately
> and kept a driver-owned PA-to-IOVA lookup table.
> 
> Jason Gunthorpe suggested passing the complete list of physical
> addresses through the existing MSI preparation flow. This series
> implements that idea as iommu_dma_prepare_msi_list(), which maps the
> ordered list into one contiguous IOVA range and records its base and
> granule shift in the MSI descriptor. The interrupt-remapping driver can
> then derive the IOVA for each list entry without maintaining a separate
> IOVA array.
> 
> Both DMA-IOMMU and iommufd backends are extended to support these list
> mappings. The existing per-page mapping objects are retained, while
> the first object identifies the complete range so identical lists can
> reuse an existing mapping. iommufd allocates and installs a complete
> range atomically and grows its software-MSI bitmaps beyond their
> previous 64-entry limit.
> 
> This is being posted separately from the RISC-V MSI remapping work for
> early RFC review of the common DMA-IOMMU and iommufd interfaces. The
> RISC-V adaptation to iommu_dma_prepare_msi_list() is still in progress
> and will be posted later.
>

Applying this to riscv makes the MSI remapping surprisingly (to me) simple
and unintrusive (IMHO) to the IMSIC driver. Only the changes below were
needed.

I'll post this series along with the imsic and kconfig changes as v5 of
the riscv host msi remapping series without the RFC tag.

Thanks,
drew


diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 42f2278a702d..c7cda52b21eb 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -656,6 +656,7 @@ config RISCV_IMSIC
 	select IRQ_DOMAIN_HIERARCHY
 	select GENERIC_IRQ_MATRIX_ALLOCATOR
 	select GENERIC_MSI_IRQ
+	select IRQ_MSI_IOMMU
 	select IRQ_MSI_LIB
 
 config RISCV_RPMI_SYSMSI
diff --git a/drivers/irqchip/irq-riscv-imsic-platform.c b/drivers/irqchip/irq-riscv-imsic-platform.c
index 643c8e459611..72f595c0d320 100644
--- a/drivers/irqchip/irq-riscv-imsic-platform.c
+++ b/drivers/irqchip/irq-riscv-imsic-platform.c
@@ -10,6 +10,7 @@
 #include <linux/cpu.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/iommu.h>
 #include <linux/irq.h>
 #include <linux/irqchip.h>
 #include <linux/irqdomain.h>
@@ -69,8 +70,10 @@ static void imsic_irq_ack(struct irq_data *d)
 	irq_move_irq(d);
 }
 
-static void imsic_irq_compose_vector_msg(struct imsic_vector *vec, struct msi_msg *msg)
+static void imsic_irq_compose_vector_msg(struct irq_data *d, struct imsic_vector *vec,
+					 struct msi_msg *msg)
 {
+	struct msi_desc *desc = irq_data_get_msi_desc(d);
 	phys_addr_t msi_addr;
 
 	if (WARN_ON(!vec))
@@ -79,6 +82,14 @@ static void imsic_irq_compose_vector_msg(struct imsic_vector *vec, struct msi_ms
 	if (WARN_ON(!imsic_cpu_page_phys(vec->cpu, 0, &msi_addr)))
 		return;
 
+	if (desc->iommu_msi_shift) {
+		const struct imsic_local_config *local;
+
+		local = per_cpu_ptr(imsic->global.local, vec->cpu);
+		msi_addr = (desc->iommu_msi_iova << desc->iommu_msi_shift) +
+			   local->smode_msi_pa_index * IMSIC_MMIO_PAGE_SZ;
+	}
+
 	msg->address_hi = upper_32_bits(msi_addr);
 	msg->address_lo = lower_32_bits(msi_addr);
 	msg->data = vec->local_id;
@@ -86,7 +97,7 @@ static void imsic_irq_compose_vector_msg(struct imsic_vector *vec, struct msi_ms
 
 static void imsic_irq_compose_msg(struct irq_data *d, struct msi_msg *msg)
 {
-	imsic_irq_compose_vector_msg(irq_data_get_irq_chip_data(d), msg);
+	imsic_irq_compose_vector_msg(d, irq_data_get_irq_chip_data(d), msg);
 }
 
 #ifdef CONFIG_SMP
@@ -94,7 +105,7 @@ static void imsic_msi_update_msg(struct irq_data *d, struct imsic_vector *vec)
 {
 	struct msi_msg msg = { };
 
-	imsic_irq_compose_vector_msg(vec, &msg);
+	imsic_irq_compose_vector_msg(d, vec, &msg);
 	irq_data_get_irq_chip(d)->irq_write_msi_msg(d, &msg);
 }
 
@@ -225,7 +236,9 @@ static struct irq_chip imsic_irq_base_chip = {
 static int imsic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 				  unsigned int nr_irqs, void *args)
 {
+	msi_alloc_info_t *info = args;
 	struct imsic_vector *vec;
+	int ret;
 
 	/* Multi-MSI is not supported yet. */
 	if (nr_irqs > 1)
@@ -235,6 +248,13 @@ static int imsic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
 	if (!vec)
 		return -ENOSPC;
 
+	ret = iommu_dma_prepare_msi_list(info->desc, imsic->smode_msi_pa,
+					 imsic->nr_smode_msi_pa, IMSIC_MMIO_PAGE_SZ);
+	if (ret) {
+		imsic_vector_free(vec);
+		return ret;
+	}
+
 	irq_domain_set_info(domain, virq, virq, &imsic_irq_base_chip, vec,
 			    handle_edge_irq, NULL, NULL);
 	irq_set_noprobe(virq);
diff --git a/drivers/irqchip/irq-riscv-imsic-state.c b/drivers/irqchip/irq-riscv-imsic-state.c
index b8d1bbbf42f7..082522081a2d 100644
--- a/drivers/irqchip/irq-riscv-imsic-state.c
+++ b/drivers/irqchip/irq-riscv-imsic-state.c
@@ -687,6 +687,32 @@ static int __init imsic_get_mmio_resource(struct fwnode_handle *fwnode,
 	return of_address_to_resource(to_of_node(fwnode), index, res);
 }
 
+static int __init imsic_init_smode_msi_pa(void)
+{
+	struct imsic_global_config *global = &imsic->global;
+	phys_addr_t *smode_msi_pa;
+	unsigned int cpu, index = 0;
+
+	smode_msi_pa = kcalloc(num_possible_cpus(), sizeof(*smode_msi_pa), GFP_KERNEL);
+	if (!smode_msi_pa)
+		return -ENOMEM;
+
+	for_each_possible_cpu(cpu) {
+		struct imsic_local_config *local = per_cpu_ptr(global->local, cpu);
+
+		if (!local->msi_pa)
+			continue;
+
+		local->smode_msi_pa_index = index;
+		smode_msi_pa[index] = local->msi_pa;
+		index++;
+	}
+
+	imsic->smode_msi_pa = smode_msi_pa;
+	imsic->nr_smode_msi_pa = index;
+	return 0;
+}
+
 static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
 				     struct imsic_global_config *global,
 				     u32 *nr_parent_irqs,
@@ -937,6 +963,12 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque)
 		goto out_local_cleanup;
 	}
 
+	rc = imsic_init_smode_msi_pa();
+	if (rc) {
+		pr_err("%pfwP: failed to initialize S-mode MSI addresses\n", fwnode);
+		goto out_local_cleanup;
+	}
+
 	/* Initialize matrix allocator */
 	rc = imsic_matrix_init();
 	if (rc) {
@@ -962,6 +994,7 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque)
 out_free_local:
 	free_percpu(imsic->global.local);
 out_free_priv:
+	kfree(imsic->smode_msi_pa);
 	kfree(imsic);
 	imsic = NULL;
 	return rc;
diff --git a/drivers/irqchip/irq-riscv-imsic-state.h b/drivers/irqchip/irq-riscv-imsic-state.h
index c42ee180b305..7bc519340b12 100644
--- a/drivers/irqchip/irq-riscv-imsic-state.h
+++ b/drivers/irqchip/irq-riscv-imsic-state.h
@@ -49,6 +49,8 @@ struct imsic_priv {
 
 	/* Global configuration common for all HARTs */
 	struct imsic_global_config		global;
+	phys_addr_t				*smode_msi_pa;
+	unsigned int				nr_smode_msi_pa;
 
 	/* Per-CPU state */
 	struct imsic_local_priv __percpu	*lpriv;
diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
index ce8fe1ead7a0..523022e0a144 100644
--- a/include/linux/irqchip/riscv-imsic.h
+++ b/include/linux/irqchip/riscv-imsic.h
@@ -40,6 +40,7 @@
 struct imsic_local_config {
 	phys_addr_t				msi_pa;
 	void __iomem				*msi_va;
+	unsigned int				smode_msi_pa_index;
 
 	/* Number of guest interrupt files per-HART */
 	u32					nr_guest_files;