* [PATCH v4 01/21] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi() Andrew Jones
` (19 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
struct iommufd_sw_msi_maps uses a fixed 64-bit bitmap, limiting each
group and hardware page table to 64 software MSI mappings. RISC-V
interrupt remapping needs a mapping for every possible CPU, so this
limit is insufficient.
Make the bitmap grow on demand and treat IDs beyond its current size
as absent. Cap it at 16K entries to bound allocation size while leaving
ample room for expected software MSI users.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/iommufd/device.c | 3 +-
drivers/iommu/iommufd/driver.c | 38 +++++++++++++++--------
drivers/iommu/iommufd/hw_pagetable.c | 1 +
drivers/iommu/iommufd/iommufd_private.h | 41 +++++++++++++++++++++++--
4 files changed, 67 insertions(+), 16 deletions(-)
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index d488c23fd353..868f1e591208 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -34,6 +34,7 @@ static void iommufd_group_release(struct kref *kref)
NULL, GFP_KERNEL);
iommu_group_put(igroup->group);
mutex_destroy(&igroup->lock);
+ kfree(igroup->required_sw_msi.bitmap);
kfree(igroup);
}
@@ -384,7 +385,7 @@ static int iommufd_group_setup_msi(struct iommufd_group *igroup,
int rc;
if (cur->sw_msi_start != igroup->sw_msi_start ||
- !test_bit(cur->id, igroup->required_sw_msi.bitmap))
+ !iommufd_sw_msi_maps_test_bit(&igroup->required_sw_msi, cur->id))
continue;
rc = iommufd_sw_msi_install(ictx, hwpt_paging, cur);
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 3b8067976eac..9b9316ae4266 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -196,13 +196,15 @@ iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) {
if (cur->sw_msi_start != sw_msi_start)
continue;
+ if (cur->pgoff == UINT_MAX)
+ return ERR_PTR(-EOVERFLOW);
max_pgoff = max(max_pgoff, cur->pgoff + 1);
if (cur->msi_addr == msi_addr)
return cur;
}
- if (ictx->sw_msi_id >=
- BITS_PER_BYTE * sizeof_field(struct iommufd_sw_msi_maps, bitmap))
+ if (ictx->sw_msi_id > IOMMUFD_SW_MSI_MAX_ID ||
+ max_pgoff > (ULONG_MAX - sw_msi_start) / PAGE_SIZE)
return ERR_PTR(-EOVERFLOW);
cur = kzalloc_obj(*cur);
@@ -222,21 +224,26 @@ int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
struct iommufd_sw_msi_map *msi_map)
{
unsigned long iova;
+ int rc;
lockdep_assert_held(&ictx->sw_msi_lock);
+ if (iommufd_sw_msi_maps_test_bit(&hwpt_paging->present_sw_msi,
+ msi_map->id))
+ return 0;
+
iova = msi_map->sw_msi_start + msi_map->pgoff * PAGE_SIZE;
- if (!test_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap)) {
- int rc;
-
- rc = iommu_map(hwpt_paging->common.domain, iova,
- msi_map->msi_addr, PAGE_SIZE,
- IOMMU_WRITE | IOMMU_READ | IOMMU_MMIO,
- GFP_KERNEL_ACCOUNT);
- if (rc)
- return rc;
- __set_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap);
- }
+ rc = iommufd_sw_msi_maps_ensure(&hwpt_paging->present_sw_msi, msi_map->id);
+ if (rc)
+ return rc;
+
+ rc = iommu_map(hwpt_paging->common.domain, iova,
+ msi_map->msi_addr, PAGE_SIZE,
+ IOMMU_WRITE | IOMMU_READ | IOMMU_MMIO,
+ GFP_KERNEL_ACCOUNT);
+ if (rc)
+ return rc;
+ __set_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap);
return 0;
}
EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi_install, "IOMMUFD_INTERNAL");
@@ -290,6 +297,11 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
if (IS_ERR(msi_map))
return PTR_ERR(msi_map);
+ rc = iommufd_sw_msi_maps_ensure(&handle->idev->igroup->required_sw_msi,
+ msi_map->id);
+ if (rc)
+ return rc;
+
rc = iommufd_sw_msi_install(ictx, hwpt_paging, msi_map);
if (rc)
return rc;
diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
index 623cc608ca0c..54873de43eb0 100644
--- a/drivers/iommu/iommufd/hw_pagetable.c
+++ b/drivers/iommu/iommufd/hw_pagetable.c
@@ -32,6 +32,7 @@ void iommufd_hwpt_paging_destroy(struct iommufd_object *obj)
}
__iommufd_hwpt_destroy(&hwpt_paging->common);
+ kfree(hwpt_paging->present_sw_msi.bitmap);
refcount_dec(&hwpt_paging->ioas->obj.users);
}
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 43fbc5bed8de..9ca5f9f92cdf 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -9,6 +9,7 @@
#include <linux/iova_bitmap.h>
#include <linux/maple_tree.h>
#include <linux/rwsem.h>
+#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/xarray.h>
#include <uapi/linux/iommufd.h>
@@ -29,11 +30,47 @@ struct iommufd_sw_msi_map {
unsigned int id;
};
-/* Bitmap of struct iommufd_sw_msi_map::id */
+/* Bitmap of struct iommufd_sw_msi_map::id; starts empty, grows on demand. */
struct iommufd_sw_msi_maps {
- DECLARE_BITMAP(bitmap, 64);
+ unsigned long *bitmap;
+ unsigned int nbits;
};
+/* Large enough for foreseeable SW MSI users while bounding bitmap growth. */
+#define IOMMUFD_SW_MSI_MAX_ID (16U * 1024 - 1)
+
+/* Grow bitmap to accommodate id. Must be called under ictx->sw_msi_lock. */
+static inline int iommufd_sw_msi_maps_ensure(struct iommufd_sw_msi_maps *maps,
+ unsigned int id)
+{
+ unsigned long *new_bitmap;
+ unsigned int new_nbits;
+
+ if (id < maps->nbits)
+ return 0;
+ if (id > IOMMUFD_SW_MSI_MAX_ID)
+ return -EOVERFLOW;
+
+ new_nbits = max(ALIGN(id + 1, BITS_PER_LONG), 64U);
+ new_bitmap = krealloc(maps->bitmap,
+ BITS_TO_LONGS(new_nbits) * sizeof(unsigned long),
+ GFP_KERNEL_ACCOUNT);
+ if (!new_bitmap)
+ return -ENOMEM;
+ bitmap_clear(new_bitmap, maps->nbits, new_nbits - maps->nbits);
+ maps->bitmap = new_bitmap;
+ maps->nbits = new_nbits;
+ return 0;
+}
+
+static inline bool iommufd_sw_msi_maps_test_bit(const struct iommufd_sw_msi_maps *maps,
+ unsigned int id)
+{
+ if (id >= maps->nbits)
+ return false;
+ return test_bit(id, maps->bitmap);
+}
+
#ifdef CONFIG_IRQ_MSI_IOMMU
int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
struct iommufd_hwpt_paging *hwpt_paging,
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
2026-08-20 21:41 ` [PATCH v4 01/21] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 22:09 ` Jason Gunthorpe
2026-08-20 21:41 ` [PATCH v4 03/21] iommu/dma: Add iommu_dma_sw_map_msi() Andrew Jones
` (18 subsequent siblings)
20 siblings, 1 reply; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
Add a descriptor-free counterpart to iommufd_sw_msi(). The existing
function is tied to a struct msi_desc and stores the result in the
descriptor. This variant returns the IOVA directly so callers can
pre-map MSI targets before any descriptor has been allocated.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/iommu-priv.h | 11 +++++++
drivers/iommu/iommufd/driver.c | 60 +++++++++++++++++++++++++---------
2 files changed, 56 insertions(+), 15 deletions(-)
diff --git a/drivers/iommu/iommu-priv.h b/drivers/iommu/iommu-priv.h
index aaffad5854fc..f60373cd2f70 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -55,12 +55,23 @@ int iommu_replace_group_handle(struct iommu_group *group,
#if IS_ENABLED(CONFIG_IOMMUFD_DRIVER_CORE) && IS_ENABLED(CONFIG_IRQ_MSI_IOMMU)
int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
phys_addr_t msi_addr);
+int iommufd_sw_map_msi(struct iommu_domain *domain, struct device *dev,
+ phys_addr_t msi_addr, size_t required_size,
+ dma_addr_t *msi_iova, unsigned int *msi_shift);
#else /* !CONFIG_IOMMUFD_DRIVER_CORE || !CONFIG_IRQ_MSI_IOMMU */
static inline int iommufd_sw_msi(struct iommu_domain *domain,
struct msi_desc *desc, phys_addr_t msi_addr)
{
return -EOPNOTSUPP;
}
+
+static inline int iommufd_sw_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift)
+{
+ return -EOPNOTSUPP;
+}
#endif /* CONFIG_IOMMUFD_DRIVER_CORE && CONFIG_IRQ_MSI_IOMMU */
int iommu_replace_device_pasid(struct iommu_domain *domain,
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 9b9316ae4266..69b3dbcbee3b 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -249,15 +249,23 @@ int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi_install, "IOMMUFD_INTERNAL");
/*
- * Called by the irq code if the platform translates the MSI address through the
- * IOMMU. msi_addr is the physical address of the MSI page. iommufd will
- * allocate a fd global iova for the physical page that is the same on all
- * domains and devices.
+ * Descriptor-free counterpart to iommufd_sw_msi(). Maps an MSI physical page
+ * into the domain and returns the IOVA. Used for pre-mapping MSI targets before
+ * any MSI descriptor has been set (e.g. IMSIC doorbell pages). The IOVA is
+ * global to the iommufd file descriptor: every domain and device using the
+ * same MSI parameters gets the same IOVA.
+ *
+ * msi_addr is the exact byte offset of the MSI doorbell; the caller must have
+ * verified it is contained within an MMIO region safe to map at PAGE_SIZE. If
+ * required_size is non-zero it must equal PAGE_SIZE. @msi_iova and @msi_shift
+ * must be non-NULL.
+ *
+ * The caller must hold @dev's iommu group mutex.
*/
-int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr)
+int iommufd_sw_map_msi(struct iommu_domain *domain, struct device *dev,
+ phys_addr_t msi_addr, size_t required_size,
+ dma_addr_t *msi_iova, unsigned int *msi_shift)
{
- struct device *dev = msi_desc_to_dev(desc);
struct iommufd_hwpt_paging *hwpt_paging;
struct iommu_attach_handle *raw_handle;
struct iommufd_attach_handle *handle;
@@ -266,6 +274,12 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
unsigned long iova;
int rc;
+ *msi_iova = 0;
+ *msi_shift = 0;
+
+ if (required_size && required_size != PAGE_SIZE)
+ return -EOPNOTSUPP;
+
/*
* It is safe to call iommu_attach_handle_get() here because the iommu
* core code invokes this under the group mutex which also prevents any
@@ -286,13 +300,7 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
ictx = handle->idev->ictx;
guard(mutex)(&ictx->sw_msi_lock);
- /*
- * The input msi_addr is the exact byte offset of the MSI doorbell, we
- * assume the caller has checked that it is contained with a MMIO region
- * that is secure to map at PAGE_SIZE.
- */
- msi_map = iommufd_sw_msi_get_map(handle->idev->ictx,
- msi_addr & PAGE_MASK,
+ msi_map = iommufd_sw_msi_get_map(ictx, msi_addr & PAGE_MASK,
handle->idev->igroup->sw_msi_start);
if (IS_ERR(msi_map))
return PTR_ERR(msi_map);
@@ -308,7 +316,29 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
__set_bit(msi_map->id, handle->idev->igroup->required_sw_msi.bitmap);
iova = msi_map->sw_msi_start + msi_map->pgoff * PAGE_SIZE;
- msi_desc_set_iommu_msi_iova(desc, iova, PAGE_SHIFT);
+ *msi_iova = iova;
+ *msi_shift = PAGE_SHIFT;
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(iommufd_sw_map_msi, "IOMMUFD");
+
+/*
+ * Called by the irq layer when the platform translates MSI addresses through
+ * the IOMMU. Wraps iommufd_sw_map_msi() and stores the result in the descriptor.
+ */
+int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
+ phys_addr_t msi_addr)
+{
+ dma_addr_t msi_iova;
+ unsigned int msi_shift;
+ int rc;
+
+ rc = iommufd_sw_map_msi(domain, msi_desc_to_dev(desc), msi_addr,
+ 0, &msi_iova, &msi_shift);
+ if (rc)
+ return rc;
+
+ msi_desc_set_iommu_msi_iova(desc, msi_iova, msi_shift);
return 0;
}
EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi, "IOMMUFD");
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-20 21:41 ` [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi() Andrew Jones
@ 2026-08-20 22:09 ` Jason Gunthorpe
2026-08-21 11:07 ` Andrew Jones
0 siblings, 1 reply; 39+ messages in thread
From: Jason Gunthorpe @ 2026-08-20 22:09 UTC (permalink / raw)
To: Andrew Jones
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Thu, Aug 20, 2026 at 11:41:31PM +0200, Andrew Jones wrote:
> Add a descriptor-free counterpart to iommufd_sw_msi(). The existing
> function is tied to a struct msi_desc and stores the result in the
> descriptor. This variant returns the IOVA directly so callers can
> pre-map MSI targets before any descriptor has been allocated.
I haven't grasped why it is like this?
The cover letter says:
ARM can map a fixed doorbell PA per ITS and cache its IOVA on the
descriptor because affinity changes only hardware routing, not the
composed address. An IMSIC target PA changes with affinity, and MSI
composition may run in atomic context, so mapping the selected target on
demand is not an option. This series pre-maps the supervisor IMSIC page
for every possible CPU into a domain-local PA-to-IOVA table when remapped
IRQs are first allocated, allowing composition to select the target with
an O(1) lookup. If iommufd replaces a paging domain while IRQs remain
allocated, the incoming domain's table is rebuilt before it is attached.
There are a few confusiong things with this statement:
1) Okay the IMSIC PA changes dynamically but since it can be
premapped the PAs required is fixed and known. ARM doesn't change
the PA dynamically?
2) Why do you say mapping on demand is not possible? ARM's
iommu_dma_prepare_msi() is not called in an atomic context and
does do the iommu mapping.
What I rather expected was for riscv to have a PA window that is very
big and not just one page, eg adjust iommu_dma_prepare_msi() so you
can pass in the entire PA space that you need for the affinity
changes. Maybe this is a list of phys_addr_t ?
Then keep with the ARM flow where everything happens at the same
times as today. Instead of just mapping one page you map the entire
list.
I'm not keen on this design where things get pre-mapped into the
domain by the iommu driver, the ARM version is much easier to
understand :\
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-20 22:09 ` Jason Gunthorpe
@ 2026-08-21 11:07 ` Andrew Jones
2026-08-21 12:02 ` Jason Gunthorpe
0 siblings, 1 reply; 39+ messages in thread
From: Andrew Jones @ 2026-08-21 11:07 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
Hi Jason,
On Thu, Aug 20, 2026 at 07:09:08PM -0300, Jason Gunthorpe wrote:
> On Thu, Aug 20, 2026 at 11:41:31PM +0200, Andrew Jones wrote:
> > Add a descriptor-free counterpart to iommufd_sw_msi(). The existing
> > function is tied to a struct msi_desc and stores the result in the
> > descriptor. This variant returns the IOVA directly so callers can
> > pre-map MSI targets before any descriptor has been allocated.
>
> I haven't grasped why it is like this?
>
> The cover letter says:
>
> ARM can map a fixed doorbell PA per ITS and cache its IOVA on the
> descriptor because affinity changes only hardware routing, not the
> composed address. An IMSIC target PA changes with affinity, and MSI
> composition may run in atomic context, so mapping the selected target on
> demand is not an option. This series pre-maps the supervisor IMSIC page
> for every possible CPU into a domain-local PA-to-IOVA table when remapped
> IRQs are first allocated, allowing composition to select the target with
> an O(1) lookup. If iommufd replaces a paging domain while IRQs remain
> allocated, the incoming domain's table is rebuilt before it is attached.
>
> There are a few confusiong things with this statement:
> 1) Okay the IMSIC PA changes dynamically but since it can be
> premapped the PAs required is fixed and known. ARM doesn't change
> the PA dynamically?
ARM's PA is fixed. On affinity change it uses MOVI to instruct the ITS to
change the routing.
> 2) Why do you say mapping on demand is not possible? ARM's
> iommu_dma_prepare_msi() is not called in an atomic context and
> does do the iommu mapping.
By "on demand" I mean after IRQ allocation, when selecting a new MSI
target may occur in atomic context. Mapping then is not possible. Like ARM,
this riscv implementation performs all mappings at alloc-irqs time.
>
> What I rather expected was for riscv to have a PA window that is very
> big and not just one page, eg adjust iommu_dma_prepare_msi() so you
> can pass in the entire PA space that you need for the affinity
> changes. Maybe this is a list of phys_addr_t ?
That's more or less what this series does. There's no big PA window
because the IMSIC PAs aren't generally contiguous. iommu_dma_map_msi()
factors the mapping operation out of iommu_dma_prepare_msi() and returns
each IOVA directly. A batched list API would be possible, but it would
only move the loop.
>
> Then keep with the ARM flow where everything happens at the same
> times as today. Instead of just mapping one page you map the entire
> list.
The goal of this approach was to match ARM's flow as much as possible
despite the quite different architecture. Here's a table showing how
they match up now.
.------------------------------------------------------------------------------.
| IRQ lifecycle phase | ARM IR / ITS | RISC-V IR / IMSIC |
|---------------------|---------------------------|----------------------------|
| IRQ allocation | ITS allocates an event/ | IMSIC allocates a vector |
| | LPI. The MSI target PA is | on a CPU. The MSI target |
| | the fixed GITS_TRANSLATER | PA is that CPU's IMSIC |
| | address. | page. |
| | | |
| | iommu_dma_prepare_msi() | iommu_dma_map_msi() |
| | is called from the IR | is called from the IR |
| | irqdomain .alloc | irqdomain .alloc |
| | callback. It calls | callback. It's called once |
| | iommu_dma_map_msi() once | for each possible IMSIC |
| | for the fixed ITS PA. | PA. |
|---------------------|---------------------------|----------------------------|
| MSI mapping setup | iommu_dma_map_msi() | iommu_dma_map_msi() |
| | dispatches to | dispatches to |
| | iommu_dma_sw_map_msi() | iommu_dma_sw_map_msi() |
| | or iommufd_sw_map_msi(). | or iommufd_sw_map_msi(). |
| | One IOVA is cached in the | One IOVA per IMSIC PA is |
| | MSI descriptor. | cached in the IOMMU domain |
| | | table. |
|---------------------|---------------------------|----------------------------|
| Initial composition | Compose the fixed target | Compose the selected CPU's |
| | IOVA plus the event ID. | IMSIC PA and local ID, |
| | | then substitute its IOVA. |
|---------------------|---------------------------|----------------------------|
| Affinity change | Send MOVI to change ITS- | Allocate a vector on the |
| | internal routing. The | new CPU. The device's MSI |
| | device MSI address does | address changes to the new |
| | not change. | CPU's IMSIC IOVA. |
| | | |
| | | The parent IMSIC irqdomain |
| | | irq_set_affinity() selects |
| | | the new vector and |
| | | recomposes from the top of |
| | | the hierarchy. The IR |
| | | irqdomain's |
| | | irq_compose_msi_msg() |
| | | translates the new IMSIC |
| | | PA to its IOVA. |
|---------------------|---------------------------|----------------------------|
| Message update | No new device MSI message | Recompose and rewrite the |
| | is required. | device MSI message through |
| | | the IRQ hierarchy. |
|---------------------|---------------------------|----------------------------|
| Composition context | No IOMMU mapping or | Composition may occur in |
| | lookup is required during | atomic context, so all |
| | an affinity change. | IMSIC PAs must already be |
| | | mapped and lookup cannot |
| | | sleep. |
|---------------------|---------------------------|----------------------------|
| Domain replacement | iommufd_group_setup_msi() | iommufd_group_setup_msi() |
| | installs the one required | installs all required MSI |
| | MSI mapping before attach.| mappings before attach. |
| | The descriptor's cached | riscv_iommu_ir_attach_ |
| | IOVA stays valid. | paging_domain() builds the |
| | | new domain's PA-to-IOVA |
| | | lookup table. |
|---------------------|---------------------------|----------------------------|
| Domain destruction | The ITS MSI mapping is | The IMSIC mappings and |
| | released with the domain. | PA-to-IOVA table are |
| | | released with the domain. |
.------------------------------------------------------------------------------.
Hopefully that helps describe the design better.
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-21 11:07 ` Andrew Jones
@ 2026-08-21 12:02 ` Jason Gunthorpe
2026-08-21 13:47 ` Andrew Jones
0 siblings, 1 reply; 39+ messages in thread
From: Jason Gunthorpe @ 2026-08-21 12:02 UTC (permalink / raw)
To: Andrew Jones
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 01:07:03PM +0200, Andrew Jones wrote:
> > What I rather expected was for riscv to have a PA window that is very
> > big and not just one page, eg adjust iommu_dma_prepare_msi() so you
> > can pass in the entire PA space that you need for the affinity
> > changes. Maybe this is a list of phys_addr_t ?
>
> That's more or less what this series does.
But it does it in a completely different way, and I'm struggling to
see any justification for this.
Again, just pass your list of PAs to iommu_dma_prepare_msi() from the
irq domain seems like the easiest and most ARM aligned thing.
When you need a new IOVA for an affinity change the PA's are already
mapped a simple offset calculation is that is needed.
No weridness in domain allocation, no messing with irq stuff in a
finalize function, no hackery to iommufd.
> There's no big PA window
> because the IMSIC PAs aren't generally contiguous. iommu_dma_map_msi()
> factors the mapping operation out of iommu_dma_prepare_msi() and returns
> each IOVA directly. A batched list API would be possible, but it would
> only move the loop.
But it keeps the entire flow consistent. Trying to pre-map MSIs and
mangle all sorts of things in the riscv driver is undoing the
generalization and what is expected to be be common code.
I don't want to see any mapping of msi in the rsicv iommu driver at
all, that should be the most important design point.
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-21 12:02 ` Jason Gunthorpe
@ 2026-08-21 13:47 ` Andrew Jones
2026-08-21 14:00 ` Jason Gunthorpe
0 siblings, 1 reply; 39+ messages in thread
From: Andrew Jones @ 2026-08-21 13:47 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 09:02:04AM -0300, Jason Gunthorpe wrote:
> On Fri, Aug 21, 2026 at 01:07:03PM +0200, Andrew Jones wrote:
>
> > > What I rather expected was for riscv to have a PA window that is very
> > > big and not just one page, eg adjust iommu_dma_prepare_msi() so you
> > > can pass in the entire PA space that you need for the affinity
> > > changes. Maybe this is a list of phys_addr_t ?
> >
> > That's more or less what this series does.
>
> But it does it in a completely different way, and I'm struggling to
> see any justification for this.
>
> Again, just pass your list of PAs to iommu_dma_prepare_msi() from the
> irq domain seems like the easiest and most ARM aligned thing.
>
> When you need a new IOVA for an affinity change the PA's are already
> mapped a simple offset calculation is that is needed.
Ah, I see what I missed. The proposed batched API would guarantee a
contiguous IOVA range, so the descriptor could cache the IOVA base and
then the riscv IR compose-msi-msg could simply compute its target off
that base. That would indeed eliminate msi_iova[] and all its complexity.
>
> No weridness in domain allocation, no messing with irq stuff in a
> finalize function, no hackery to iommufd.
>
> > There's no big PA window
> > because the IMSIC PAs aren't generally contiguous. iommu_dma_map_msi()
> > factors the mapping operation out of iommu_dma_prepare_msi() and returns
> > each IOVA directly. A batched list API would be possible, but it would
> > only move the loop.
>
> But it keeps the entire flow consistent. Trying to pre-map MSIs and
> mangle all sorts of things in the riscv driver is undoing the
> generalization and what is expected to be be common code.
>
> I don't want to see any mapping of msi in the rsicv iommu driver at
> all, that should be the most important design point.
ARM calls iommu_dma_prepare_msi() from ITS since there is no SMMU
irqdomain and ITS owns the MSI target. This series adds a RISC-V IOMMU IR
irqdomain to replace the IMSIC PA with the corresponding IOVA, so the IR
domain's alloc callback is the right place to create the mappings through
common DMA-IOMMU/iommufd code.
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-21 13:47 ` Andrew Jones
@ 2026-08-21 14:00 ` Jason Gunthorpe
2026-08-21 14:23 ` Andrew Jones
0 siblings, 1 reply; 39+ messages in thread
From: Jason Gunthorpe @ 2026-08-21 14:00 UTC (permalink / raw)
To: Andrew Jones
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 03:47:17PM +0200, Andrew Jones wrote:
> On Fri, Aug 21, 2026 at 09:02:04AM -0300, Jason Gunthorpe wrote:
> > On Fri, Aug 21, 2026 at 01:07:03PM +0200, Andrew Jones wrote:
> >
> > > > What I rather expected was for riscv to have a PA window that is very
> > > > big and not just one page, eg adjust iommu_dma_prepare_msi() so you
> > > > can pass in the entire PA space that you need for the affinity
> > > > changes. Maybe this is a list of phys_addr_t ?
> > >
> > > That's more or less what this series does.
> >
> > But it does it in a completely different way, and I'm struggling to
> > see any justification for this.
> >
> > Again, just pass your list of PAs to iommu_dma_prepare_msi() from the
> > irq domain seems like the easiest and most ARM aligned thing.
> >
> > When you need a new IOVA for an affinity change the PA's are already
> > mapped a simple offset calculation is that is needed.
>
> Ah, I see what I missed. The proposed batched API would guarantee a
> contiguous IOVA range, so the descriptor could cache the IOVA base and
> then the riscv IR compose-msi-msg could simply compute its target off
> that base. That would indeed eliminate msi_iova[] and all its complexity.
Yeah, that is pretty simple like that
Then you just need to teach everything downstream to have a list of
phys not a single phys and thats an easy to understand delta
> ARM calls iommu_dma_prepare_msi() from ITS since there is no SMMU
> irqdomain and ITS owns the MSI target.
Right SMMU is not involved in interrupt translation
> This series adds a RISC-V IOMMU IR irqdomain to replace the IMSIC PA
> with the corresponding IOVA, so the IR domain's alloc callback is
> the right place to create the mappings through common
> DMA-IOMMU/iommufd code.
But RISC-V IOMMU is also not involved in interrupt translation. Beyond
it could remap the PAs which represent the CPUs, but you are not using
it that way since it is statically setup.
So I'm not sure why the iommu gets an IR? Isn't it the same as ARM
where the IMSIC itself just has a wack of PAs it needs to access so it
calls iommu_dma_prepare_msi() to do it?
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-21 14:00 ` Jason Gunthorpe
@ 2026-08-21 14:23 ` Andrew Jones
2026-08-21 14:31 ` Jason Gunthorpe
0 siblings, 1 reply; 39+ messages in thread
From: Andrew Jones @ 2026-08-21 14:23 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 11:00:33AM -0300, Jason Gunthorpe wrote:
> On Fri, Aug 21, 2026 at 03:47:17PM +0200, Andrew Jones wrote:
> > On Fri, Aug 21, 2026 at 09:02:04AM -0300, Jason Gunthorpe wrote:
> > > On Fri, Aug 21, 2026 at 01:07:03PM +0200, Andrew Jones wrote:
> > >
> > > > > What I rather expected was for riscv to have a PA window that is very
> > > > > big and not just one page, eg adjust iommu_dma_prepare_msi() so you
> > > > > can pass in the entire PA space that you need for the affinity
> > > > > changes. Maybe this is a list of phys_addr_t ?
> > > >
> > > > That's more or less what this series does.
> > >
> > > But it does it in a completely different way, and I'm struggling to
> > > see any justification for this.
> > >
> > > Again, just pass your list of PAs to iommu_dma_prepare_msi() from the
> > > irq domain seems like the easiest and most ARM aligned thing.
> > >
> > > When you need a new IOVA for an affinity change the PA's are already
> > > mapped a simple offset calculation is that is needed.
> >
> > Ah, I see what I missed. The proposed batched API would guarantee a
> > contiguous IOVA range, so the descriptor could cache the IOVA base and
> > then the riscv IR compose-msi-msg could simply compute its target off
> > that base. That would indeed eliminate msi_iova[] and all its complexity.
>
> Yeah, that is pretty simple like that
>
> Then you just need to teach everything downstream to have a list of
> phys not a single phys and thats an easy to understand delta
>
> > ARM calls iommu_dma_prepare_msi() from ITS since there is no SMMU
> > irqdomain and ITS owns the MSI target.
>
> Right SMMU is not involved in interrupt translation
>
> > This series adds a RISC-V IOMMU IR irqdomain to replace the IMSIC PA
> > with the corresponding IOVA, so the IR domain's alloc callback is
> > the right place to create the mappings through common
> > DMA-IOMMU/iommufd code.
>
> But RISC-V IOMMU is also not involved in interrupt translation. Beyond
> it could remap the PAs which represent the CPUs, but you are not using
> it that way since it is statically setup.
>
> So I'm not sure why the iommu gets an IR? Isn't it the same as ARM
> where the IMSIC itself just has a wack of PAs it needs to access so it
> calls iommu_dma_prepare_msi() to do it?
Right, this series is only remapping IMSIC PAs, not using the IOMMU MSI
table. The MSI table must be disabled when second-stage translation is
Bare, so it cannot be used for the host mappings here. It will be needed
for guest interrupt files (the irqbypass series I'm also working on),
where this IR irqdomain can then intercept irq_set_vcpu_affinity().
Also, keeping the MSI setup in this IR irqdomain avoids putting IOMMU
mapping knowledge in the IMSIC driver.
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-21 14:23 ` Andrew Jones
@ 2026-08-21 14:31 ` Jason Gunthorpe
2026-08-21 15:18 ` Andrew Jones
0 siblings, 1 reply; 39+ messages in thread
From: Jason Gunthorpe @ 2026-08-21 14:31 UTC (permalink / raw)
To: Andrew Jones
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 04:23:22PM +0200, Andrew Jones wrote:
> > But RISC-V IOMMU is also not involved in interrupt translation. Beyond
> > it could remap the PAs which represent the CPUs, but you are not using
> > it that way since it is statically setup.
> >
> > So I'm not sure why the iommu gets an IR? Isn't it the same as ARM
> > where the IMSIC itself just has a wack of PAs it needs to access so it
> > calls iommu_dma_prepare_msi() to do it?
>
> Right, this series is only remapping IMSIC PAs, not using the IOMMU MSI
> table. The MSI table must be disabled when second-stage translation is
> Bare, so it cannot be used for the host mappings here. It will be needed
> for guest interrupt files (the irqbypass series I'm also working on),
> where this IR irqdomain can then intercept irq_set_vcpu_affinity().
> Also, keeping the MSI setup in this IR irqdomain avoids putting IOMMU
> mapping knowledge in the IMSIC driver.
It's ok for the IMSIC driver to call iommu_dma_prepare_msi(), that's
the architecutre of this at least.
Until you get to adding something more complicated, eg irqbypass, I'd
suggest keeping this series simple.
I guess I would wonder why the IOMMU needs to be involved in changing
the mapping down the road, why not just reprogram the MSI-X address?
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-21 14:31 ` Jason Gunthorpe
@ 2026-08-21 15:18 ` Andrew Jones
2026-08-21 16:12 ` Jason Gunthorpe
0 siblings, 1 reply; 39+ messages in thread
From: Andrew Jones @ 2026-08-21 15:18 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 11:31:55AM -0300, Jason Gunthorpe wrote:
> On Fri, Aug 21, 2026 at 04:23:22PM +0200, Andrew Jones wrote:
> > > But RISC-V IOMMU is also not involved in interrupt translation. Beyond
> > > it could remap the PAs which represent the CPUs, but you are not using
> > > it that way since it is statically setup.
> > >
> > > So I'm not sure why the iommu gets an IR? Isn't it the same as ARM
> > > where the IMSIC itself just has a wack of PAs it needs to access so it
> > > calls iommu_dma_prepare_msi() to do it?
> >
> > Right, this series is only remapping IMSIC PAs, not using the IOMMU MSI
> > table. The MSI table must be disabled when second-stage translation is
> > Bare, so it cannot be used for the host mappings here. It will be needed
> > for guest interrupt files (the irqbypass series I'm also working on),
> > where this IR irqdomain can then intercept irq_set_vcpu_affinity().
> > Also, keeping the MSI setup in this IR irqdomain avoids putting IOMMU
> > mapping knowledge in the IMSIC driver.
>
> It's ok for the IMSIC driver to call iommu_dma_prepare_msi(), that's
> the architecutre of this at least.
>
> Until you get to adding something more complicated, eg irqbypass, I'd
> suggest keeping this series simple.
>
> I guess I would wonder why the IOMMU needs to be involved in changing
> the mapping down the road, why not just reprogram the MSI-X address?
>
For guest interrupts the MSI-X address is a guest IMSIC address.
Reprogramming it handles a guest reroute, but not vcpu migration (the
guest address stays the same while its host backing moves between
guest interrupt files and MRIFs). The IOMMU driver updates that mapping
through "the hypervisor to IOMMU driver interface", a.k.a. the IRQ
domain's irq_set_vcpu_affinity() callback. That mapping cannot be
prepared ahead of time because only the hypervisor knows the target,
and it may change later.
Since I know I'll need an IRQ domain in the IOMMU driver for
irq_set_vcpu_affinity(), I'd prefer to keep it in this series too.
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-21 15:18 ` Andrew Jones
@ 2026-08-21 16:12 ` Jason Gunthorpe
2026-08-21 17:12 ` Andrew Jones
0 siblings, 1 reply; 39+ messages in thread
From: Jason Gunthorpe @ 2026-08-21 16:12 UTC (permalink / raw)
To: Andrew Jones
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 05:18:49PM +0200, Andrew Jones wrote:
> On Fri, Aug 21, 2026 at 11:31:55AM -0300, Jason Gunthorpe wrote:
> > On Fri, Aug 21, 2026 at 04:23:22PM +0200, Andrew Jones wrote:
> > > > But RISC-V IOMMU is also not involved in interrupt translation. Beyond
> > > > it could remap the PAs which represent the CPUs, but you are not using
> > > > it that way since it is statically setup.
> > > >
> > > > So I'm not sure why the iommu gets an IR? Isn't it the same as ARM
> > > > where the IMSIC itself just has a wack of PAs it needs to access so it
> > > > calls iommu_dma_prepare_msi() to do it?
> > >
> > > Right, this series is only remapping IMSIC PAs, not using the IOMMU MSI
> > > table. The MSI table must be disabled when second-stage translation is
> > > Bare, so it cannot be used for the host mappings here. It will be needed
> > > for guest interrupt files (the irqbypass series I'm also working on),
> > > where this IR irqdomain can then intercept irq_set_vcpu_affinity().
> > > Also, keeping the MSI setup in this IR irqdomain avoids putting IOMMU
> > > mapping knowledge in the IMSIC driver.
> >
> > It's ok for the IMSIC driver to call iommu_dma_prepare_msi(), that's
> > the architecutre of this at least.
> >
> > Until you get to adding something more complicated, eg irqbypass, I'd
> > suggest keeping this series simple.
> >
> > I guess I would wonder why the IOMMU needs to be involved in changing
> > the mapping down the road, why not just reprogram the MSI-X address?
> >
>
> For guest interrupts the MSI-X address is a guest IMSIC address.
> Reprogramming it handles a guest reroute, but not vcpu migration (the
> guest address stays the same while its host backing moves between
> guest interrupt files and MRIFs).
Linux isn't prepared to have a guest controlled MSI-X addr,
AFAIK. I've wanted this for a long time but we are not there..
Does riscv absolutely require this? I suppose you have a new problem
where you don't know what affinity the guest is selecting when it
provides a MSI-X addr as you don't know what physical IMSIC page is
under its S1 IOVA?
> The IOMMU driver updates that mapping through "the hypervisor to
> IOMMU driver interface", a.k.a. the IRQ domain's
> irq_set_vcpu_affinity() callback. That mapping cannot be prepared
> ahead of time because only the hypervisor knows the target, and it
> may change later.
Nicolin has a series that worked toward making ARM work properly, it
is not at all like this and it isn't done with remapping domains.
You want to preserve the guests physical MSI-X info directly and plub
through the proper mapping so that the S1 -> S2 translation setup by
the guest actually works right.
If you don't do that then you can just have Linux adjust the MSI-X
addr like intel does and use the RMR trick from ARM to get the IMSIC
pages into the S1 IOVA. Still no need for a remapping domain.
It's so complex, so I think you will have an easier time doing tiny
small steps at a time..
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-21 16:12 ` Jason Gunthorpe
@ 2026-08-21 17:12 ` Andrew Jones
2026-08-21 17:20 ` Jason Gunthorpe
0 siblings, 1 reply; 39+ messages in thread
From: Andrew Jones @ 2026-08-21 17:12 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 01:12:56PM -0300, Jason Gunthorpe wrote:
> On Fri, Aug 21, 2026 at 05:18:49PM +0200, Andrew Jones wrote:
> > On Fri, Aug 21, 2026 at 11:31:55AM -0300, Jason Gunthorpe wrote:
> > > On Fri, Aug 21, 2026 at 04:23:22PM +0200, Andrew Jones wrote:
> > > > > But RISC-V IOMMU is also not involved in interrupt translation. Beyond
> > > > > it could remap the PAs which represent the CPUs, but you are not using
> > > > > it that way since it is statically setup.
> > > > >
> > > > > So I'm not sure why the iommu gets an IR? Isn't it the same as ARM
> > > > > where the IMSIC itself just has a wack of PAs it needs to access so it
> > > > > calls iommu_dma_prepare_msi() to do it?
> > > >
> > > > Right, this series is only remapping IMSIC PAs, not using the IOMMU MSI
> > > > table. The MSI table must be disabled when second-stage translation is
> > > > Bare, so it cannot be used for the host mappings here. It will be needed
> > > > for guest interrupt files (the irqbypass series I'm also working on),
> > > > where this IR irqdomain can then intercept irq_set_vcpu_affinity().
> > > > Also, keeping the MSI setup in this IR irqdomain avoids putting IOMMU
> > > > mapping knowledge in the IMSIC driver.
> > >
> > > It's ok for the IMSIC driver to call iommu_dma_prepare_msi(), that's
> > > the architecutre of this at least.
> > >
> > > Until you get to adding something more complicated, eg irqbypass, I'd
> > > suggest keeping this series simple.
> > >
> > > I guess I would wonder why the IOMMU needs to be involved in changing
> > > the mapping down the road, why not just reprogram the MSI-X address?
> > >
> >
> > For guest interrupts the MSI-X address is a guest IMSIC address.
> > Reprogramming it handles a guest reroute, but not vcpu migration (the
> > guest address stays the same while its host backing moves between
> > guest interrupt files and MRIFs).
>
> Linux isn't prepared to have a guest controlled MSI-X addr,
> AFAIK. I've wanted this for a long time but we are not there..
>
> Does riscv absolutely require this? I suppose you have a new problem
> where you don't know what affinity the guest is selecting when it
> provides a MSI-X addr as you don't know what physical IMSIC page is
> under its S1 IOVA?
The IOMMU MSI table operates after S1, so it matches the resulting IMSIC
GPA. With a vIOMMU, the guest may use any MSI IOVA that its S1 maps to
that GPA. With S1 Bare, the device uses the GPA directly. In either case
the hypervisor provides the guest IMSIC topology and the guest interrupt
file or MRIF target needed to program the MSI table (through the IRQ
domain).
>
> > The IOMMU driver updates that mapping through "the hypervisor to
> > IOMMU driver interface", a.k.a. the IRQ domain's
> > irq_set_vcpu_affinity() callback. That mapping cannot be prepared
> > ahead of time because only the hypervisor knows the target, and it
> > may change later.
>
> Nicolin has a series that worked toward making ARM work properly, it
> is not at all like this and it isn't done with remapping domains.
I'll look it up.
>
> You want to preserve the guests physical MSI-X info directly and plub
> through the proper mapping so that the S1 -> S2 translation setup by
> the guest actually works right.
>
> If you don't do that then you can just have Linux adjust the MSI-X
> addr like intel does and use the RMR trick from ARM to get the IMSIC
> pages into the S1 IOVA. Still no need for a remapping domain.
>
> It's so complex, so I think you will have an easier time doing tiny
> small steps at a time..
>
Right, I have a working prototype where the hypervisor manages the MSI
table through the IOMMU driver's IR irqdomain with
irq_set_vcpu_affinity(). Splitting that into upstreamable steps is what
I'm working on now.
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi()
2026-08-21 17:12 ` Andrew Jones
@ 2026-08-21 17:20 ` Jason Gunthorpe
0 siblings, 0 replies; 39+ messages in thread
From: Jason Gunthorpe @ 2026-08-21 17:20 UTC (permalink / raw)
To: Andrew Jones
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 07:12:15PM +0200, Andrew Jones wrote:
> > Does riscv absolutely require this? I suppose you have a new problem
> > where you don't know what affinity the guest is selecting when it
> > provides a MSI-X addr as you don't know what physical IMSIC page is
> > under its S1 IOVA?
>
> The IOMMU MSI table operates after S1, so it matches the resulting IMSIC
> GPA. With a vIOMMU, the guest may use any MSI IOVA that its S1 maps to
> that GPA. With S1 Bare, the device uses the GPA directly. In either case
> the hypervisor provides the guest IMSIC topology and the guest interrupt
> file or MRIF target needed to program the MSI table (through the IRQ
> domain).
I suspect you should have an IRQ domain for the msiptp functionality,
but IDK what it should do... Is this basically to remap vCPUs to
pCPUs?
The viommu can set the msi_addr_pattern
The existing msi stuff can place the physical ICMC at the right spot
in the S1 to give to the viommu (Nicolin's series may be needed for
this too, I forget)
Not sure how you provide the guest MSI descriptor and have the irq
layer program it directly..
It is unfortunate you can't learn the vCPU the MSI is targetting from
the MSI descriptor, in terms of linux that's a pretty difficult choice
to implement.
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v4 03/21] iommu/dma: Add iommu_dma_sw_map_msi()
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
2026-08-20 21:41 ` [PATCH v4 01/21] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
2026-08-20 21:41 ` [PATCH v4 02/21] iommufd: Add iommufd_sw_map_msi() Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 04/21] iommu/dma: Add iommu_dma_map_msi() Andrew Jones
` (17 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
Add a descriptor-free counterpart to iommu_dma_sw_msi(). The existing
function is tied to a struct msi_desc and stores the result in the
descriptor. This variant returns the IOVA directly so callers can
pre-map MSI targets before any descriptor has been allocated.
Callers may pass a required mapping size so MSI doorbells that must
not share a larger IOMMU leaf, such as RISC-V IMSIC files, can fail
before a mapping is installed.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/dma-iommu.c | 48 ++++++++++++++++++++++++++++++++-------
drivers/iommu/dma-iommu.h | 12 ++++++++++
2 files changed, 52 insertions(+), 8 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 9a07eb39336e..3ce9f4878d92 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2247,24 +2247,56 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
return NULL;
}
-int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr)
+/*
+ * Descriptor-free counterpart to iommu_dma_sw_msi(). Maps an MSI physical
+ * page into the domain and returns the IOVA and mapping granule. Used for
+ * pre-mapping MSI targets before any MSI descriptor has been set.
+ *
+ * The caller must pass a device attached to @domain and hold @dev's IOMMU
+ * group mutex. If @required_size is non-zero then it must exactly match the
+ * domain's MSI mapping granule. @msi_iova and @msi_shift must be non-NULL.
+ */
+int iommu_dma_sw_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift)
{
- struct device *dev = msi_desc_to_dev(desc);
const struct iommu_dma_msi_page *msi_page;
+ size_t size;
- if (!has_msi_cookie(domain)) {
- msi_desc_set_iommu_msi_iova(desc, 0, 0);
+ *msi_iova = 0;
+ *msi_shift = 0;
+
+ if (!has_msi_cookie(domain))
return 0;
- }
+
+ size = cookie_msi_granule(domain);
+ if (required_size && size != required_size)
+ return -EOPNOTSUPP;
iommu_group_mutex_assert(dev);
msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
if (!msi_page)
return -ENOMEM;
- msi_desc_set_iommu_msi_iova(desc, msi_page->iova,
- ilog2(cookie_msi_granule(domain)));
+ *msi_iova = msi_page->iova;
+ *msi_shift = ilog2(size);
+ return 0;
+}
+
+int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
+ phys_addr_t msi_addr)
+{
+ struct device *dev = msi_desc_to_dev(desc);
+ dma_addr_t msi_iova;
+ unsigned int msi_shift;
+ int ret;
+
+ ret = iommu_dma_sw_map_msi(domain, dev, msi_addr, 0, &msi_iova, &msi_shift);
+ if (ret)
+ return ret;
+
+ msi_desc_set_iommu_msi_iova(desc, msi_iova, msi_shift);
return 0;
}
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 040d00252563..3e1adeafdcac 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -19,6 +19,10 @@ int iommu_dma_init_fq(struct iommu_domain *domain);
void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
+int iommu_dma_sw_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift);
int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
phys_addr_t msi_addr);
@@ -53,6 +57,14 @@ static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_he
{
}
+static inline int iommu_dma_sw_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift)
+{
+ return -ENODEV;
+}
+
static inline int iommu_dma_sw_msi(struct iommu_domain *domain,
struct msi_desc *desc, phys_addr_t msi_addr)
{
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 04/21] iommu/dma: Add iommu_dma_map_msi()
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (2 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 03/21] iommu/dma: Add iommu_dma_sw_map_msi() Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 05/21] iommu: Document MSI mapping during domain replacement Andrew Jones
` (16 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
Interrupt-remapping drivers may need to map MSI targets before any MSI
descriptor exists and retain their own PA-to-IOVA lookup table. Add
iommu_dma_map_msi() to return the IOVA and mapping granule for both
DMA-IOMMU and iommufd domains.
Allow callers to require an exact mapping size. This prevents doorbells
such as RISC-V IMSIC files from sharing a larger IOMMU leaf.
Building a table requires the domain to remain stable across every
mapping. The IOMMU group mutex provides that serialization, but its
structure is private to the core. Expose lock helpers and a scoped guard
so external callers can hold it for the complete operation.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/dma-iommu.c | 22 +------
drivers/iommu/dma-iommu.h | 8 ---
drivers/iommu/iommu-priv.h | 8 ---
drivers/iommu/iommu.c | 104 ++++++++++++++++++++++++++++-----
drivers/iommu/iommufd/driver.c | 31 ++--------
include/linux/iommu.h | 31 ++++++++++
6 files changed, 127 insertions(+), 77 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 3ce9f4878d92..39ee5f0b1da9 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2248,9 +2248,9 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
}
/*
- * Descriptor-free counterpart to iommu_dma_sw_msi(). Maps an MSI physical
- * page into the domain and returns the IOVA and mapping granule. Used for
- * pre-mapping MSI targets before any MSI descriptor has been set.
+ * Maps an MSI physical page into the domain and returns the IOVA and
+ * mapping granule. Used for pre-mapping MSI targets before any MSI
+ * descriptor has been set.
*
* The caller must pass a device attached to @domain and hold @dev's IOMMU
* group mutex. If @required_size is non-zero then it must exactly match the
@@ -2284,22 +2284,6 @@ int iommu_dma_sw_map_msi(struct iommu_domain *domain,
return 0;
}
-int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr)
-{
- struct device *dev = msi_desc_to_dev(desc);
- dma_addr_t msi_iova;
- unsigned int msi_shift;
- int ret;
-
- ret = iommu_dma_sw_map_msi(domain, dev, msi_addr, 0, &msi_iova, &msi_shift);
- if (ret)
- return ret;
-
- msi_desc_set_iommu_msi_iova(desc, msi_iova, msi_shift);
- return 0;
-}
-
static int iommu_dma_init(void)
{
if (is_kdump_kernel())
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 3e1adeafdcac..7d707157aebb 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -23,8 +23,6 @@ int iommu_dma_sw_map_msi(struct iommu_domain *domain,
struct device *dev, phys_addr_t msi_addr,
size_t required_size, dma_addr_t *msi_iova,
unsigned int *msi_shift);
-int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr);
extern bool iommu_dma_forcedac;
@@ -65,11 +63,5 @@ static inline int iommu_dma_sw_map_msi(struct iommu_domain *domain,
return -ENODEV;
}
-static inline int iommu_dma_sw_msi(struct iommu_domain *domain,
- struct msi_desc *desc, phys_addr_t msi_addr)
-{
- return -ENODEV;
-}
-
#endif /* CONFIG_IOMMU_DMA */
#endif /* __DMA_IOMMU_H */
diff --git a/drivers/iommu/iommu-priv.h b/drivers/iommu/iommu-priv.h
index f60373cd2f70..109aca07470b 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -53,18 +53,10 @@ int iommu_replace_group_handle(struct iommu_group *group,
struct iommu_attach_handle *handle);
#if IS_ENABLED(CONFIG_IOMMUFD_DRIVER_CORE) && IS_ENABLED(CONFIG_IRQ_MSI_IOMMU)
-int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr);
int iommufd_sw_map_msi(struct iommu_domain *domain, struct device *dev,
phys_addr_t msi_addr, size_t required_size,
dma_addr_t *msi_iova, unsigned int *msi_shift);
#else /* !CONFIG_IOMMUFD_DRIVER_CORE || !CONFIG_IRQ_MSI_IOMMU */
-static inline int iommufd_sw_msi(struct iommu_domain *domain,
- struct msi_desc *desc, phys_addr_t msi_addr)
-{
- return -EOPNOTSUPP;
-}
-
static inline int iommufd_sw_map_msi(struct iommu_domain *domain,
struct device *dev, phys_addr_t msi_addr,
size_t required_size, dma_addr_t *msi_iova,
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index a3dea7353500..84b60d0f8c23 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -1364,15 +1364,46 @@ void iommu_group_remove_device(struct device *dev)
}
EXPORT_SYMBOL_GPL(iommu_group_remove_device);
-#if IS_ENABLED(CONFIG_LOCKDEP) && IS_ENABLED(CONFIG_IOMMU_API)
+#if IS_ENABLED(CONFIG_IOMMU_API)
+/*
+ * iommu_group_mutex_lock(), iommu_group_mutex_unlock(), and
+ * iommu_group_mutex_assert() must be called after device group param is
+ * set.
+ */
+
+/**
+ * iommu_group_mutex_lock - Lock the iommu group mutex for a device
+ * @dev: the device whose group mutex should be locked
+ *
+ * Callers that need to invoke a function documented as requiring the
+ * device's iommu group mutex (e.g. iommu_dma_map_msi()) from outside
+ * drivers/iommu/ use this instead of reaching into struct iommu_group,
+ * which is private to the core. Must be paired with
+ * iommu_group_mutex_unlock().
+ */
+void iommu_group_mutex_lock(struct device *dev)
+{
+ mutex_lock(&dev->iommu_group->mutex);
+}
+EXPORT_SYMBOL_GPL(iommu_group_mutex_lock);
+
+/**
+ * iommu_group_mutex_unlock - Unlock the iommu group mutex for a device
+ * @dev: the device whose group mutex should be unlocked
+ */
+void iommu_group_mutex_unlock(struct device *dev)
+{
+ mutex_unlock(&dev->iommu_group->mutex);
+}
+EXPORT_SYMBOL_GPL(iommu_group_mutex_unlock);
+
+#if IS_ENABLED(CONFIG_LOCKDEP)
/**
* iommu_group_mutex_assert - Check device group mutex lock
* @dev: the device that has group param set
*
* This function is called by an iommu driver to check whether it holds
* group mutex lock for the given device or not.
- *
- * Note that this function must be called after device group param is set.
*/
void iommu_group_mutex_assert(struct device *dev)
{
@@ -1381,7 +1412,8 @@ void iommu_group_mutex_assert(struct device *dev)
lockdep_assert_held(&group->mutex);
}
EXPORT_SYMBOL_GPL(iommu_group_mutex_assert);
-#endif
+#endif /* CONFIG_LOCKDEP */
+#endif /* CONFIG_IOMMU_API */
static struct device *iommu_group_first_dev(struct iommu_group *group)
{
@@ -4223,6 +4255,52 @@ void pci_dev_reset_iommu_done(struct pci_dev *pdev)
EXPORT_SYMBOL_GPL(pci_dev_reset_iommu_done);
#if IS_ENABLED(CONFIG_IRQ_MSI_IOMMU)
+/**
+ * iommu_dma_map_msi() - Map an MSI page in an IOMMU domain
+ * @domain: IOMMU domain to map into
+ * @dev: Device used to allocate the IOVA
+ * @msi_addr: MSI target address to be mapped
+ * @required_size: Required mapping size, or 0 to accept any size
+ * @msi_iova: IOVA for @msi_addr, or 0 for passthrough
+ * @msi_shift: Mapping granule shift, or 0 for passthrough
+ *
+ * The caller must hold @dev's iommu group mutex, e.g. via
+ * iommu_group_mutex_lock()/iommu_group_mutex_unlock(). This function does
+ * not take the mutex itself because callers building a table of mappings
+ * (e.g. one IOVA per possible CPU's IMSIC page) call it in a loop; locking
+ * inside would mean re-acquiring the mutex on every iteration and would not
+ * stop the domain from changing between iterations, leaving the table
+ * inconsistent. The caller locks once around the whole loop instead.
+ *
+ * Return: 0 on success or negative error code if the mapping failed.
+ */
+int iommu_dma_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift)
+{
+ *msi_iova = 0;
+ *msi_shift = 0;
+
+ if (!domain)
+ return -EINVAL;
+
+ if (domain->type == IOMMU_DOMAIN_IDENTITY)
+ return 0;
+
+ switch (domain->cookie_type) {
+ case IOMMU_COOKIE_DMA_MSI:
+ case IOMMU_COOKIE_DMA_IOVA:
+ return iommu_dma_sw_map_msi(domain, dev, msi_addr,
+ required_size, msi_iova, msi_shift);
+ case IOMMU_COOKIE_IOMMUFD:
+ return iommufd_sw_map_msi(domain, dev, msi_addr,
+ required_size, msi_iova, msi_shift);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
/**
* iommu_dma_prepare_msi() - Map the MSI page in the IOMMU domain
* @desc: MSI descriptor, will store the MSI page
@@ -4238,6 +4316,8 @@ int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr)
{
struct device *dev = msi_desc_to_dev(desc);
struct iommu_group *group = dev->iommu_group;
+ dma_addr_t msi_iova;
+ unsigned int msi_shift;
int ret = 0;
if (!group)
@@ -4246,18 +4326,10 @@ int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr)
mutex_lock(&group->mutex);
/* An IDENTITY domain must pass through */
if (group->domain && group->domain->type != IOMMU_DOMAIN_IDENTITY) {
- switch (group->domain->cookie_type) {
- case IOMMU_COOKIE_DMA_MSI:
- case IOMMU_COOKIE_DMA_IOVA:
- ret = iommu_dma_sw_msi(group->domain, desc, msi_addr);
- break;
- case IOMMU_COOKIE_IOMMUFD:
- ret = iommufd_sw_msi(group->domain, desc, msi_addr);
- break;
- default:
- ret = -EOPNOTSUPP;
- break;
- }
+ ret = iommu_dma_map_msi(group->domain, dev, msi_addr, 0,
+ &msi_iova, &msi_shift);
+ if (!ret)
+ msi_desc_set_iommu_msi_iova(desc, msi_iova, msi_shift);
}
mutex_unlock(&group->mutex);
return ret;
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 69b3dbcbee3b..b39796bc7251 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -249,11 +249,11 @@ int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi_install, "IOMMUFD_INTERNAL");
/*
- * Descriptor-free counterpart to iommufd_sw_msi(). Maps an MSI physical page
- * into the domain and returns the IOVA. Used for pre-mapping MSI targets before
- * any MSI descriptor has been set (e.g. IMSIC doorbell pages). The IOVA is
- * global to the iommufd file descriptor: every domain and device using the
- * same MSI parameters gets the same IOVA.
+ * Maps an MSI physical page into the domain and returns the IOVA. Used for
+ * pre-mapping MSI targets before any MSI descriptor has been set (e.g.
+ * IMSIC doorbell pages). The IOVA is global to the iommufd file
+ * descriptor: every domain and device using the same MSI parameters gets
+ * the same IOVA.
*
* msi_addr is the exact byte offset of the MSI doorbell; the caller must have
* verified it is contained within an MMIO region safe to map at PAGE_SIZE. If
@@ -321,27 +321,6 @@ int iommufd_sw_map_msi(struct iommu_domain *domain, struct device *dev,
return 0;
}
EXPORT_SYMBOL_NS_GPL(iommufd_sw_map_msi, "IOMMUFD");
-
-/*
- * Called by the irq layer when the platform translates MSI addresses through
- * the IOMMU. Wraps iommufd_sw_map_msi() and stores the result in the descriptor.
- */
-int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr)
-{
- dma_addr_t msi_iova;
- unsigned int msi_shift;
- int rc;
-
- rc = iommufd_sw_map_msi(domain, msi_desc_to_dev(desc), msi_addr,
- 0, &msi_iova, &msi_shift);
- if (rc)
- return rc;
-
- msi_desc_set_iommu_msi_iova(desc, msi_iova, msi_shift);
- return 0;
-}
-EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi, "IOMMUFD");
#endif
MODULE_DESCRIPTION("iommufd code shared with builtin modules");
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index ac43b8b93f14..868b14b63e77 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -8,6 +8,7 @@
#define __LINUX_IOMMU_H
#include <linux/scatterlist.h>
+#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/errno.h>
@@ -1561,8 +1562,22 @@ static inline void pci_dev_reset_iommu_done(struct pci_dev *pdev)
#ifdef CONFIG_IRQ_MSI_IOMMU
#ifdef CONFIG_IOMMU_API
+int iommu_dma_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift);
int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr);
#else
+static inline int iommu_dma_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift)
+{
+ *msi_iova = 0;
+ *msi_shift = 0;
+ return 0;
+}
+
static inline int iommu_dma_prepare_msi(struct msi_desc *desc,
phys_addr_t msi_addr)
{
@@ -1571,6 +1586,22 @@ static inline int iommu_dma_prepare_msi(struct msi_desc *desc,
#endif /* CONFIG_IOMMU_API */
#endif /* CONFIG_IRQ_MSI_IOMMU */
+#if IS_ENABLED(CONFIG_IOMMU_API)
+void iommu_group_mutex_lock(struct device *dev);
+void iommu_group_mutex_unlock(struct device *dev);
+#else
+static inline void iommu_group_mutex_lock(struct device *dev) { }
+static inline void iommu_group_mutex_unlock(struct device *dev) { }
+#endif
+
+/*
+ * scoped_guard(iommu_group, dev) { ... } locks dev's iommu group mutex for
+ * the scope of the block. See iommu_group_mutex_lock().
+ */
+DEFINE_LOCK_GUARD_1(iommu_group, struct device,
+ iommu_group_mutex_lock(_T->lock),
+ iommu_group_mutex_unlock(_T->lock))
+
#if IS_ENABLED(CONFIG_LOCKDEP) && IS_ENABLED(CONFIG_IOMMU_API)
void iommu_group_mutex_assert(struct device *dev);
#else
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 05/21] iommu: Document MSI mapping during domain replacement
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (3 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 04/21] iommu/dma: Add iommu_dma_map_msi() Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 06/21] genirq/msi: Provide DOMAIN_BUS_MSI_REMAP Andrew Jones
` (15 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
Interrupt-remapping drivers may need to prepare software MSI mappings
for an incoming domain from their attach_dev() callback. At that point
the device is not yet attached to the supplied domain and, for an
iommufd replacement, the current attach handle still describes the old
domain.
Document that iommu_dma_map_msi() supports this use. Clarify the DMA
backend only requires a compatible device and that the iommufd backend
intentionally combines context from the current handle with the
explicitly supplied destination HWPT.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/dma-iommu.c | 7 ++++---
drivers/iommu/iommu.c | 5 +++++
drivers/iommu/iommufd/driver.c | 8 +++++---
3 files changed, 14 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 39ee5f0b1da9..030948a7e6b9 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2252,9 +2252,10 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
* mapping granule. Used for pre-mapping MSI targets before any MSI
* descriptor has been set.
*
- * The caller must pass a device attached to @domain and hold @dev's IOMMU
- * group mutex. If @required_size is non-zero then it must exactly match the
- * domain's MSI mapping granule. @msi_iova and @msi_shift must be non-NULL.
+ * The caller must pass a device compatible with @domain and hold @dev's IOMMU
+ * group mutex. @domain may be the incoming domain of an attach_dev() callback.
+ * If @required_size is non-zero then it must exactly match the domain's MSI
+ * mapping granule. @msi_iova and @msi_shift must be non-NULL.
*/
int iommu_dma_sw_map_msi(struct iommu_domain *domain,
struct device *dev, phys_addr_t msi_addr,
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index 84b60d0f8c23..389d67c4bda3 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -4272,6 +4272,11 @@ EXPORT_SYMBOL_GPL(pci_dev_reset_iommu_done);
* stop the domain from changing between iterations, leaving the table
* inconsistent. The caller locks once around the whole loop instead.
*
+ * @domain may be the incoming domain of an attach_dev() callback. In that
+ * case, @dev need not be attached to @domain yet, but it must be compatible
+ * with @domain and the caller must keep the current group attachment stable
+ * until the callback completes.
+ *
* Return: 0 on success or negative error code if the mapping failed.
*/
int iommu_dma_map_msi(struct iommu_domain *domain,
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index b39796bc7251..358cc6d87941 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -281,9 +281,11 @@ int iommufd_sw_map_msi(struct iommu_domain *domain, struct device *dev,
return -EOPNOTSUPP;
/*
- * It is safe to call iommu_attach_handle_get() here because the iommu
- * core code invokes this under the group mutex which also prevents any
- * change of the attach handle for the duration of this function.
+ * During a domain replacement this returns the current handle, not the
+ * handle for @domain. That is intentional: the current handle supplies
+ * the stable iommufd context and group MSI window, while @domain selects
+ * the HWPT where the mapping is installed. The group mutex prevents the
+ * current handle from changing for the duration of this function.
*/
iommu_group_mutex_assert(dev);
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 06/21] genirq/msi: Provide DOMAIN_BUS_MSI_REMAP
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (4 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 05/21] iommu: Document MSI mapping during domain replacement Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 07/21] irqchip/riscv-imsic: Compose MSI updates through the hierarchy Andrew Jones
` (14 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu,
Nutty Liu
Add a bus token for MSI domains that remap interrupts, needed by the
upcoming RISC-V IOMMU interrupt-remapping domain to distinguish itself
from NEXUS domains. The token is generic because remapping itself is
the only property that needs to be conveyed -- there is nothing
RISC-V-specific about it.
Such a domain implements init_dev_msi_info() via
msi_parent_init_dev_msi_info(), which leaves 'domain' pointing at the
NEXUS domain while 'real_parent' points at the remap domain itself.
Accept that combination in msi_lib_init_dev_msi_info(); no other
msi-lib changes are needed.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
---
drivers/irqchip/irq-msi-lib.c | 8 ++++----
include/linux/irqdomain_defs.h | 1 +
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/drivers/irqchip/irq-msi-lib.c b/drivers/irqchip/irq-msi-lib.c
index 45e0ed3134ce..9027dfbab15e 100644
--- a/drivers/irqchip/irq-msi-lib.c
+++ b/drivers/irqchip/irq-msi-lib.c
@@ -36,14 +36,14 @@ bool msi_lib_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
return false;
/*
- * MSI parent domain specific settings. For now there is only the
- * root parent domain, e.g. NEXUS, acting as a MSI parent, but it is
- * possible to stack MSI parents. See x86 vector -> irq remapping
+ * MSI parent domain specific settings. There may be only the root
+ * parent domain, e.g. NEXUS, acting as a MSI parent, or there may
+ * be stacked MSI parents, typically used for remapping.
*/
if (domain->bus_token == pops->bus_select_token) {
if (WARN_ON_ONCE(domain != real_parent))
return false;
- } else {
+ } else if (real_parent->bus_token != DOMAIN_BUS_MSI_REMAP) {
WARN_ON_ONCE(1);
return false;
}
diff --git a/include/linux/irqdomain_defs.h b/include/linux/irqdomain_defs.h
index 3a03bdfeeee9..954cf585b3c4 100644
--- a/include/linux/irqdomain_defs.h
+++ b/include/linux/irqdomain_defs.h
@@ -26,6 +26,7 @@ enum irq_domain_bus_token {
DOMAIN_BUS_AMDVI,
DOMAIN_BUS_DEVICE_MSI,
DOMAIN_BUS_WIRED_TO_MSI,
+ DOMAIN_BUS_MSI_REMAP,
};
#endif /* _LINUX_IRQDOMAIN_DEFS_H */
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 07/21] irqchip/riscv-imsic: Compose MSI updates through the hierarchy
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (5 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 06/21] genirq/msi: Provide DOMAIN_BUS_MSI_REMAP Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 08/21] iommu/riscv: Add IRQ domain for interrupt remapping Andrew Jones
` (13 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
imsic_irq_set_affinity() currently writes a newly composed MSI message
directly to the device. This bypasses intermediate irqdomains, so an
IOMMU remapping domain cannot translate the target after an affinity
change.
Compose affinity updates from the top of the hierarchy instead. This
allows each intermediate domain to update the message and any descriptor
state before the device is programmed.
Non-atomic moves temporarily use the old address with the new interrupt
ID before switching to the new vector. Preserve that transition by
composing the temporary message before publishing the new vector, then
compose the final message after publishing it, but before the old vector
is retired.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/irqchip/irq-riscv-imsic-platform.c | 27 ++++++++--------------
1 file changed, 10 insertions(+), 17 deletions(-)
diff --git a/drivers/irqchip/irq-riscv-imsic-platform.c b/drivers/irqchip/irq-riscv-imsic-platform.c
index 643c8e459611..5634641dc223 100644
--- a/drivers/irqchip/irq-riscv-imsic-platform.c
+++ b/drivers/irqchip/irq-riscv-imsic-platform.c
@@ -90,19 +90,12 @@ static void imsic_irq_compose_msg(struct irq_data *d, struct msi_msg *msg)
}
#ifdef CONFIG_SMP
-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);
- irq_data_get_irq_chip(d)->irq_write_msi_msg(d, &msg);
-}
-
static int imsic_irq_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
bool force)
{
+ struct irq_data *top = irq_get_irq_data(d->irq);
struct imsic_vector *old_vec, *new_vec;
- struct imsic_vector tmp_vec;
+ struct msi_msg msg = { };
/*
* Requirements for the downstream irqdomains (or devices):
@@ -153,20 +146,20 @@ static int imsic_irq_set_affinity(struct irq_data *d, const struct cpumask *mask
*/
if (!irq_can_move_in_process_context(d) &&
new_vec->local_id != old_vec->local_id) {
- /* Setup temporary vector */
- tmp_vec.cpu = old_vec->cpu;
- tmp_vec.local_id = new_vec->local_id;
-
/* Point device to the temporary vector */
- imsic_msi_update_msg(irq_get_irq_data(d->irq), &tmp_vec);
+ BUG_ON(irq_chip_compose_msi_msg(top, &msg));
+ msg.data = new_vec->local_id;
+ irq_data_get_irq_chip(top)->irq_write_msi_msg(top, &msg);
}
- /* Point device to the new vector */
- imsic_msi_update_msg(irq_get_irq_data(d->irq), new_vec);
-
/* Update irq descriptors with the new vector */
d->chip_data = new_vec;
+ /* Point device to the new vector */
+ memset(&msg, 0, sizeof(msg));
+ BUG_ON(irq_chip_compose_msi_msg(top, &msg));
+ irq_data_get_irq_chip(top)->irq_write_msi_msg(top, &msg);
+
/* Update effective affinity */
irq_data_update_effective_affinity(d, cpumask_of(new_vec->cpu));
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 08/21] iommu/riscv: Add IRQ domain for interrupt remapping
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (6 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 07/21] irqchip/riscv-imsic: Compose MSI updates through the hierarchy Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 09/21] iommu/riscv: Refresh platform MSI domain before IR setup Andrew Jones
` (12 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
Create a per-device MSI parent domain as a hook for interrupt remapping.
Keep the remapping tables with the attached paging domain because MSI
IOVA mappings are part of its page tables.
Initially, the new domain only forwards operations to its parent. Later
commits add MSI target mappings for paging domains, while non-paging
domains continue to use physical IMSIC addresses.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/Makefile | 2 +-
drivers/iommu/riscv/iommu-ir.c | 118 ++++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.c | 46 ++++++-----
drivers/iommu/riscv/iommu.h | 27 +++++++
include/linux/irqchip/riscv-imsic.h | 7 ++
5 files changed, 181 insertions(+), 19 deletions(-)
create mode 100644 drivers/iommu/riscv/iommu-ir.c
diff --git a/drivers/iommu/riscv/Makefile b/drivers/iommu/riscv/Makefile
index b5929f9f23e6..9c83f877d50f 100644
--- a/drivers/iommu/riscv/Makefile
+++ b/drivers/iommu/riscv/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
-obj-y += iommu.o iommu-platform.o
+obj-y += iommu.o iommu-ir.o iommu-platform.o
obj-$(CONFIG_RISCV_IOMMU_PCI) += iommu-pci.o
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
new file mode 100644
index 000000000000..0aab0efeb70e
--- /dev/null
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * IOMMU Interrupt Remapping
+ *
+ * Copyright (c) 2026 Qualcomm Technologies, Inc.
+ */
+#include <linux/cleanup.h>
+#include <linux/msi.h>
+#include <linux/slab.h>
+
+#include "iommu.h"
+
+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,
+};
+
+static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain,
+ unsigned int irq_base, unsigned int nr_irqs,
+ void *arg)
+{
+ int ret;
+
+ ret = irq_domain_alloc_irqs_parent(irqdomain, irq_base, nr_irqs, arg);
+ if (ret)
+ return ret;
+
+ for (unsigned int i = 0; i < nr_irqs; i++) {
+ struct irq_data *data = irq_domain_get_irq_data(irqdomain, irq_base + i);
+
+ data->chip = &riscv_iommu_ir_irq_chip;
+ }
+
+ return 0;
+}
+
+static const struct irq_domain_ops riscv_iommu_ir_irq_domain_ops = {
+ .alloc = riscv_iommu_ir_irq_domain_alloc_irqs,
+ .free = irq_domain_free_irqs_parent,
+};
+
+static const struct msi_parent_ops riscv_iommu_ir_msi_parent_ops = {
+ .prefix = "IR-",
+ .supported_flags = MSI_GENERIC_FLAGS_MASK |
+ MSI_FLAG_PCI_MSIX,
+ .required_flags = MSI_FLAG_USE_DEF_DOM_OPS |
+ MSI_FLAG_USE_DEF_CHIP_OPS |
+ MSI_FLAG_PCI_MSI_MASK_PARENT,
+ .chip_flags = MSI_CHIP_FLAG_SET_ACK,
+ .init_dev_msi_info = msi_parent_init_dev_msi_info,
+};
+
+struct irq_domain *riscv_iommu_ir_irq_domain_create(struct device *dev,
+ struct riscv_iommu_info *info)
+{
+ struct irq_domain *irqparent = dev_get_msi_domain(dev);
+ char *fwname __free(kfree) = NULL;
+ struct irq_domain *irqdomain;
+ struct fwnode_handle *fn;
+
+ if (!irqparent)
+ return NULL;
+
+ fwname = kasprintf(GFP_KERNEL, "IOMMU-IR-%s", dev_name(dev));
+ if (!fwname)
+ return ERR_PTR(-ENOMEM);
+
+ fn = irq_domain_alloc_named_fwnode(fwname);
+ if (!fn)
+ return ERR_PTR(-ENOMEM);
+
+ irqdomain = irq_domain_create_hierarchy(irqparent, 0, 0, fn,
+ &riscv_iommu_ir_irq_domain_ops, info);
+ if (!irqdomain) {
+ irq_domain_free_fwnode(fn);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /*
+ * The RISC-V IOMMU doesn't validate MSI data, so we can't set
+ * IRQ_DOMAIN_FLAG_ISOLATED_MSI. This means VFIO requires its
+ * allow_unsafe_interrupts module parameter.
+ */
+ irqdomain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
+ irqdomain->msi_parent_ops = &riscv_iommu_ir_msi_parent_ops;
+ irq_domain_update_bus_token(irqdomain, DOMAIN_BUS_MSI_REMAP);
+
+ dev_set_msi_domain(dev, irqdomain);
+
+ return irqdomain;
+}
+
+void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_info *info)
+{
+ struct fwnode_handle *fn;
+
+ if (!info->irqdomain)
+ return;
+
+ dev_set_msi_domain(dev, info->irqdomain->parent);
+ fn = info->irqdomain->fwnode;
+ irq_domain_remove(info->irqdomain);
+ info->irqdomain = NULL;
+ irq_domain_free_fwnode(fn);
+}
+
+int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
+ struct iommu_domain *old)
+{
+ return 0;
+}
+
+void riscv_iommu_ir_free_paging_domain(struct iommu_domain *iommu_domain)
+{
+}
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index cec3ddd7ab10..bfe606a0cb9a 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -19,9 +19,9 @@
#include <linux/init.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
+#include <linux/irqchip/riscv-imsic.h>
#include <linux/kernel.h>
#include <linux/pci.h>
-#include <linux/generic_pt/iommu.h>
#include "../iommu-pages.h"
#include "iommu-bits.h"
@@ -810,26 +810,9 @@ static int riscv_iommu_iodir_set_mode(struct riscv_iommu_device *iommu,
return 0;
}
-/* This struct contains protection domain specific IOMMU driver data. */
-struct riscv_iommu_domain {
- union {
- struct iommu_domain domain;
- struct pt_iommu_riscv_64 riscvpt;
- };
- struct list_head bonds;
- spinlock_t lock; /* protect bonds list updates. */
- int pscid;
-};
-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 *domain;
-};
-
/*
* Linkage between an iommu_domain and attached devices.
*
@@ -1258,6 +1241,8 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
WARN_ON(!list_empty(&domain->bonds));
+ riscv_iommu_ir_free_paging_domain(iommu_domain);
+
if ((int)domain->pscid > 0)
ida_free(&riscv_iommu_pscids, domain->pscid);
@@ -1289,6 +1274,7 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
struct pt_iommu_riscv_64_hw_info pt_info;
u64 fsc, ta;
+ int ret;
pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
@@ -1303,6 +1289,12 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
if (riscv_iommu_bond_link(domain, dev))
return -ENOMEM;
+ ret = riscv_iommu_ir_attach_paging_domain(iommu_domain, dev, old);
+ if (ret) {
+ riscv_iommu_bond_unlink(domain, dev);
+ return ret;
+ }
+
riscv_iommu_iodir_update(iommu, dev, fsc, ta);
riscv_iommu_bond_unlink(info->domain, dev);
info->domain = domain;
@@ -1430,6 +1422,7 @@ static int riscv_iommu_of_xlate(struct device *dev, const struct of_phandle_args
static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
{
struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
+ struct irq_domain *irqdomain = NULL;
struct riscv_iommu_device *iommu;
struct riscv_iommu_info *info;
struct riscv_iommu_dc *dc;
@@ -1453,6 +1446,21 @@ static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
info = kzalloc_obj(*info);
if (!info)
return ERR_PTR(-ENOMEM);
+
+ if (imsic_enabled()) {
+ irqdomain = riscv_iommu_ir_irq_domain_create(dev, info);
+ if (IS_ERR(irqdomain)) {
+ kfree(info);
+ return ERR_CAST(irqdomain);
+ }
+ }
+
+ /*
+ * irqdomain is NULL when it's not necessary; either there aren't
+ * any IMSICs or no MSI domain has been set up for the device.
+ */
+ info->irqdomain = irqdomain;
+
/*
* Allocate and pre-configure device context entries in
* the device directory. Do not mark the context valid yet.
@@ -1461,6 +1469,7 @@ static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
for (i = 0; i < fwspec->num_ids; i++) {
dc = riscv_iommu_get_dc(iommu, fwspec->ids[i]);
if (!dc) {
+ riscv_iommu_ir_irq_domain_remove(dev, info);
kfree(info);
return ERR_PTR(-ENODEV);
}
@@ -1478,6 +1487,7 @@ static void riscv_iommu_release_device(struct device *dev)
{
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+ riscv_iommu_ir_irq_domain_remove(dev, info);
kfree_rcu_mightsleep(info);
}
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 46df79dd5495..5d83537911b6 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -14,9 +14,29 @@
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
+#include <linux/irqdomain.h>
+#include <linux/generic_pt/iommu.h>
#include "iommu-bits.h"
+/* This struct contains protection domain specific IOMMU driver data. */
+struct riscv_iommu_domain {
+ union {
+ struct iommu_domain domain;
+ struct pt_iommu_riscv_64 riscvpt;
+ };
+ struct list_head bonds;
+ spinlock_t lock; /* protect bonds list updates. */
+ int pscid;
+};
+PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
+
+/* Private IOMMU data for managed devices, dev_iommu_priv_* */
+struct riscv_iommu_info {
+ struct riscv_iommu_domain *domain;
+ struct irq_domain *irqdomain;
+};
+
struct riscv_iommu_device;
struct riscv_iommu_queue {
@@ -66,6 +86,13 @@ int riscv_iommu_init(struct riscv_iommu_device *iommu);
void riscv_iommu_remove(struct riscv_iommu_device *iommu);
void riscv_iommu_disable(struct riscv_iommu_device *iommu);
+struct irq_domain *riscv_iommu_ir_irq_domain_create(struct device *dev,
+ struct riscv_iommu_info *info);
+void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_info *info);
+int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
+ struct iommu_domain *old);
+void riscv_iommu_ir_free_paging_domain(struct iommu_domain *iommu_domain);
+
#define riscv_iommu_readl(iommu, addr) \
readl_relaxed((iommu)->reg + (addr))
diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
index 61af3a5bea09..ce8fe1ead7a0 100644
--- a/include/linux/irqchip/riscv-imsic.h
+++ b/include/linux/irqchip/riscv-imsic.h
@@ -91,6 +91,13 @@ static inline const struct imsic_global_config *imsic_get_global_config(void)
#endif
+static inline bool imsic_enabled(void)
+{
+ const struct imsic_global_config *imsic_global = imsic_get_global_config();
+
+ return imsic_global && imsic_global->nr_ids;
+}
+
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_RISCV_IMSIC)
int imsic_platform_acpi_probe(struct fwnode_handle *fwnode);
struct fwnode_handle *imsic_acpi_get_fwnode(struct device *dev);
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 09/21] iommu/riscv: Refresh platform MSI domain before IR setup
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (7 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 08/21] iommu/riscv: Add IRQ domain for interrupt remapping Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 10/21] iommu/riscv: Prepare info->domain for concurrent RCU read access Andrew Jones
` (11 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
Platform devices receive their MSI domain when they are created. On
RISC-V, the IMSIC is itself a platform device, so consumer devices may
be created before the IMSIC driver registers its MSI domain. fw_devlink
delays consumer driver probing but does not refresh dev->msi.domain,
leaving it NULL when the IOMMU probe_device callback runs.
Refresh the OF or ACPI platform MSI domain before creating the
per-device interrupt-remapping domain. Leave PCI devices alone because
PCI establishes their MSI domain during device setup.
This mirrors the workaround currently used by other RISC-V platform MSI
consumers. A proposed driver-core change would perform the refresh from
platform_dma_configure(), but until that is merged the local refresh
avoids silently skipping interrupt-remapping setup.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu-ir.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index 0aab0efeb70e..3e08249015cd 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -4,8 +4,11 @@
*
* Copyright (c) 2026 Qualcomm Technologies, Inc.
*/
+#include <linux/acpi.h>
#include <linux/cleanup.h>
#include <linux/msi.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include "iommu.h"
@@ -53,14 +56,35 @@ static const struct msi_parent_ops riscv_iommu_ir_msi_parent_ops = {
.init_dev_msi_info = msi_parent_init_dev_msi_info,
};
+static void riscv_iommu_ir_refresh_msi_domain(struct device *dev)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
+ struct irq_domain *irqdomain;
+
+ if (dev_get_msi_domain(dev) || !dev_is_platform(dev))
+ return;
+
+ if (is_of_node(fwnode)) {
+ of_msi_configure(dev, to_of_node(fwnode));
+ } else if (is_acpi_device_node(fwnode)) {
+ fwnode = imsic_acpi_get_fwnode(dev);
+ irqdomain = irq_find_matching_fwnode(fwnode, DOMAIN_BUS_PLATFORM_MSI);
+ if (irqdomain)
+ dev_set_msi_domain(dev, irqdomain);
+ }
+}
+
struct irq_domain *riscv_iommu_ir_irq_domain_create(struct device *dev,
struct riscv_iommu_info *info)
{
- struct irq_domain *irqparent = dev_get_msi_domain(dev);
+ struct irq_domain *irqparent;
char *fwname __free(kfree) = NULL;
struct irq_domain *irqdomain;
struct fwnode_handle *fn;
+ riscv_iommu_ir_refresh_msi_domain(dev);
+ irqparent = dev_get_msi_domain(dev);
+
if (!irqparent)
return NULL;
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 10/21] iommu/riscv: Prepare info->domain for concurrent RCU read access
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (8 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 09/21] iommu/riscv: Refresh platform MSI domain before IR setup Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 11/21] iommu/riscv: Reserve an MSI IOVA window for iommufd Andrew Jones
` (10 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
An upcoming compose_msi_msg() implementation reads info->domain from
potentially atomic context, concurrent with domain switches. Apply
RCU in preparation.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu.c | 26 ++++++++++++++++----------
drivers/iommu/riscv/iommu.h | 3 ++-
2 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index bfe606a0cb9a..4e14c8412abc 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -865,17 +865,21 @@ static int riscv_iommu_bond_link(struct riscv_iommu_domain *domain,
return 0;
}
-static void riscv_iommu_bond_unlink(struct riscv_iommu_domain *domain,
+static void riscv_iommu_bond_unlink(struct iommu_domain *iommu_domain,
struct device *dev)
{
- struct riscv_iommu_device *iommu = dev_to_iommu(dev);
+ struct riscv_iommu_domain *domain;
+ struct riscv_iommu_device *iommu;
struct riscv_iommu_bond *bond, *found = NULL;
struct riscv_iommu_command cmd;
int count = 0;
- if (!domain)
+ if (!iommu_domain || !(iommu_domain->type & __IOMMU_DOMAIN_PAGING))
return;
+ domain = iommu_domain_to_riscv(iommu_domain);
+ iommu = dev_to_iommu(dev);
+
spin_lock(&domain->lock);
list_for_each_entry(bond, &domain->bonds, list) {
if (found && count)
@@ -1241,6 +1245,8 @@ static void riscv_iommu_free_paging_domain(struct iommu_domain *iommu_domain)
WARN_ON(!list_empty(&domain->bonds));
+ synchronize_rcu();
+
riscv_iommu_ir_free_paging_domain(iommu_domain);
if ((int)domain->pscid > 0)
@@ -1291,13 +1297,13 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
ret = riscv_iommu_ir_attach_paging_domain(iommu_domain, dev, old);
if (ret) {
- riscv_iommu_bond_unlink(domain, dev);
+ riscv_iommu_bond_unlink(iommu_domain, dev);
return ret;
}
riscv_iommu_iodir_update(iommu, dev, fsc, ta);
- riscv_iommu_bond_unlink(info->domain, dev);
- info->domain = domain;
+ riscv_iommu_bond_unlink(old, dev);
+ rcu_assign_pointer(info->domain, domain);
return 0;
}
@@ -1373,8 +1379,8 @@ static int riscv_iommu_attach_blocking_domain(struct iommu_domain *iommu_domain,
/* Make device context invalid, translation requests will fault w/ #258 */
riscv_iommu_iodir_update(iommu, dev, RISCV_IOMMU_FSC_BARE, 0);
- riscv_iommu_bond_unlink(info->domain, dev);
- info->domain = NULL;
+ riscv_iommu_bond_unlink(old, dev);
+ rcu_assign_pointer(info->domain, NULL);
return 0;
}
@@ -1394,8 +1400,8 @@ static int riscv_iommu_attach_identity_domain(struct iommu_domain *iommu_domain,
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
riscv_iommu_iodir_update(iommu, dev, RISCV_IOMMU_FSC_BARE, RISCV_IOMMU_PC_TA_V);
- riscv_iommu_bond_unlink(info->domain, dev);
- info->domain = NULL;
+ riscv_iommu_bond_unlink(old, dev);
+ rcu_assign_pointer(info->domain, NULL);
return 0;
}
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 5d83537911b6..ded18aec8010 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -15,6 +15,7 @@
#include <linux/types.h>
#include <linux/iopoll.h>
#include <linux/irqdomain.h>
+#include <linux/rcupdate.h>
#include <linux/generic_pt/iommu.h>
#include "iommu-bits.h"
@@ -33,7 +34,7 @@ PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
/* Private IOMMU data for managed devices, dev_iommu_priv_* */
struct riscv_iommu_info {
- struct riscv_iommu_domain *domain;
+ struct riscv_iommu_domain __rcu *domain;
struct irq_domain *irqdomain;
};
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 11/21] iommu/riscv: Reserve an MSI IOVA window for iommufd
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (9 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 10/21] iommu/riscv: Prepare info->domain for concurrent RCU read access Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 12/21] iommu/riscv: Pre-map IMSIC MSI targets Andrew Jones
` (9 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
iommufd requires an IOMMU_RESV_SW_MSI region to allocate stable IOVAs
for MSI targets. Advertise such a region when the device uses an IMSIC
MSI hierarchy so interrupt remapping can map IMSIC pages instead of
falling back to physical addresses.
Reserve one page per possible CPU, sufficient for each supervisor IMSIC
page. Place the window at 128 MiB, matching the established ARM SMMU MSI
IOVA convention rather than introducing an architecture-specific choice.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu-ir.c | 16 ++++++++++++++++
drivers/iommu/riscv/iommu.c | 8 ++++++++
drivers/iommu/riscv/iommu.h | 5 +++++
3 files changed, 29 insertions(+)
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index 3e08249015cd..de5e22ecbd63 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -140,3 +140,19 @@ 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)
{
}
+
+void riscv_iommu_ir_get_resv_regions(struct device *dev, struct list_head *head)
+{
+ struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+ struct iommu_resv_region *region;
+
+ if (!info || !info->irqdomain)
+ return;
+
+ region = iommu_alloc_resv_region(RISCV_IOMMU_MSI_IOVA_BASE,
+ (size_t)num_possible_cpus() * PAGE_SIZE,
+ IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO,
+ IOMMU_RESV_SW_MSI, GFP_KERNEL);
+ if (region)
+ list_add_tail(®ion->list, head);
+}
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 4e14c8412abc..049ca1852d7b 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -23,6 +23,7 @@
#include <linux/kernel.h>
#include <linux/pci.h>
+#include "../dma-iommu.h"
#include "../iommu-pages.h"
#include "iommu-bits.h"
#include "iommu.h"
@@ -1497,6 +1498,12 @@ static void riscv_iommu_release_device(struct device *dev)
kfree_rcu_mightsleep(info);
}
+static void riscv_iommu_get_resv_regions(struct device *dev, struct list_head *head)
+{
+ riscv_iommu_ir_get_resv_regions(dev, head);
+ iommu_dma_get_resv_regions(dev, head);
+}
+
static const struct iommu_ops riscv_iommu_ops = {
.of_xlate = riscv_iommu_of_xlate,
.identity_domain = &riscv_iommu_identity_domain,
@@ -1506,6 +1513,7 @@ static const struct iommu_ops riscv_iommu_ops = {
.device_group = riscv_iommu_device_group,
.probe_device = riscv_iommu_probe_device,
.release_device = riscv_iommu_release_device,
+ .get_resv_regions = riscv_iommu_get_resv_regions,
};
static int riscv_iommu_init_check(struct riscv_iommu_device *iommu)
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index ded18aec8010..16b3c9c4cf8c 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -16,10 +16,14 @@
#include <linux/iopoll.h>
#include <linux/irqdomain.h>
#include <linux/rcupdate.h>
+#include <linux/sizes.h>
#include <linux/generic_pt/iommu.h>
#include "iommu-bits.h"
+/* IOVA base for the SW MSI reservation; same convention as ARM SMMU. */
+#define RISCV_IOMMU_MSI_IOVA_BASE SZ_128M
+
/* This struct contains protection domain specific IOMMU driver data. */
struct riscv_iommu_domain {
union {
@@ -93,6 +97,7 @@ void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_inf
int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
struct iommu_domain *old);
void riscv_iommu_ir_free_paging_domain(struct iommu_domain *iommu_domain);
+void riscv_iommu_ir_get_resv_regions(struct device *dev, struct list_head *head);
#define riscv_iommu_readl(iommu, addr) \
readl_relaxed((iommu)->reg + (addr))
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 12/21] iommu/riscv: Pre-map IMSIC MSI targets
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (10 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 11/21] iommu/riscv: Reserve an MSI IOVA window for iommufd Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 13/21] iommu/riscv: Preserve MSI IOVA state across domain replacement Andrew Jones
` (8 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
IMSIC target addresses change with interrupt affinity, which may be
updated in atomic context. Therefore, an interrupt-remapping domain
cannot create mappings on demand while composing an MSI message.
Pre-map the supervisor IMSIC page for each possible CPU when a paging
domain first allocates remapped interrupts. Also prepare the mappings
when attaching a paging domain to a device with allocated interrupts, so
an incoming domain is ready before the hardware switches to it. Reject
passthrough mapping results because interrupt remapping requires the
IMSIC page IOVA and its fixed page shift.
Store the resulting IOVAs in a domain-local table so message composition
only requires a lookup. Use the IMSIC group and hart fields as the table
index, matching the RISC-V IOMMU MSI address extraction scheme. These
topology fields do not necessarily produce dense CPU indices, so size
the table for their full encoded range and leave unused entries empty.
Create the per-device interrupt-remapping domain during IOMMU probe, but
publish it from probe_finalize() after the IOMMU core has assigned the
device to its group. This ensures the allocation callback can safely
lock the group.
Select IRQ_MSI_IOMMU for the generic MSI mapping API.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/Kconfig | 1 +
drivers/iommu/riscv/iommu-ir.c | 133 ++++++++++++++++++++++++++++++++-
drivers/iommu/riscv/iommu.c | 12 ++-
drivers/iommu/riscv/iommu.h | 7 ++
4 files changed, 147 insertions(+), 6 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 de5e22ecbd63..4dc104c6d4b1 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -6,13 +6,108 @@
*/
#include <linux/acpi.h>
#include <linux/cleanup.h>
+#include <linux/irqchip/riscv-imsic.h>
#include <linux/msi.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/vmalloc.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;
+ 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 = vcalloc(riscv_iommu_ir_msi_iova_count(), sizeof(*msi_iova));
+ 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)
+ goto err_free;
+ if (shift != IMSIC_MMIO_PAGE_SHIFT) {
+ ret = -EBUSY;
+ goto err_free;
+ }
+ }
+
+ domain->msi_iova = msi_iova;
+
+ return 0;
+
+err_free:
+ vfree(msi_iova);
+ return ret;
+}
+
static struct irq_chip riscv_iommu_ir_irq_chip = {
.name = "IOMMU-IR",
.irq_ack = irq_chip_ack_parent,
@@ -25,8 +120,24 @@ 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;
int 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) {
+ struct riscv_iommu_domain *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;
@@ -112,11 +223,22 @@ struct irq_domain *riscv_iommu_ir_irq_domain_create(struct device *dev,
irqdomain->msi_parent_ops = &riscv_iommu_ir_msi_parent_ops;
irq_domain_update_bus_token(irqdomain, DOMAIN_BUS_MSI_REMAP);
- dev_set_msi_domain(dev, irqdomain);
-
+ /*
+ * Publication is deferred to riscv_iommu_ir_irq_domain_publish(),
+ * called from probe_finalize() after the IOMMU core assigns
+ * dev->iommu_group, because the allocation callback locks the group.
+ */
return irqdomain;
}
+void riscv_iommu_ir_irq_domain_publish(struct device *dev)
+{
+ struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+
+ if (info->irqdomain)
+ dev_set_msi_domain(dev, info->irqdomain);
+}
+
void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_info *info)
{
struct fwnode_handle *fn;
@@ -124,7 +246,8 @@ void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_inf
if (!info->irqdomain)
return;
- dev_set_msi_domain(dev, info->irqdomain->parent);
+ if (dev_get_msi_domain(dev) == info->irqdomain)
+ dev_set_msi_domain(dev, info->irqdomain->parent);
fn = info->irqdomain->fwnode;
irq_domain_remove(info->irqdomain);
info->irqdomain = NULL;
@@ -139,6 +262,10 @@ 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);
+
+ vfree(domain->msi_iova);
+ domain->msi_iova = NULL;
}
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 049ca1852d7b..1a30bac88101 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.
*
@@ -1341,6 +1338,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);
/*
@@ -1467,6 +1466,7 @@ static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
* any IMSICs or no MSI domain has been set up for the device.
*/
info->irqdomain = irqdomain;
+ info->dev = dev;
/*
* Allocate and pre-configure device context entries in
@@ -1490,6 +1490,11 @@ static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
return &iommu->iommu;
}
+static void riscv_iommu_probe_finalize(struct device *dev)
+{
+ riscv_iommu_ir_irq_domain_publish(dev);
+}
+
static void riscv_iommu_release_device(struct device *dev)
{
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
@@ -1512,6 +1517,7 @@ static const struct iommu_ops riscv_iommu_ops = {
.domain_alloc_paging = riscv_iommu_alloc_paging_domain,
.device_group = riscv_iommu_device_group,
.probe_device = riscv_iommu_probe_device,
+ .probe_finalize = riscv_iommu_probe_finalize,
.release_device = riscv_iommu_release_device,
.get_resv_regions = riscv_iommu_get_resv_regions,
};
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 16b3c9c4cf8c..59779aa9d380 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;
@@ -93,6 +99,7 @@ void riscv_iommu_disable(struct riscv_iommu_device *iommu);
struct irq_domain *riscv_iommu_ir_irq_domain_create(struct device *dev,
struct riscv_iommu_info *info);
+void riscv_iommu_ir_irq_domain_publish(struct device *dev);
void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_info *info);
int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
struct iommu_domain *old);
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 13/21] iommu/riscv: Preserve MSI IOVA state across domain replacement
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (11 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 12/21] iommu/riscv: Pre-map IMSIC MSI targets Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:52 ` Jason Gunthorpe
2026-08-20 21:41 ` [PATCH v4 14/21] iommu/riscv: Gate direct identity boundary switches with live MSIs Andrew Jones
` (7 subsequent siblings)
20 siblings, 1 reply; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
MSI IOVAs belong to a paging domain, but iommufd may replace a device's
domain while its interrupts remain allocated. Waiting for another IRQ
allocation to populate the new table would leave existing interrupts
without valid MSI IOVAs.
Track live IRQ allocations so the attach path knows when the incoming
domain needs an MSI IOVA table. Rebuild the table through
iommu_dma_map_msi(), allowing each backend to preserve its own IOVA
allocation rules.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu-ir.c | 41 +++++++++++++++++++++++++++++++---
drivers/iommu/riscv/iommu.h | 2 ++
2 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index 4dc104c6d4b1..5718f90affd6 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -128,19 +128,27 @@ static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain,
* 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.
+ * out from under the build. Bump info->nr_irqs here too, before
+ * irq_domain_alloc_irqs_parent() runs unlocked below, so a concurrent
+ * riscv_iommu_ir_attach_paging_domain() can never observe a count that
+ * is lower than the number of IRQs actually in flight for this device.
*/
scoped_guard(iommu_group, info->dev) {
struct riscv_iommu_domain *domain = rcu_dereference_protected(info->domain, true);
ret = domain ? riscv_iommu_ir_build_msi_iova(domain, info->dev) : 0;
+ if (!ret)
+ info->nr_irqs += nr_irqs;
}
if (ret)
return ret;
ret = irq_domain_alloc_irqs_parent(irqdomain, irq_base, nr_irqs, arg);
- if (ret)
+ if (ret) {
+ guard(iommu_group)(info->dev);
+ info->nr_irqs -= nr_irqs;
return ret;
+ }
for (unsigned int i = 0; i < nr_irqs; i++) {
struct irq_data *data = irq_domain_get_irq_data(irqdomain, irq_base + i);
@@ -151,9 +159,25 @@ static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain,
return 0;
}
+static void riscv_iommu_ir_irq_domain_free_irqs(struct irq_domain *irqdomain,
+ unsigned int irq_base, unsigned int nr_irqs)
+{
+ struct riscv_iommu_info *info = irqdomain->host_data;
+
+ irq_domain_free_irqs_parent(irqdomain, irq_base, nr_irqs);
+
+ /*
+ * Decrement only after the parent free completes, so a concurrent
+ * riscv_iommu_ir_attach_paging_domain() never observes a count lower
+ * than the number of IRQs that are actually still live.
+ */
+ scoped_guard(iommu_group, info->dev)
+ info->nr_irqs -= nr_irqs;
+}
+
static const struct irq_domain_ops riscv_iommu_ir_irq_domain_ops = {
.alloc = riscv_iommu_ir_irq_domain_alloc_irqs,
- .free = irq_domain_free_irqs_parent,
+ .free = riscv_iommu_ir_irq_domain_free_irqs,
};
static const struct msi_parent_ops riscv_iommu_ir_msi_parent_ops = {
@@ -257,6 +281,17 @@ void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_inf
int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
struct iommu_domain *old)
{
+ struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
+ struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+
+ /*
+ * Build the table if this device has allocated MSIs, since those MSIs may
+ * already be live and expecting riscv_iommu_ir_compose_msi_msg() to find
+ * a populated table for whatever domain is now attached.
+ */
+ if (info->nr_irqs)
+ return riscv_iommu_ir_build_msi_iova(domain, dev);
+
return 0;
}
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 59779aa9d380..24006c1c1722 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -46,6 +46,8 @@ struct riscv_iommu_info {
struct riscv_iommu_domain __rcu *domain;
struct irq_domain *irqdomain;
struct device *dev;
+ /* Number of currently allocated IRQs; protected by the group mutex */
+ unsigned int nr_irqs;
};
struct riscv_iommu_device;
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* Re: [PATCH v4 13/21] iommu/riscv: Preserve MSI IOVA state across domain replacement
2026-08-20 21:41 ` [PATCH v4 13/21] iommu/riscv: Preserve MSI IOVA state across domain replacement Andrew Jones
@ 2026-08-20 21:52 ` Jason Gunthorpe
2026-08-21 11:14 ` Andrew Jones
0 siblings, 1 reply; 39+ messages in thread
From: Jason Gunthorpe @ 2026-08-20 21:52 UTC (permalink / raw)
To: Andrew Jones
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Thu, Aug 20, 2026 at 11:41:42PM +0200, Andrew Jones wrote:
> MSI IOVAs belong to a paging domain, but iommufd may replace a device's
> domain while its interrupts remain allocated. Waiting for another IRQ
> allocation to populate the new table would leave existing interrupts
> without valid MSI IOVAs.
???
iommufd is supposed to handle all of this itself, that is what commit
40f5175d0eb7 ("iommufd: Implement sw_msi support natively")
is doing?
The whole point of that commit was so drivers don't have to deal with
this at all.
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [PATCH v4 13/21] iommu/riscv: Preserve MSI IOVA state across domain replacement
2026-08-20 21:52 ` Jason Gunthorpe
@ 2026-08-21 11:14 ` Andrew Jones
2026-08-21 13:22 ` Jason Gunthorpe
0 siblings, 1 reply; 39+ messages in thread
From: Andrew Jones @ 2026-08-21 11:14 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Thu, Aug 20, 2026 at 06:52:04PM -0300, Jason Gunthorpe wrote:
> On Thu, Aug 20, 2026 at 11:41:42PM +0200, Andrew Jones wrote:
> > MSI IOVAs belong to a paging domain, but iommufd may replace a device's
> > domain while its interrupts remain allocated. Waiting for another IRQ
> > allocation to populate the new table would leave existing interrupts
> > without valid MSI IOVAs.
>
> ???
>
> iommufd is supposed to handle all of this itself, that is what commit
> 40f5175d0eb7 ("iommufd: Implement sw_msi support natively")
>
> is doing?
>
> The whole point of that commit was so drivers don't have to deal with
> this at all.
>
This driver relies on that support. iommufd installs all required MSI
mappings in the incoming HWPT before attach. The additional driver work
is to populate the PA-to-IOVA lookup table riscv needs in MSI composition
on IRQ affinity changes.
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [PATCH v4 13/21] iommu/riscv: Preserve MSI IOVA state across domain replacement
2026-08-21 11:14 ` Andrew Jones
@ 2026-08-21 13:22 ` Jason Gunthorpe
2026-08-21 13:56 ` Andrew Jones
0 siblings, 1 reply; 39+ messages in thread
From: Jason Gunthorpe @ 2026-08-21 13:22 UTC (permalink / raw)
To: Andrew Jones
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 01:14:39PM +0200, Andrew Jones wrote:
> On Thu, Aug 20, 2026 at 06:52:04PM -0300, Jason Gunthorpe wrote:
> > On Thu, Aug 20, 2026 at 11:41:42PM +0200, Andrew Jones wrote:
> > > MSI IOVAs belong to a paging domain, but iommufd may replace a device's
> > > domain while its interrupts remain allocated. Waiting for another IRQ
> > > allocation to populate the new table would leave existing interrupts
> > > without valid MSI IOVAs.
> >
> > ???
> >
> > iommufd is supposed to handle all of this itself, that is what commit
> > 40f5175d0eb7 ("iommufd: Implement sw_msi support natively")
> >
> > is doing?
> >
> > The whole point of that commit was so drivers don't have to deal with
> > this at all.
> >
>
> This driver relies on that support. iommufd installs all required MSI
> mappings in the incoming HWPT before attach. The additional driver work
> is to populate the PA-to-IOVA lookup table riscv needs in MSI composition
> on IRQ affinity changes.
So it is all because some of the MSI state got leaked into the iommu
driver? I do not like it :)
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread* Re: [PATCH v4 13/21] iommu/riscv: Preserve MSI IOVA state across domain replacement
2026-08-21 13:22 ` Jason Gunthorpe
@ 2026-08-21 13:56 ` Andrew Jones
0 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-21 13:56 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Fri, Aug 21, 2026 at 10:22:52AM -0300, Jason Gunthorpe wrote:
> On Fri, Aug 21, 2026 at 01:14:39PM +0200, Andrew Jones wrote:
> > On Thu, Aug 20, 2026 at 06:52:04PM -0300, Jason Gunthorpe wrote:
> > > On Thu, Aug 20, 2026 at 11:41:42PM +0200, Andrew Jones wrote:
> > > > MSI IOVAs belong to a paging domain, but iommufd may replace a device's
> > > > domain while its interrupts remain allocated. Waiting for another IRQ
> > > > allocation to populate the new table would leave existing interrupts
> > > > without valid MSI IOVAs.
> > >
> > > ???
> > >
> > > iommufd is supposed to handle all of this itself, that is what commit
> > > 40f5175d0eb7 ("iommufd: Implement sw_msi support natively")
> > >
> > > is doing?
> > >
> > > The whole point of that commit was so drivers don't have to deal with
> > > this at all.
> > >
> >
> > This driver relies on that support. iommufd installs all required MSI
> > mappings in the incoming HWPT before attach. The additional driver work
> > is to populate the PA-to-IOVA lookup table riscv needs in MSI composition
> > on IRQ affinity changes.
>
> So it is all because some of the MSI state got leaked into the iommu
> driver? I do not like it :)
>
It's a cache of the MSI target IOVAs, rather than MSI state itself. But
the batched API lets the descriptor cache one IOVA base, so msi_iova[]
and its maintenance can go. I'll rework that for the next version.
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v4 14/21] iommu/riscv: Gate direct identity boundary switches with live MSIs
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (12 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 13/21] iommu/riscv: Preserve MSI IOVA state across domain replacement Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 15/21] iommu/riscv: Remap IMSIC targets during MSI composition Andrew Jones
` (6 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
MSI composition may run concurrently with a domain switch and derives
the target address from the attached domain. A stale domain read across
a direct paging-to-identity or identity-to-paging transition can select
the wrong addressing mode, so reject these transitions while IRQs remain
allocated.
Keep the blocking domain available as an immediate fail-stop mode. Since
it does not retain the previous translation mode, transitions through it
cannot be checked reliably and still require callers to quiesce MSI
state. Paging-to-paging replacement remains supported because the new
domain's MSI IOVA table is prepared before attachment.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu-ir.c | 38 ++++++++++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.c | 9 ++++++++
drivers/iommu/riscv/iommu.h | 6 ++++++
3 files changed, 53 insertions(+)
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index 5718f90affd6..f61b65f29888 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -278,6 +278,44 @@ void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_inf
irq_domain_free_fwnode(fn);
}
+int riscv_iommu_ir_check_attach_paging_domain(struct iommu_domain *iommu_domain,
+ struct device *dev,
+ struct iommu_domain *old)
+{
+ struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+
+ /*
+ * IOMMU_DOMAIN_BLOCKED is intentionally not checked. The immediate old
+ * domain does not reveal whether BLOCKED was entered from identity or
+ * paging, so rejecting it would also reject valid same-mode restores.
+ * IDENTITY -> BLOCKED -> PAGING can therefore bypass this check; callers
+ * must quiesce and tear down MSIs before making such a change.
+ */
+ if (old && old->type == IOMMU_DOMAIN_IDENTITY && info->nr_irqs)
+ return -EBUSY;
+
+ return 0;
+}
+
+int riscv_iommu_ir_check_attach_identity_domain(struct iommu_domain *iommu_domain,
+ struct device *dev,
+ struct iommu_domain *old)
+{
+ struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+
+ /*
+ * IOMMU_DOMAIN_BLOCKED is intentionally not checked. The immediate old
+ * domain does not reveal whether BLOCKED was entered from identity or
+ * paging, so rejecting it would also reject valid same-mode restores.
+ * PAGING -> BLOCKED -> IDENTITY can therefore bypass this check; callers
+ * must quiesce and tear down MSIs before making such a change.
+ */
+ if (old && (old->type & __IOMMU_DOMAIN_PAGING) && info->nr_irqs)
+ return -EBUSY;
+
+ return 0;
+}
+
int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
struct iommu_domain *old)
{
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 1a30bac88101..22cccc69bd2c 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1280,6 +1280,10 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
u64 fsc, ta;
int ret;
+ ret = riscv_iommu_ir_check_attach_paging_domain(iommu_domain, dev, old);
+ if (ret)
+ return ret;
+
pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
if (!riscv_iommu_pt_supported(iommu, pt_info.fsc_iosatp_mode))
@@ -1398,6 +1402,11 @@ static int riscv_iommu_attach_identity_domain(struct iommu_domain *iommu_domain,
{
struct riscv_iommu_device *iommu = dev_to_iommu(dev);
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+ int ret;
+
+ ret = riscv_iommu_ir_check_attach_identity_domain(iommu_domain, dev, old);
+ if (ret)
+ return ret;
riscv_iommu_iodir_update(iommu, dev, RISCV_IOMMU_FSC_BARE, RISCV_IOMMU_PC_TA_V);
riscv_iommu_bond_unlink(old, dev);
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 24006c1c1722..19e2c0f29301 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -103,6 +103,12 @@ struct irq_domain *riscv_iommu_ir_irq_domain_create(struct device *dev,
struct riscv_iommu_info *info);
void riscv_iommu_ir_irq_domain_publish(struct device *dev);
void riscv_iommu_ir_irq_domain_remove(struct device *dev, struct riscv_iommu_info *info);
+int riscv_iommu_ir_check_attach_paging_domain(struct iommu_domain *iommu_domain,
+ struct device *dev,
+ struct iommu_domain *old);
+int riscv_iommu_ir_check_attach_identity_domain(struct iommu_domain *iommu_domain,
+ struct device *dev,
+ struct iommu_domain *old);
int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struct device *dev,
struct iommu_domain *old);
void riscv_iommu_ir_free_paging_domain(struct iommu_domain *iommu_domain);
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 15/21] iommu/riscv: Remap IMSIC targets during MSI composition
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (13 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 14/21] iommu/riscv: Gate direct identity boundary switches with live MSIs Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 16/21] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V Andrew Jones
` (5 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
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@oss.qualcomm.com>
---
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
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 16/21] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (14 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 15/21] iommu/riscv: Remap IMSIC targets during MSI composition Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 17/21] iommu/riscv: Report cache coherency capability Andrew Jones
` (4 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
From: Tomasz Jeznach <tomasz.jeznach@linux.dev>
Enable IOMMU_DMA for 64-bit RISC-V now that the RISC-V
IOMMU driver supports MSI remapping.
Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6e07bd69467a..1c9f2ad10f36 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -151,7 +151,7 @@ config OF_IOMMU
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
- def_bool ARM64 || X86 || S390
+ def_bool ARM64 || X86 || S390 || (RISCV && 64BIT)
select DMA_OPS_HELPERS
select IOMMU_API
select IOMMU_IOVA
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 17/21] iommu/riscv: Report cache coherency capability
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (15 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 16/21] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:47 ` Jason Gunthorpe
2026-08-20 21:41 ` [PATCH v4 18/21] vfio: enable IOMMU_TYPE1 for RISC-V Andrew Jones
` (3 subsequent siblings)
20 siblings, 1 reply; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
VFIO and iommufd require IOMMU_CAP_CACHE_COHERENCY because they use
IOMMU_CACHE mappings and have no userspace cache-maintenance path.
RISC-V page tables preserve physical memory attributes for IOMMU_CACHE
mappings. Svpbmt marks mappings without IOMMU_CACHE as non-cacheable,
but preserving physical attributes cannot make a noncoherent device
path coherent.
Report the capability only for devices marked DMA coherent. This limits
userspace assignment to paths that can honor cacheable mappings.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 22cccc69bd2c..161093b1d9c5 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -16,6 +16,7 @@
#include <linux/acpi_rimt.h>
#include <linux/compiler.h>
#include <linux/crash_dump.h>
+#include <linux/dma-map-ops.h>
#include <linux/init.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
@@ -1429,6 +1430,16 @@ static struct iommu_group *riscv_iommu_device_group(struct device *dev)
return generic_device_group(dev);
}
+static bool riscv_iommu_capable(struct device *dev, enum iommu_cap cap)
+{
+ switch (cap) {
+ case IOMMU_CAP_CACHE_COHERENCY:
+ return dev_is_dma_coherent(dev);
+ default:
+ return false;
+ }
+}
+
static int riscv_iommu_of_xlate(struct device *dev, const struct of_phandle_args *args)
{
return iommu_fwspec_add_ids(dev, args->args, 1);
@@ -1520,6 +1531,7 @@ static void riscv_iommu_get_resv_regions(struct device *dev, struct list_head *h
static const struct iommu_ops riscv_iommu_ops = {
.of_xlate = riscv_iommu_of_xlate,
+ .capable = riscv_iommu_capable,
.identity_domain = &riscv_iommu_identity_domain,
.blocked_domain = &riscv_iommu_blocking_domain,
.release_domain = &riscv_iommu_blocking_domain,
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* Re: [PATCH v4 17/21] iommu/riscv: Report cache coherency capability
2026-08-20 21:41 ` [PATCH v4 17/21] iommu/riscv: Report cache coherency capability Andrew Jones
@ 2026-08-20 21:47 ` Jason Gunthorpe
2026-08-21 11:15 ` Andrew Jones
0 siblings, 1 reply; 39+ messages in thread
From: Jason Gunthorpe @ 2026-08-20 21:47 UTC (permalink / raw)
To: Andrew Jones
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Thu, Aug 20, 2026 at 11:41:46PM +0200, Andrew Jones wrote:
> VFIO and iommufd require IOMMU_CAP_CACHE_COHERENCY because they use
> IOMMU_CACHE mappings and have no userspace cache-maintenance path.
>
> RISC-V page tables preserve physical memory attributes for IOMMU_CACHE
> mappings. Svpbmt marks mappings without IOMMU_CACHE as non-cacheable,
> but preserving physical attributes cannot make a noncoherent device
> path coherent.
>
> Report the capability only for devices marked DMA coherent. This limits
> userspace assignment to paths that can honor cacheable mappings.
>
> Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
> ---
> drivers/iommu/riscv/iommu.c | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
You can send this outside this series?
Jason
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* Re: [PATCH v4 17/21] iommu/riscv: Report cache coherency capability
2026-08-20 21:47 ` Jason Gunthorpe
@ 2026-08-21 11:15 ` Andrew Jones
0 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-21 11:15 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-riscv, iommu, linux-kernel, tomasz.jeznach, tjeznach, joro,
will, robin.murphy, pjw, palmer, anup, tglx, kevin.tian,
fangyu.yu
On Thu, Aug 20, 2026 at 06:47:30PM -0300, Jason Gunthorpe wrote:
> On Thu, Aug 20, 2026 at 11:41:46PM +0200, Andrew Jones wrote:
> > VFIO and iommufd require IOMMU_CAP_CACHE_COHERENCY because they use
> > IOMMU_CACHE mappings and have no userspace cache-maintenance path.
> >
> > RISC-V page tables preserve physical memory attributes for IOMMU_CACHE
> > mappings. Svpbmt marks mappings without IOMMU_CACHE as non-cacheable,
> > but preserving physical attributes cannot make a noncoherent device
> > path coherent.
> >
> > Report the capability only for devices marked DMA coherent. This limits
> > userspace assignment to paths that can honor cacheable mappings.
> >
> > Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
> > ---
> > drivers/iommu/riscv/iommu.c | 12 ++++++++++++
> > 1 file changed, 12 insertions(+)
>
> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
>
> You can send this outside this series?
Sure
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 39+ messages in thread
* [PATCH v4 18/21] vfio: enable IOMMU_TYPE1 for RISC-V
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (16 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 17/21] iommu/riscv: Report cache coherency capability Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 19/21] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch Andrew Jones
` (2 subsequent siblings)
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu,
Nutty Liu
From: Tomasz Jeznach <tjeznach@rivosinc.com>
Enable VFIO support on RISC-V architecture, now that the RISC-V IOMMU
driver reports the IOMMU_CAP_CACHE_COHERENCY capability VFIO_TYPE1 and
iommufd both require before allowing a device to be bound.
Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
---
drivers/vfio/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
index ceae52fd7586..ad62205b4e45 100644
--- a/drivers/vfio/Kconfig
+++ b/drivers/vfio/Kconfig
@@ -39,7 +39,7 @@ config VFIO_GROUP
config VFIO_CONTAINER
bool "Support for the VFIO container /dev/vfio/vfio"
- select VFIO_IOMMU_TYPE1 if MMU && (X86 || S390 || ARM || ARM64)
+ select VFIO_IOMMU_TYPE1 if MMU && (X86 || S390 || ARM || ARM64 || RISCV)
depends on VFIO_GROUP
default y
help
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 19/21] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (17 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 18/21] vfio: enable IOMMU_TYPE1 for RISC-V Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 20/21] riscv: defconfig: Enable IOMMUFD and VFIO Andrew Jones
2026-08-20 21:41 ` [PATCH v4 21/21] selftests/vfio: Allow building on RISC-V Andrew Jones
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu,
Nutty Liu
From: Tomasz Jeznach <tjeznach@rivosinc.com>
Enable KVM/VFIO support on RISC-V architecture, now that VFIO device
assignment is available on RISC-V through VFIO_IOMMU_TYPE1, so a
RISC-V KVM guest can be notified about VFIO-assigned devices.
Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
---
arch/riscv/kvm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index ec2cee0a39e0..49179aae9504 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -29,6 +29,7 @@ config KVM
select KVM_GENERIC_DIRTYLOG_READ_PROTECT
select KVM_GENERIC_HARDWARE_ENABLING
select KVM_MMIO
+ select KVM_VFIO
select VIRT_XFER_TO_GUEST_WORK
select SCHED_INFO
select GUEST_PERF_EVENTS if PERF_EVENTS
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 20/21] riscv: defconfig: Enable IOMMUFD and VFIO
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (18 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 19/21] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
2026-08-20 21:41 ` [PATCH v4 21/21] selftests/vfio: Allow building on RISC-V Andrew Jones
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
Enable iommufd and VFIO in the default configuration now that the
RISC-V IOMMU provides the DMA and MSI remapping needed for userspace
device access, including assignment to virtual machines.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
arch/riscv/configs/defconfig | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
index ed605b5e3162..bae9a0f32f89 100644
--- a/arch/riscv/configs/defconfig
+++ b/arch/riscv/configs/defconfig
@@ -249,6 +249,10 @@ CONFIG_DMADEVICES=y
CONFIG_DMA_SUN6I=m
CONFIG_DW_AXI_DMAC=y
CONFIG_MMP_PDMA=m
+CONFIG_IOMMUFD=m
+CONFIG_VFIO_DEVICE_CDEV=y
+CONFIG_VFIO=m
+CONFIG_VFIO_PCI=m
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_INPUT=y
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread* [PATCH v4 21/21] selftests/vfio: Allow building on RISC-V
2026-08-20 21:41 [PATCH v4 00/21] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (19 preceding siblings ...)
2026-08-20 21:41 ` [PATCH v4 20/21] riscv: defconfig: Enable IOMMUFD and VFIO Andrew Jones
@ 2026-08-20 21:41 ` Andrew Jones
20 siblings, 0 replies; 39+ messages in thread
From: Andrew Jones @ 2026-08-20 21:41 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
The generic VFIO selftests, including iommufd coverage, require no
RISC-V-specific source changes. Include riscv64 in their build list.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
tools/testing/selftests/vfio/Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tools/testing/selftests/vfio/Makefile b/tools/testing/selftests/vfio/Makefile
index 2c32c48db509..c6c5764185ba 100644
--- a/tools/testing/selftests/vfio/Makefile
+++ b/tools/testing/selftests/vfio/Makefile
@@ -1,6 +1,6 @@
ARCH ?= $(shell uname -m)
-ifeq (,$(filter $(ARCH),aarch64 arm64 x86 x86_64))
+ifeq (,$(filter $(ARCH),aarch64 arm64 x86 x86_64 riscv64))
# Do nothing on unsupported architectures
include ../lib.mk
else
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 39+ messages in thread