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

From: Andrew Jones

Date: Thu Aug 27 2026 - 04:14:02 EST


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;