* [PATCH v3 01/19] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
@ 2026-08-07 18:16 ` Andrew Jones
2026-08-07 18:16 ` [PATCH v3 02/19] iommufd: Add iommufd_sw_map_msi() Andrew Jones
` (17 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:16 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 currently uses a fixed 64-bit bitmap,
capping the number of distinct SW MSI mappings a context or hwpt can
track. An upcoming caller needs one mapping per possible CPU.
Convert the fixed bitmap to a pointer plus size that grows on demand
via iommufd_sw_msi_maps_ensure(). Add iommufd_sw_msi_maps_test_bit()
alongside it: unlike the __set_bit() call sites, which always follow
their own successful iommufd_sw_msi_maps_ensure() call on the same id,
iommufd_group_setup_msi() tests an id from the fd-global sw_msi_list
against a specific group's required_sw_msi map, which may not have
been grown to cover that id yet. The bitmap is arbitrarily capped at
16K entries in order to simplify arithmetic validation (i.e. simple
bounds checks vs. integer wrap control).
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 170a7005f0bc..402251c7b887 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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 02/19] iommufd: Add iommufd_sw_map_msi()
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
2026-08-07 18:16 ` [PATCH v3 01/19] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
@ 2026-08-07 18:16 ` Andrew Jones
2026-08-07 18:16 ` [PATCH v3 03/19] iommu/dma: Add iommu_dma_sw_map_msi() Andrew Jones
` (16 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:16 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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 03/19] iommu/dma: Add iommu_dma_sw_map_msi()
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
2026-08-07 18:16 ` [PATCH v3 01/19] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
2026-08-07 18:16 ` [PATCH v3 02/19] iommufd: Add iommufd_sw_map_msi() Andrew Jones
@ 2026-08-07 18:16 ` Andrew Jones
2026-08-07 18:16 ` [PATCH v3 04/19] iommu/dma: Add iommu_dma_map_msi() Andrew Jones
` (15 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:16 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 9abaec0703ef..56a5072b4dde 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2234,24 +2234,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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 04/19] iommu/dma: Add iommu_dma_map_msi()
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (2 preceding siblings ...)
2026-08-07 18:16 ` [PATCH v3 03/19] iommu/dma: Add iommu_dma_sw_map_msi() Andrew Jones
@ 2026-08-07 18:16 ` Andrew Jones
2026-08-07 18:16 ` [PATCH v3 05/19] genirq/msi: Provide DOMAIN_BUS_MSI_REMAP Andrew Jones
` (14 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:16 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 iommu_dma_map_msi() to map an MSI doorbell and return the IOVA
and granule shift to callers that need their own PA->IOVA lookup,
dispatching to the DMA-IOMMU or iommufd cookie implementation as
appropriate.
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.
iommu_dma_map_msi() requires the caller to hold @dev's iommu group
mutex, but struct iommu_group is private to drivers/iommu/iommu.c, so
a caller outside the core (e.g. an interrupt-remapping driver building
a table of mappings ahead of any MSI descriptor existing) has no way
to take it. Add iommu_group_mutex_lock()/iommu_group_mutex_unlock() to
bridge that gap, next to the existing iommu_group_mutex_assert().
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 56a5072b4dde..bcd3eb94bee5 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2235,9 +2235,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
@@ -2271,22 +2271,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 e8f13dcebbde..15adaf55666f 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 d20aa6f6863a..1745ef1525da 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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 05/19] genirq/msi: Provide DOMAIN_BUS_MSI_REMAP
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (3 preceding siblings ...)
2026-08-07 18:16 ` [PATCH v3 04/19] iommu/dma: Add iommu_dma_map_msi() Andrew Jones
@ 2026-08-07 18:16 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 06/19] irqchip/riscv-imsic: Compose MSI updates through the hierarchy Andrew Jones
` (13 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:16 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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 06/19] irqchip/riscv-imsic: Compose MSI updates through the hierarchy
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (4 preceding siblings ...)
2026-08-07 18:16 ` [PATCH v3 05/19] genirq/msi: Provide DOMAIN_BUS_MSI_REMAP Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 20:25 ` Thomas Gleixner
2026-08-07 18:17 ` [PATCH v3 07/19] iommu/riscv: Add IRQ domain for interrupt remapping Andrew Jones
` (12 subsequent siblings)
18 siblings, 1 reply; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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() wrote the new IMSIC target straight to the
device via imsic_msi_update_msg(), bypassing the irqdomain hierarchy.
That skips any intermediate IOMMU irqdomain sitting between IMSIC and
the device, so a remapped device would keep the pre-remap physical
address after every affinity change.
Compose MSI updates via irq_chip_compose_msi_msg() starting from the
top of the hierarchy instead, so an intermediate remap domain gets to
translate the target before the device is written.
For the temporary vector used by non-atomic affinity updates, compose
the current vector through the hierarchy before d->chip_data is switched
to the new vector, then replace only msg.data with the new local ID.
This preserves the required old-address/new-data temporary target while
also allowing an intermediate remap domain to refresh or clear any MSI
IOVA state cached on the device's MSI descriptor.
Moving the final write to after d->chip_data = new_vec is required
because irq_chip_compose_msi_msg() reads d->chip_data. Keep it before
updating effective affinity and migrating vector state so the device is
retargeted before the old vector is disabled.
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
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH v3 06/19] irqchip/riscv-imsic: Compose MSI updates through the hierarchy
2026-08-07 18:17 ` [PATCH v3 06/19] irqchip/riscv-imsic: Compose MSI updates through the hierarchy Andrew Jones
@ 2026-08-07 20:25 ` Thomas Gleixner
0 siblings, 0 replies; 23+ messages in thread
From: Thomas Gleixner @ 2026-08-07 20:25 UTC (permalink / raw)
To: Andrew Jones, linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, kevin.tian, fangyu.yu
On Fri, Aug 07 2026 at 20:17, Andrew Jones wrote:
> imsic_irq_set_affinity() wrote the new IMSIC target straight to the
Wrote? When was that?
You are describing the current state/context, so this wants to be
'write' no?
> device via imsic_msi_update_msg(), bypassing the irqdomain hierarchy.
> That skips any intermediate IOMMU irqdomain sitting between IMSIC and
Otherwise 'skips' makes no sense either.
> the device, so a remapped device would keep the pre-remap physical
> address after every affinity change.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v3 07/19] iommu/riscv: Add IRQ domain for interrupt remapping
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (5 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 06/19] irqchip/riscv-imsic: Compose MSI updates through the hierarchy Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 20:32 ` Thomas Gleixner
2026-08-07 18:17 ` [PATCH v3 08/19] iommu/riscv: Prepare info->domain for concurrent RCU read access Andrew Jones
` (11 subsequent siblings)
18 siblings, 1 reply; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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 irqdomain as the hierarchy hook for
future interrupt remapping. The remapping tables will be owned by the
attached paging domain since the MSI IOVA mappings live in its page
tables.
The domain is installed from probe_device() and removed from
release_device(). This is only the initial skeleton: it does not
remap interrupts yet, and non-paging IOMMU domains will fall back to
the raw IMSIC physical address when remapping is added.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/Makefile | 2 +-
drivers/iommu/riscv/iommu-ir.c | 119 ++++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.c | 46 ++++++-----
drivers/iommu/riscv/iommu.h | 27 +++++++
include/linux/irqchip/riscv-imsic.h | 7 ++
5 files changed, 182 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..5873addf2a1b
--- /dev/null
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -0,0 +1,119 @@
+// 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)
+{
+ struct irq_data *data;
+ int i, ret;
+
+ ret = irq_domain_alloc_irqs_parent(irqdomain, irq_base, nr_irqs, arg);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++) {
+ 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);
+ struct irq_domain *irqdomain;
+ struct fwnode_handle *fn;
+ char *fwname __free(kfree) = NULL;
+
+ 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
+ * allow_unsafe_interrupts.
+ */
+ 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
^ permalink raw reply related [flat|nested] 23+ messages in thread* Re: [PATCH v3 07/19] iommu/riscv: Add IRQ domain for interrupt remapping
2026-08-07 18:17 ` [PATCH v3 07/19] iommu/riscv: Add IRQ domain for interrupt remapping Andrew Jones
@ 2026-08-07 20:32 ` Thomas Gleixner
2026-08-07 20:36 ` Thomas Gleixner
0 siblings, 1 reply; 23+ messages in thread
From: Thomas Gleixner @ 2026-08-07 20:32 UTC (permalink / raw)
To: Andrew Jones, linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, kevin.tian, fangyu.yu
On Fri, Aug 07 2026 at 20:17, Andrew Jones wrote:
> +static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain,
> + unsigned int irq_base, unsigned int nr_irqs,
> + void *arg)
> +{
> + struct irq_data *data;
> + int i, ret;
> +
> + ret = irq_domain_alloc_irqs_parent(irqdomain, irq_base, nr_irqs, arg);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < nr_irqs; i++) {
for (unsigned int i = 0; .....
nr_irqs is unsigned after all
The struct irq_data declaration want's to be inside the loop as that's
the scope where it is used.
> + 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,
https://docs.kernel.org/process/maintainer-tip.html#struct-declarations-and-initializers
> +};
> +
> +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)
You have 100 characters, please use them.
> +{
> + struct irq_domain *irqparent = dev_get_msi_domain(dev);
> + struct irq_domain *irqdomain;
> + struct fwnode_handle *fn;
> + char *fwname __free(kfree) = NULL;
https://docs.kernel.org/process/maintainer-tip.html#variable-declarations
> + 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);
100 chars.
> + 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
> + * allow_unsafe_interrupts.
what is allow_unsafe_interrupts? A variable, a function a parameter or
what?
Please write comments which do not require to grep the tree.
Thanks,
tglx
^ permalink raw reply [flat|nested] 23+ messages in thread* Re: [PATCH v3 07/19] iommu/riscv: Add IRQ domain for interrupt remapping
2026-08-07 20:32 ` Thomas Gleixner
@ 2026-08-07 20:36 ` Thomas Gleixner
0 siblings, 0 replies; 23+ messages in thread
From: Thomas Gleixner @ 2026-08-07 20:36 UTC (permalink / raw)
To: Andrew Jones, linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, kevin.tian, fangyu.yu
On Fri, Aug 07 2026 at 22:32, Thomas Gleixner wrote:
> On Fri, Aug 07 2026 at 20:17, Andrew Jones wrote:
>> +static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain,
>> + unsigned int irq_base, unsigned int nr_irqs,
>> + void *arg)
>> +{
>> + struct irq_data *data;
>> + int i, ret;
>> +
>> + ret = irq_domain_alloc_irqs_parent(irqdomain, irq_base, nr_irqs, arg);
>> + if (ret)
>> + return ret;
>> +
>> + for (i = 0; i < nr_irqs; i++) {
>
> for (unsigned int i = 0; .....
>
> nr_irqs is unsigned after all
>
> The struct irq_data declaration want's to be inside the loop as that's
> the scope where it is used.
>
>> + 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,
>
> https://docs.kernel.org/process/maintainer-tip.html#struct-declarations-and-initializers
>
>> +};
>> +
>> +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)
>
> You have 100 characters, please use them.
Oops. Just noticed, that this is IOMMU territory. So whatever the IOMMU
maintainers prefer :)
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v3 08/19] iommu/riscv: Prepare info->domain for concurrent RCU read access
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (6 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 07/19] iommu/riscv: Add IRQ domain for interrupt remapping Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 09/19] iommu/riscv: Publish IOMMU_RESV_SW_MSI region for iommufd MSI remapping Andrew Jones
` (10 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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 | 14 ++++++++------
drivers/iommu/riscv/iommu.h | 3 ++-
2 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index bfe606a0cb9a..27dfd60834ac 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1241,6 +1241,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)
@@ -1296,8 +1298,8 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
}
riscv_iommu_iodir_update(iommu, dev, fsc, ta);
- riscv_iommu_bond_unlink(info->domain, dev);
- info->domain = domain;
+ riscv_iommu_bond_unlink(rcu_access_pointer(info->domain), dev);
+ rcu_assign_pointer(info->domain, domain);
return 0;
}
@@ -1373,8 +1375,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(rcu_access_pointer(info->domain), dev);
+ rcu_assign_pointer(info->domain, NULL);
return 0;
}
@@ -1394,8 +1396,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(rcu_access_pointer(info->domain), 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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 09/19] iommu/riscv: Publish IOMMU_RESV_SW_MSI region for iommufd MSI remapping
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (7 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 08/19] iommu/riscv: Prepare info->domain for concurrent RCU read access Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 10/19] iommu/riscv: Pre-map IMSIC MSI targets Andrew Jones
` (9 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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's SW MSI path (iommufd_sw_map_msi) requires an IOMMU_RESV_SW_MSI
reservation region to know the IOVA base (sw_msi_start) for MSI page
mappings. Without it, sw_msi_start stays at PHYS_ADDR_MAX and
iommufd_sw_map_msi silently returns passthrough.
Add a get_resv_regions callback that, when IMSIC-based MSI is in use,
reserves an IOVA range of num_possible_cpus() pages starting at
RISCV_IOMMU_MSI_IOVA_BASE. This is large enough to hold one 4 KiB
IMSIC supervisor page per hart.
RISCV_IOMMU_MSI_IOVA_BASE is set to 128 MiB, matching the ARM SMMU
convention. That value is arbitrary for ARM too; its introducing commit
(f3ebee80b313) calls it an "arbitrary MSI IOVA window", so there is
nothing riscv-specific lost by reusing it here.
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 5873addf2a1b..1e157faf7bbe 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -117,3 +117,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 27dfd60834ac..106cc8216942 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"
@@ -1493,6 +1494,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,
@@ -1502,6 +1509,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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 10/19] iommu/riscv: Pre-map IMSIC MSI targets
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (8 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 09/19] iommu/riscv: Publish IOMMU_RESV_SW_MSI region for iommufd MSI remapping Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 11/19] iommu/riscv: Copy MSI IOVA table when replacing an iommufd domain Andrew Jones
` (8 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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
Prepare for irq_compose_msi_msg() to redirect IMSIC-targeted MSIs
through the IOMMU with an O(1) lookup. Unlike fixed-target MSI
controllers, IMSIC target PAs change on irq_set_affinity(), which
may run in atomic context, so compose cannot map the selected
target on demand.
Pre-map each populated IMSIC MSI target page into a domain-local
IOVA table when the device's paging domain first allocates
IMSIC-remapped IRQs, so every possible target IOVA is available at
lookup time. The table is indexed by a transformed IMSIC physical
address using the same transformation defined in the IOMMU
specification for the MSI table (and the function will be used for
the MSI table when that support is added later).
The table is sized by riscv_iommu_ir_msi_iova_count(), i.e.
BIT(group_index_bits + hart_index_bits), rather than
num_possible_cpus(). This is because riscv_iommu_ir_msi_iova_idx()
derives its index from hart_index_bits and group_index_bits, which
are sized for the group with the most harts, i.e. there may be a
sparse mapping.
Select IRQ_MSI_IOMMU so iommu_dma_map_msi() and iommufd_sw_map_msi()
are available to the driver.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/Kconfig | 1 +
drivers/iommu/riscv/iommu-ir.c | 105 +++++++++++++++++++++++++++++++++
drivers/iommu/riscv/iommu.c | 12 +++-
drivers/iommu/riscv/iommu.h | 6 ++
4 files changed, 121 insertions(+), 3 deletions(-)
diff --git a/drivers/iommu/riscv/Kconfig b/drivers/iommu/riscv/Kconfig
index b86e5ab94183..f557b55c2c14 100644
--- a/drivers/iommu/riscv/Kconfig
+++ b/drivers/iommu/riscv/Kconfig
@@ -10,6 +10,7 @@ config RISCV_IOMMU
select GENERIC_PT
select IOMMU_PT
select IOMMU_PT_RISCV64
+ select IRQ_MSI_IOMMU
help
Support for implementations of the RISC-V IOMMU architecture that
complements the RISC-V MMU capabilities, providing similar address
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index 1e157faf7bbe..0dff74aa9b18 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -5,11 +5,97 @@
* Copyright (c) 2026 Qualcomm Technologies, Inc.
*/
#include <linux/cleanup.h>
+#include <linux/irqchip/riscv-imsic.h>
#include <linux/msi.h>
#include <linux/slab.h>
#include "iommu.h"
+/*
+ * Compute the MSI index for an MSI physical address using the
+ * IOMMU "extract" function (RISC-V IOMMU spec section 2.3.3).
+ */
+static size_t riscv_iommu_ir_extract_msi_idx(phys_addr_t pa)
+{
+ const struct imsic_global_config *global = imsic_get_global_config();
+ phys_addr_t mask, addr = pa >> 12;
+ size_t idx;
+
+ mask = BIT(global->hart_index_bits + global->guest_index_bits) - 1;
+ idx = addr & mask;
+
+ if (global->group_index_bits) {
+ phys_addr_t group_mask = BIT(global->group_index_bits) - 1;
+ phys_addr_t group_shift = global->group_index_shift - 12;
+ phys_addr_t group = (addr >> group_shift) & group_mask;
+
+ idx |= group << fls64(mask);
+ }
+
+ return idx;
+}
+
+static size_t riscv_iommu_ir_msi_iova_idx(phys_addr_t pa)
+{
+ const struct imsic_global_config *global = imsic_get_global_config();
+
+ /* msi_iova[] is only used for the host imsics */
+ return riscv_iommu_ir_extract_msi_idx(pa) >> global->guest_index_bits;
+}
+
+static size_t riscv_iommu_ir_msi_iova_count(void)
+{
+ const struct imsic_global_config *global = imsic_get_global_config();
+
+ return BIT(global->group_index_bits + global->hart_index_bits);
+}
+
+static int riscv_iommu_ir_build_msi_iova(struct riscv_iommu_domain *domain, struct device *dev)
+{
+ const struct imsic_global_config *global = imsic_get_global_config();
+ struct iommu_domain *d = &domain->domain;
+ dma_addr_t *msi_iova __free(kfree) = NULL;
+ unsigned int cpu;
+ int ret;
+
+ guard(mutex)(&domain->mutex);
+
+ if (domain->msi_iova)
+ return 0;
+
+ switch (d->cookie_type) {
+ case IOMMU_COOKIE_DMA_IOVA:
+ case IOMMU_COOKIE_DMA_MSI:
+ case IOMMU_COOKIE_IOMMUFD:
+ break;
+ default:
+ return 0;
+ }
+
+ msi_iova = kcalloc(riscv_iommu_ir_msi_iova_count(), sizeof(*msi_iova), GFP_KERNEL);
+ if (!msi_iova)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ const struct imsic_local_config *local = per_cpu_ptr(global->local, cpu);
+ phys_addr_t pa = local->msi_pa;
+ unsigned int shift;
+ size_t idx;
+
+ if (!pa)
+ continue;
+
+ idx = riscv_iommu_ir_msi_iova_idx(pa);
+ ret = iommu_dma_map_msi(d, dev, pa, IMSIC_MMIO_PAGE_SZ, &msi_iova[idx], &shift);
+ if (ret)
+ return ret;
+ }
+
+ domain->msi_iova = no_free_ptr(msi_iova);
+
+ return 0;
+}
+
static struct irq_chip riscv_iommu_ir_irq_chip = {
.name = "IOMMU-IR",
.irq_ack = irq_chip_ack_parent,
@@ -22,9 +108,25 @@ static int riscv_iommu_ir_irq_domain_alloc_irqs(struct irq_domain *irqdomain,
unsigned int irq_base, unsigned int nr_irqs,
void *arg)
{
+ struct riscv_iommu_info *info = irqdomain->host_data;
+ struct riscv_iommu_domain *domain;
struct irq_data *data;
int i, ret;
+ /*
+ * MSI IOVAs are domain-local, just like DMA IOVAs. The device must be
+ * quiesced, including MSI teardown, before switching away from or freeing
+ * the domain. iommu_dma_map_msi() requires the group mutex to be held;
+ * take it around the domain lookup too so info->domain can't change
+ * out from under the build.
+ */
+ scoped_guard(iommu_group, info->dev) {
+ domain = rcu_dereference_protected(info->domain, true);
+ ret = domain ? riscv_iommu_ir_build_msi_iova(domain, info->dev) : 0;
+ }
+ if (ret)
+ return ret;
+
ret = irq_domain_alloc_irqs_parent(irqdomain, irq_base, nr_irqs, arg);
if (ret)
return ret;
@@ -116,6 +218,9 @@ int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struc
void riscv_iommu_ir_free_paging_domain(struct iommu_domain *iommu_domain)
{
+ struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
+
+ kfree(domain->msi_iova);
}
void riscv_iommu_ir_get_resv_regions(struct device *dev, struct list_head *head)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 106cc8216942..07287c0e72e7 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -811,9 +811,6 @@ static int riscv_iommu_iodir_set_mode(struct riscv_iommu_device *iommu,
return 0;
}
-#define iommu_domain_to_riscv(iommu_domain) \
- container_of(iommu_domain, struct riscv_iommu_domain, domain)
-
/*
* Linkage between an iommu_domain and attached devices.
*
@@ -1337,6 +1334,8 @@ static struct iommu_domain *riscv_iommu_alloc_paging_domain(struct device *dev)
if (!domain)
return ERR_PTR(-ENOMEM);
+ mutex_init(&domain->mutex);
+
INIT_LIST_HEAD_RCU(&domain->bonds);
spin_lock_init(&domain->lock);
/*
@@ -1450,6 +1449,13 @@ static struct iommu_device *riscv_iommu_probe_device(struct device *dev)
if (!info)
return ERR_PTR(-ENOMEM);
+ /*
+ * Set info->dev before creating the irqdomain: riscv_iommu_ir_irq_domain_create()
+ * publishes the irqdomain via dev_set_msi_domain(), making the .alloc callback
+ * (which dereferences info->dev) reachable.
+ */
+ info->dev = dev;
+
if (imsic_enabled()) {
irqdomain = riscv_iommu_ir_irq_domain_create(dev, info);
if (IS_ERR(irqdomain)) {
diff --git a/drivers/iommu/riscv/iommu.h b/drivers/iommu/riscv/iommu.h
index 16b3c9c4cf8c..8bd41dd63f0e 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -32,14 +32,20 @@ struct riscv_iommu_domain {
};
struct list_head bonds;
spinlock_t lock; /* protect bonds list updates. */
+ struct mutex mutex; /* serializes sleepable, domain-wide setups */
int pscid;
+ dma_addr_t *msi_iova;
};
PT_IOMMU_CHECK_DOMAIN(struct riscv_iommu_domain, riscvpt.iommu, domain);
+#define iommu_domain_to_riscv(iommu_domain) \
+ container_of(iommu_domain, struct riscv_iommu_domain, domain)
+
/* Private IOMMU data for managed devices, dev_iommu_priv_* */
struct riscv_iommu_info {
struct riscv_iommu_domain __rcu *domain;
struct irq_domain *irqdomain;
+ struct device *dev;
};
struct riscv_iommu_device;
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 11/19] iommu/riscv: Copy MSI IOVA table when replacing an iommufd domain
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (9 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 10/19] iommu/riscv: Pre-map IMSIC MSI targets Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 12/19] iommu/riscv: Gate identity boundary switches with live MSIs Andrew Jones
` (7 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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 may replace one paging domain with another on a device while
leaving the device otherwise operational, e.g. when converting a
device's HWPT. If the old domain already had a populated MSI IOVA
table then the new domain must inherit that table rather than wait
to build its own, since the MSI IOVAs are only built on the next
irq_domain_alloc_irqs(), which does not happen on a domain
replacement.
Only copy from an old domain that is a genuine RISC-V paging domain;
iommu.c already has riscv_iommu_paging_domain_ops in scope to check
this, so do the check there and pass NULL down on a mismatch. This
keeps riscv_iommu_ir_attach_paging_domain() simple: a non-NULL old is
always safe to cast with iommu_domain_to_riscv().
Only iommufd cookie domains are copied from and to, since VFIO type1
and DMA API domains build their own tables from irq_domain_alloc_irqs()
before any device is attached, and are never replaced while a device is
live.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu-ir.c | 78 ++++++++++++++++++++++++++++++++--
drivers/iommu/riscv/iommu.h | 2 +
2 files changed, 77 insertions(+), 3 deletions(-)
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index 0dff74aa9b18..7d9a1eaca92e 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -118,18 +118,26 @@ 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_msis 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 MSIs actually in flight for this device.
*/
scoped_guard(iommu_group, info->dev) {
domain = rcu_dereference_protected(info->domain, true);
ret = domain ? riscv_iommu_ir_build_msi_iova(domain, info->dev) : 0;
+ if (!ret)
+ info->nr_msis += 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_msis -= nr_irqs;
return ret;
+ }
for (i = 0; i < nr_irqs; i++) {
data = irq_domain_get_irq_data(irqdomain, irq_base + i);
@@ -139,9 +147,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 MSIs that are actually still live.
+ */
+ scoped_guard(iommu_group, info->dev)
+ info->nr_msis -= 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 = {
@@ -213,6 +237,54 @@ 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);
+ struct riscv_iommu_domain *old_domain = NULL;
+ dma_addr_t *msi_iova = NULL;
+
+ if (old && (old->type & __IOMMU_DOMAIN_PAGING))
+ old_domain = iommu_domain_to_riscv(old);
+
+ /*
+ * Copying is only correct between two IOMMUFD domains: their MSI IOVAs
+ * come from the fd-wide SW_MSI reservation, so they match across
+ * domain instances. Every other cookie type derives its MSI IOVAs from
+ * domain-local allocator state.
+ */
+ if (old_domain && old_domain->domain.cookie_type == IOMMU_COOKIE_IOMMUFD &&
+ iommu_domain->cookie_type == IOMMU_COOKIE_IOMMUFD) {
+ scoped_guard(mutex, &old_domain->mutex) {
+ if (old_domain->msi_iova) {
+ msi_iova = kmemdup(old_domain->msi_iova,
+ riscv_iommu_ir_msi_iova_count() *
+ sizeof(*msi_iova),
+ GFP_KERNEL);
+ if (!msi_iova)
+ return -ENOMEM;
+ }
+ }
+
+ if (msi_iova) {
+ guard(mutex)(&domain->mutex);
+
+ if (domain->msi_iova)
+ kfree(msi_iova);
+ else
+ domain->msi_iova = msi_iova;
+
+ return 0;
+ }
+ }
+
+ /*
+ * No table to copy: build one from scratch if this device has ever
+ * 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_msis)
+ 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 8bd41dd63f0e..77328282a236 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 MSIs; protected by the group mutex */
+ unsigned int nr_msis;
};
struct riscv_iommu_device;
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 12/19] iommu/riscv: Gate identity boundary switches with live MSIs
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (10 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 11/19] iommu/riscv: Copy MSI IOVA table when replacing an iommufd domain Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 13/19] iommu/riscv: Implement irq_compose_msi_msg for IMSIC remapping Andrew Jones
` (6 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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
Reject identity-boundary domain transitions while a device still has
allocated MSIs.
The RISC-V IOMMU MSI compose path can run concurrently with domain
attach and derives the MSI address rewrite policy from info->domain.
Across an identity boundary, a stale decision can turn into a wrong
addressing mode (IOVA vs PA), so require nr_msis == 0 when crossing
between paging and identity domains.
This is just a defensive guard since MSI state should be quiesced
before crossing the identity boundary anyway.
Do not gate paging <-> blocking: blocking is the fail-stop mode and
must become available immediately, even with live MSIs.
Do not gate paging -> paging: that path already uses nr_msis to
ensure new-domain MSI table readiness before install.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index 07287c0e72e7..f40db2cda417 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1272,10 +1272,14 @@ static int riscv_iommu_attach_paging_domain(struct iommu_domain *iommu_domain,
struct riscv_iommu_domain *domain = iommu_domain_to_riscv(iommu_domain);
struct riscv_iommu_device *iommu = dev_to_iommu(dev);
struct riscv_iommu_info *info = dev_iommu_priv_get(dev);
+ bool old_identity = old && old->type == IOMMU_DOMAIN_IDENTITY;
struct pt_iommu_riscv_64_hw_info pt_info;
u64 fsc, ta;
int ret;
+ if (old_identity && info->nr_msis)
+ return -EBUSY;
+
pt_iommu_riscv_64_hw_info(&domain->riscvpt, &pt_info);
if (!riscv_iommu_pt_supported(iommu, pt_info.fsc_iosatp_mode))
@@ -1394,6 +1398,10 @@ 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);
+ bool old_paging = old && (old->type & __IOMMU_DOMAIN_PAGING);
+
+ if (old_paging && info->nr_msis)
+ return -EBUSY;
riscv_iommu_iodir_update(iommu, dev, RISCV_IOMMU_FSC_BARE, RISCV_IOMMU_PC_TA_V);
riscv_iommu_bond_unlink(rcu_access_pointer(info->domain), dev);
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 13/19] iommu/riscv: Implement irq_compose_msi_msg for IMSIC remapping
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (11 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 12/19] iommu/riscv: Gate identity boundary switches with live MSIs Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 14/19] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V Andrew Jones
` (5 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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
Wire up irq_compose_msi_msg() on the IOMMU-IR irq chip to look up the
pre-mapped IOVA for the target IMSIC page and rewrite the composed
message to use it, redirecting MSI writes through the IOMMU instead
of landing at the raw IMSIC physical address.
The lookup is O(1), since the "extract" function used to build the
table's index is also used here, which is necessary since the number
of IMSICs may eventually be in the hundreds and irq_compose_msi_msg()
may run in atomic context.
domain->msi_iova is read here without domain->mutex, since compose can
run in atomic context, and RCU only protects info->domain itself. Pair
smp_store_release()/smp_load_acquire() on domain->msi_iova to ensure
it's observably filled before publishing its pointer. That guarantees
a non-NULL domain->msi_iova observed here is safe to use.
A NULL domain->msi_iova falls back to the raw IMSIC physical address,
same as when info->domain itself is NULL, since a live paging domain
should not normally reach compose without a populated table.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu-ir.c | 49 +++++++++++++++++++++++++++++++---
1 file changed, 45 insertions(+), 4 deletions(-)
diff --git a/drivers/iommu/riscv/iommu-ir.c b/drivers/iommu/riscv/iommu-ir.c
index 7d9a1eaca92e..98be3e3fddad 100644
--- a/drivers/iommu/riscv/iommu-ir.c
+++ b/drivers/iommu/riscv/iommu-ir.c
@@ -91,17 +91,56 @@ static int riscv_iommu_ir_build_msi_iova(struct riscv_iommu_domain *domain, stru
return ret;
}
- domain->msi_iova = no_free_ptr(msi_iova);
+ /* Pair with smp_load_acquire() in riscv_iommu_ir_compose_msi_msg() */
+ smp_store_release(&domain->msi_iova, no_free_ptr(msi_iova));
return 0;
}
+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,
@@ -267,10 +306,12 @@ int riscv_iommu_ir_attach_paging_domain(struct iommu_domain *iommu_domain, struc
if (msi_iova) {
guard(mutex)(&domain->mutex);
- if (domain->msi_iova)
+ if (domain->msi_iova) {
kfree(msi_iova);
- else
- domain->msi_iova = msi_iova;
+ } else {
+ /* Pair with smp_load_acquire() in riscv_iommu_ir_compose_msi_msg() */
+ smp_store_release(&domain->msi_iova, msi_iova);
+ }
return 0;
}
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 14/19] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (12 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 13/19] iommu/riscv: Implement irq_compose_msi_msg for IMSIC remapping Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 15/19] iommu/riscv: report iommu capabilities Andrew Jones
` (4 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 15/19] iommu/riscv: report iommu capabilities
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (13 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 14/19] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 16/19] vfio: enable IOMMU_TYPE1 for RISC-V Andrew Jones
` (3 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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>
Report RISC-V IOMMU capability required by the VFIO subsystem
to enable PCIe device assignment.
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/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 f40db2cda417..a286f5a24c16 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -1424,6 +1424,17 @@ 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:
+ /* The RISC-V IOMMU is always DMA cache coherent. */
+ return true;
+ 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);
@@ -1516,6 +1527,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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 16/19] vfio: enable IOMMU_TYPE1 for RISC-V
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (14 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 15/19] iommu/riscv: report iommu capabilities Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 17/19] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch Andrew Jones
` (2 subsequent siblings)
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 17/19] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (15 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 16/19] vfio: enable IOMMU_TYPE1 for RISC-V Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 18/19] riscv: defconfig: Enable IOMMUFD and VFIO Andrew Jones
2026-08-07 18:17 ` [PATCH v3 19/19] selftests/vfio: Allow building on RISC-V Andrew Jones
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 18/19] riscv: defconfig: Enable IOMMUFD and VFIO
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (16 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 17/19] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
2026-08-07 18:17 ` [PATCH v3 19/19] selftests/vfio: Allow building on RISC-V Andrew Jones
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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
Now that the RISC-V IOMMU driver supports MSI remapping through
iommufd enable IOMMUFD and VFIO.
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
^ permalink raw reply related [flat|nested] 23+ messages in thread* [PATCH v3 19/19] selftests/vfio: Allow building on RISC-V
2026-08-07 18:16 [PATCH v3 00/19] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (17 preceding siblings ...)
2026-08-07 18:17 ` [PATCH v3 18/19] riscv: defconfig: Enable IOMMUFD and VFIO Andrew Jones
@ 2026-08-07 18:17 ` Andrew Jones
18 siblings, 0 replies; 23+ messages in thread
From: Andrew Jones @ 2026-08-07 18:17 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 and iommufd selftests can now run on RISC-V.
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..c39f95086a54 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 riscv))
# Do nothing on unsupported architectures
include ../lib.mk
else
--
2.43.0
^ permalink raw reply related [flat|nested] 23+ messages in thread