From: Andrew Jones <andrew.jones@oss.qualcomm.com>
To: linux-riscv@lists.infradead.org, iommu@lists.linux.dev
Cc: linux-kernel@vger.kernel.org, tomasz.jeznach@linux.dev,
tjeznach@rivosinc.com, jgg@ziepe.ca, jgg@nvidia.com,
joro@8bytes.org, will@kernel.org, robin.murphy@arm.com,
pjw@kernel.org, palmer@dabbelt.com, anup@brainfault.org,
tglx@kernel.org, kevin.tian@intel.com,
fangyu.yu@linux.alibaba.com
Subject: [PATCH v3 10/19] iommu/riscv: Pre-map IMSIC MSI targets
Date: Fri, 7 Aug 2026 20:17:04 +0200 [thread overview]
Message-ID: <20260807181713.228535-11-andrew.jones@oss.qualcomm.com> (raw)
In-Reply-To: <20260807181713.228535-1-andrew.jones@oss.qualcomm.com>
Prepare for irq_compose_msi_msg() to redirect IMSIC-targeted MSIs
through the IOMMU with an O(1) lookup. Unlike fixed-target MSI
controllers, IMSIC target PAs change on irq_set_affinity(), which
may run in atomic context, so compose cannot map the selected
target on demand.
Pre-map each populated IMSIC MSI target page into a domain-local
IOVA table when the device's paging domain first allocates
IMSIC-remapped IRQs, so every possible target IOVA is available at
lookup time. The table is indexed by a transformed IMSIC physical
address using the same transformation defined in the IOMMU
specification for the MSI table (and the function will be used for
the MSI table when that support is added later).
The table is sized by riscv_iommu_ir_msi_iova_count(), i.e.
BIT(group_index_bits + hart_index_bits), rather than
num_possible_cpus(). This is because riscv_iommu_ir_msi_iova_idx()
derives its index from hart_index_bits and group_index_bits, which
are sized for the group with the most harts, i.e. there may be a
sparse mapping.
Select IRQ_MSI_IOMMU so iommu_dma_map_msi() and iommufd_sw_map_msi()
are available to the driver.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/Kconfig | 1 +
drivers/iommu/riscv/iommu-ir.c | 105 +++++++++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.c | 12 +++-
drivers/iommu/riscv/iommu.h | 6 ++
4 files changed, 121 insertions(+), 3 deletions(-)
diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
index b86e5ab94183..f557b55c2c14 100644
--- a/drivers/iommu/riscv/Kconfig
+++ b/drivers/iommu/riscv/Kconfig
@@ -10,6 +10,7 @@ config RISCV_IOMMU
select GENERIC_PT
select IOMMU_PT
select IOMMU_PT_RISCV64
+ select IRQ_MSI_IOMMU
help
Support for implementations of the RISC-V IOMMU architecture that
complements the RISC-V MMU capabilities, providing similar address
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index 1e157faf7bbe..0dff74aa9b18 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -5,11 +5,97 @@
* Copyright (c) 2026 Qualcomm Technologies, Inc.
*/
#include <linux/cleanup.h>
+#include <linux/irqchip/riscv-imsic.h>
#include <linux/msi.h>
#include <linux/slab.h>
#include "iommu.h"
+/*
+ * Compute the MSI index for an MSI physical address using the
+ * IOMMU "extract" function (RISC-V IOMMU spec section 2.3.3).
+ */
+static size_t riscv_iommu_ir_extract_msi_idx(phys_addr_t pa)
+{
+ const struct imsic_global_config *global = imsic_get_global_config();
+ phys_addr_t mask, addr = pa >> 12;
+ size_t idx;
+
+ mask = BIT(global->hart_index_bits + global->guest_index_bits) - 1;
+ idx = addr & mask;
+
+ if (global->group_index_bits) {
+ phys_addr_t group_mask = BIT(global->group_index_bits) - 1;
+ phys_addr_t group_shift = global->group_index_shift - 12;
+ phys_addr_t group = (addr >> group_shift) & group_mask;
+
+ idx |= group << fls64(mask);
+ }
+
+ return idx;
+}
+
+static size_t riscv_iommu_ir_msi_iova_idx(phys_addr_t pa)
+{
+ const struct imsic_global_config *global = imsic_get_global_config();
+
+ /* msi_iova[] is only used for the host imsics */
+ return riscv_iommu_ir_extract_msi_idx(pa) >> global->guest_index_bits;
+}
+
+static size_t riscv_iommu_ir_msi_iova_count(void)
+{
+ const struct imsic_global_config *global = imsic_get_global_config();
+
+ return BIT(global->group_index_bits + global->hart_index_bits);
+}
+
+static int riscv_iommu_ir_build_msi_iova(struct riscv_iommu_domain *domain, struct device *dev)
+{
+ const struct imsic_global_config *global = imsic_get_global_config();
+ struct iommu_domain *d = &domain->domain;
+ dma_addr_t *msi_iova __free(kfree) = NULL;
+ unsigned int cpu;
+ int ret;
+
+ guard(mutex)(&domain->mutex);
+
+ if (domain->msi_iova)
+ return 0;
+
+ switch (d->cookie_type) {
+ case IOMMU_COOKIE_DMA_IOVA:
+ case IOMMU_COOKIE_DMA_MSI:
+ case IOMMU_COOKIE_IOMMUFD:
+ break;
+ default:
+ return 0;
+ }
+
+ msi_iova = kcalloc(riscv_iommu_ir_msi_iova_count(), sizeof(*msi_iova), GFP_KERNEL);
+ if (!msi_iova)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ const struct imsic_local_config *local = per_cpu_ptr(global->local, cpu);
+ phys_addr_t pa = local->msi_pa;
+ unsigned int shift;
+ size_t idx;
+
+ if (!pa)
+ continue;
+
+ idx = riscv_iommu_ir_msi_iova_idx(pa);
+ ret = iommu_dma_map_msi(d, dev, pa, IMSIC_MMIO_PAGE_SZ, &msi_iova[idx], &shift);
+ if (ret)
+ return ret;
+ }
+
+ domain->msi_iova = no_free_ptr(msi_iova);
+
+ return 0;
+}
+
static struct irq_chip riscv_iommu_ir_irq_chip = {
.name = "IOMMU-IR",
.irq_ack = irq_chip_ack_parent,
@@ -22,9 +108,25 @@ static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain,
unsigned int irq_base, unsigned int nr_irqs,
void *arg)
{
+ struct riscv_iommu_info *info = irqdomain->host_data;
+ struct riscv_iommu_domain *domain;
struct irq_data *data;
int i, ret;
+ /*
+ * MSI IOVAs are domain-local, just like DMA IOVAs. The device must be
+ * quiesced, including MSI teardown, before switching away from or freeing
+ * the domain. iommu_dma_map_msi() requires the group mutex to be held;
+ * take it around the domain lookup too so info->domain can't change
+ * out from under the build.
+ */
+ scoped_guard(iommu_group, info->dev) {
+ domain = rcu_dereference_protected(info->domain, true);
+ ret = domain ? riscv_iommu_ir_build_msi_iova(domain, info->dev) : 0;
+ }
+ if (ret)
+ return ret;
+
ret = irq_domain_alloc_irqs_parent(irqdomain, irq_base, nr_irqs, arg);
if (ret)
return ret;
@@ -116,6 +218,9 @@ int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struc
void riscv_iommu_ir_free_paging_domain(struct iommu_domain *iommu_domain)
{
+ struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
+
+ kfree(domain->msi_iova);
}
void riscv_iommu_ir_get_resv_regions(struct device *dev, struct list_head *head)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 106cc8216942..07287c0e72e7 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -811,9 +811,6 @@ static int riscv_iommu_iodir_set_mode(struct riscv_iommu_device *iommu,
return 0;
}
-#define iommu_domain_to_riscv(iommu_domain) \
- container_of(iommu_domain, struct riscv_iommu_domain, domain)
-
/*
* Linkage between an iommu_domain and attached devices.
*
@@ -1337,6 +1334,8 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
if (!domain)
return ERR_PTR(-ENOMEM);
+ mutex_init(&domain->mutex);
+
INIT_LIST_HEAD_RCU(&domain->bonds);
spin_lock_init(&domain->lock);
/*
@@ -1450,6 +1449,13 @@ static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
if (!info)
return ERR_PTR(-ENOMEM);
+ /*
+ * Set info->dev before creating the irqdomain: riscv_iommu_ir_irq_domain_create()
+ * publishes the irqdomain via dev_set_msi_domain(), making the .alloc callback
+ * (which dereferences info->dev) reachable.
+ */
+ info->dev = dev;
+
if (imsic_enabled()) {
irqdomain = riscv_iommu_ir_irq_domain_create(dev, info);
if (IS_ERR(irqdomain)) {
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 16b3c9c4cf8c..8bd41dd63f0e 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -32,14 +32,20 @@ struct riscv_iommu_domain {
};
struct list_head bonds;
spinlock_t lock; /* protect bonds list updates. */
+ struct mutex mutex; /* serializes sleepable, domain-wide setups */
int pscid;
+ dma_addr_t *msi_iova;
};
PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
+#define iommu_domain_to_riscv(iommu_domain) \
+ container_of(iommu_domain, struct riscv_iommu_domain, domain)
+
/* Private IOMMU data for managed devices, dev_iommu_priv_* */
struct riscv_iommu_info {
struct riscv_iommu_domain __rcu *domain;
struct irq_domain *irqdomain;
+ struct device *dev;
};
struct riscv_iommu_device;
--
2.43.0
next prev parent reply other threads:[~2026-08-07 18:17 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
2026-08-07 18:16 ` [PATCH v3 01/19] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
2026-08-07 18:16 ` [PATCH v3 02/19] iommufd: Add iommufd_sw_map_msi() Andrew Jones
2026-08-07 18:16 ` [PATCH v3 03/19] iommu/dma: Add iommu_dma_sw_map_msi() Andrew Jones
2026-08-07 18:16 ` [PATCH v3 04/19] iommu/dma: Add iommu_dma_map_msi() Andrew Jones
2026-08-07 18:16 ` [PATCH v3 05/19] genirq/msi: Provide DOMAIN_BUS_MSI_REMAP Andrew Jones
2026-08-07 18:17 ` [PATCH v3 06/19] irqchip/riscv-imsic: Compose MSI updates through the hierarchy Andrew Jones
2026-08-07 20:25 ` Thomas Gleixner
2026-08-10 8:01 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 07/19] iommu/riscv: Add IRQ domain for interrupt remapping Andrew Jones
2026-08-07 20:32 ` Thomas Gleixner
2026-08-07 20:36 ` Thomas Gleixner
2026-08-10 8:11 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 08/19] iommu/riscv: Prepare info->domain for concurrent RCU read access Andrew Jones
2026-08-07 18:17 ` [PATCH v3 09/19] iommu/riscv: Publish IOMMU_RESV_SW_MSI region for iommufd MSI remapping Andrew Jones
2026-08-07 18:17 ` Andrew Jones [this message]
2026-08-07 18:17 ` [PATCH v3 11/19] iommu/riscv: Copy MSI IOVA table when replacing an iommufd domain Andrew Jones
2026-08-07 18:17 ` [PATCH v3 12/19] iommu/riscv: Gate identity boundary switches with live MSIs Andrew Jones
2026-08-07 18:17 ` [PATCH v3 13/19] iommu/riscv: Implement irq_compose_msi_msg for IMSIC remapping Andrew Jones
2026-08-07 18:17 ` [PATCH v3 14/19] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V Andrew Jones
2026-08-07 18:17 ` [PATCH v3 15/19] iommu/riscv: report iommu capabilities Andrew Jones
2026-08-07 18:17 ` [PATCH v3 16/19] vfio: enable IOMMU_TYPE1 for RISC-V Andrew Jones
2026-08-07 18:17 ` [PATCH v3 17/19] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch Andrew Jones
2026-08-07 18:17 ` [PATCH v3 18/19] riscv: defconfig: Enable IOMMUFD and VFIO Andrew Jones
2026-08-07 18:17 ` [PATCH v3 19/19] selftests/vfio: Allow building on RISC-V Andrew Jones
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260807181713.228535-11-andrew.jones@oss.qualcomm.com \
--to=andrew.jones@oss.qualcomm.com \
--cc=anup@brainfault.org \
--cc=fangyu.yu@linux.alibaba.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@nvidia.com \
--cc=jgg@ziepe.ca \
--cc=joro@8bytes.org \
--cc=kevin.tian@intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=palmer@dabbelt.com \
--cc=pjw@kernel.org \
--cc=robin.murphy@arm.com \
--cc=tglx@kernel.org \
--cc=tjeznach@rivosinc.com \
--cc=tomasz.jeznach@linux.dev \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®