* [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO
@ 2026-08-31 14:59 Andrew Jones
2026-08-31 14:59 ` [PATCH v5 01/17] iommu/dma: Prepare MSI physical address lists Andrew Jones
` (17 more replies)
0 siblings, 18 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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
This series adds MSI remapping for IMSIC so a device's MSI target gets
translated the same way its DMA does, allowing RISC-V to enable IOMMU_DMA
and paging domains by default.
v1[1] used get_resv_regions() with IOMMU_RESV_DIRECT_RELAXABLE to identity
map IMSIC pages, but that was rejected as only a workaround. v2 through
v4[2] instead introduced a RISC-V IOMMU IRQ domain which pre-mapped every
possible IMSIC target and maintained a domain-local PA-to-IOVA table. The
v4 discussion with Jason identified a simpler way to handle the RISC-V
requirement that the MSI target address changes with interrupt affinity:
extend the existing iommu_dma_prepare_msi() model to prepare an ordered
list of MSI targets as one contiguous IOVA range. v5 is a complete redesign
around that approach.
The IMSIC driver now builds an array containing the supervisor IMSIC page
for every possible CPU, indexed by logical CPU number. When allocating an
IRQ, it passes the complete array to iommu_dma_prepare_msi_list(). The new
API maps the ordered physical address list into one contiguous IOVA range
through either DMA-IOMMU or iommufd, then caches the base IOVA and mapping
granule in the MSI descriptor. MSI composition can therefore select the
target for the current CPU with simple arithmetic, including during an
affinity change, without allocating memory or consulting IOMMU-owned state
in atomic context.
DMA-IOMMU extends its existing per-page MSI cache to recognize and reuse
complete ranges. iommufd grows its software-MSI bitmap on demand, bounds
allocations to the reserved MSI window, and prepares, installs, rolls back,
and replays a range as one unit. This keeps the descriptor's contiguous
IOVA valid across iommufd paging-domain replacement without exposing a
partially installed range.
Unlike v4, v5 has no RISC-V IOMMU IRQ domain, no domain-local IMSIC mapping
table, and no IOMMU lookup during MSI composition. The IMSIC IRQ domain
owns the target list and message composition, while the IOMMU layers only
provide the mappings. Devices which do not need IOMMU MSI translation keep
using physical MSI addresses through the same IMSIC path.
The series also carries the remaining plumbing needed for RISC-V PCIe
device assignment through VFIO/KVM: the RISC-V IOMMU reports DMA
cache-coherency capability for coherent devices, VFIO type1 and KVM_VFIO
are enabled for RISC-V, defconfig enables IOMMUFD/VFIO as modules with cdev
support, and the generic VFIO/iommufd selftests can be built for RISC-V.
The RISC-V IOMMU specification does not provide MSI data validation, so
VFIO device assignment requires the applicable allow_unsafe_interrupts=1
module parameter. Direct MSI routing to guest interrupt files (irqbypass)
is not yet supported by this series and will be posted separately on top.
LLM-based coding assistants were used during development for code
exploration, patch review, test execution, and drafting and editing
commit messages and this cover letter. I reviewed and finalized all
resulting code and text. Per-patch Assisted-by tags are omitted in
light of ongoing discussions about simplifying coding-assistant
attribution.
Thanks,
drew
[1] https://lore.kernel.org/all/20260508212339.381933-1-andrew.jones@oss.qualcomm.com/
[2] https://lore.kernel.org/all/20260820214150.545737-1-andrew.jones@oss.qualcomm.com/
v5:
- Completely redesigned MSI remapping based on the v4 discussion[2]
- Replaced per-target mapping with iommu_dma_prepare_msi_list(), which maps
an ordered physical address list into one contiguous IOVA range
- Reworked DMA-IOMMU and iommufd software MSI mappings to allocate, cache,
install, roll back, and replay complete ranges
- Moved host MSI remapping into the IMSIC IRQ domain and removed the RISC-V
IOMMU IRQ domain and domain-local PA-to-IOVA table
- Made the IMSIC driver publish a logical-CPU-indexed S-mode address list
and derive each composed target from the descriptor's cached IOVA base
- Fixed the VFIO selftest architecture check to accept both riscv and
riscv64
Andrew Jones (14):
iommu/dma: Prepare MSI physical address lists
iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap
iommufd: Split software MSI map lookup and allocation
iommufd: Bound software MSI mappings to the reserved range
iommufd: Prepare software MSI maps for address lists
iommufd: Install software MSI map ranges atomically
iommufd: Prepare software MSI installation for address lists
iommu/dma: Introduce iommu_dma_prepare_msi_list()
iommu/riscv: Report cache coherency capability
iommu/riscv: Reserve an MSI IOVA window for iommufd
irqchip/riscv-imsic: Add S-mode MSI address list
irqchip/riscv-imsic: Support IOMMU MSI address lists
riscv: defconfig: Enable IOMMUFD and VFIO
selftests/vfio: Allow building on RISC-V
Tomasz Jeznach (3):
iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V
vfio: enable IOMMU_TYPE1 for RISC-V
RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch
arch/riscv/configs/defconfig | 4 +
arch/riscv/kvm/Kconfig | 1 +
drivers/iommu/Kconfig | 2 +-
drivers/iommu/dma-iommu.c | 117 ++++++--
drivers/iommu/dma-iommu.h | 13 +-
drivers/iommu/iommu-priv.h | 7 +-
drivers/iommu/iommu.c | 109 +++++--
drivers/iommu/iommufd/device.c | 20 +-
drivers/iommu/iommufd/driver.c | 315 +++++++++++++++++----
drivers/iommu/iommufd/hw_pagetable.c | 1 +
drivers/iommu/iommufd/io_pagetable.c | 9 +-
drivers/iommu/iommufd/iommufd_private.h | 51 +++-
drivers/iommu/riscv/iommu.c | 32 +++
drivers/iommu/riscv/iommu.h | 4 +
drivers/irqchip/Kconfig | 1 +
drivers/irqchip/irq-riscv-imsic-platform.c | 25 +-
drivers/irqchip/irq-riscv-imsic-state.c | 54 ++++
drivers/irqchip/irq-riscv-imsic-state.h | 1 +
drivers/vfio/Kconfig | 2 +-
include/linux/iommu.h | 8 +
include/linux/irqchip/riscv-imsic.h | 6 +
tools/testing/selftests/vfio/Makefile | 2 +-
22 files changed, 657 insertions(+), 127 deletions(-)
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v5 01/17] iommu/dma: Prepare MSI physical address lists
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 02/17] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
` (16 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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
Software MSI mappings may cover an ordered list of physical addresses
which must be mapped into one contiguous IOVA range. Extend the internal
DMA-IOMMU mapping helpers to accept an address array, address count, and
required mapping granule.
Teach iommu_dma_get_msi_page() to map the list into one contiguous IOVA
allocation. Keep the existing per-page cache entries and mark the first
entry with the size of the allocation so an identical list can reuse the
mapping without changing the existing cache representation.
This prepares for the forthcoming iommu_dma_prepare_msi_list() API.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/dma-iommu.c | 116 +++++++++++++++++++++++++++++---------
1 file changed, 88 insertions(+), 28 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 9a07eb39336e..38a0c4e244d6 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -42,6 +42,7 @@ struct iommu_dma_msi_page {
struct list_head list;
dma_addr_t iova;
phys_addr_t phys;
+ size_t range_size; /* IOVA range size, or 0 if not the first map */
};
enum iommu_dma_queue_type {
@@ -481,7 +482,7 @@ static int cookie_init_hw_msi_region(struct iommu_dma_cookie *cookie,
num_pages = iova_align(iovad, end - start) >> iova_shift(iovad);
for (i = 0; i < num_pages; i++) {
- msi_page = kmalloc_obj(*msi_page);
+ msi_page = kzalloc_obj(*msi_page);
if (!msi_page)
return -ENOMEM;
@@ -2196,15 +2197,42 @@ static struct list_head *cookie_msi_pages(const struct iommu_domain *domain)
}
}
+static bool iommu_dma_msi_range_matches(struct list_head *msi_page_list,
+ const struct iommu_dma_msi_page *base_page,
+ const phys_addr_t *phys_addrs,
+ unsigned int nr_addrs, size_t granule)
+{
+ const struct iommu_dma_msi_page *msi_page;
+ unsigned int nr_found = 0;
+ dma_addr_t offset;
+
+ list_for_each_entry(msi_page, msi_page_list, list) {
+ if (msi_page->iova < base_page->iova)
+ continue;
+ offset = msi_page->iova - base_page->iova;
+ if (offset >= base_page->range_size)
+ continue;
+ if (!IS_ALIGNED((size_t)offset, granule) ||
+ msi_page->phys != phys_addrs[(size_t)offset / granule])
+ return false;
+ nr_found++;
+ }
+
+ return nr_found == nr_addrs;
+}
+
static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
- phys_addr_t msi_addr, struct iommu_domain *domain)
+ const phys_addr_t *phys_addrs, unsigned int nr_addrs, size_t granule,
+ struct iommu_domain *domain)
{
struct list_head *msi_page_list = cookie_msi_pages(domain);
- struct iommu_dma_msi_page *msi_page;
- dma_addr_t iova;
+ struct iommu_dma_msi_page *msi_page, *first_page = NULL;
int prot = IOMMU_WRITE | IOMMU_NOEXEC | IOMMU_MMIO;
- size_t size = cookie_msi_granule(domain);
static DEFINE_MUTEX(msi_prepare_lock);
+ LIST_HEAD(new_msi_pages);
+ dma_addr_t base_iova;
+ unsigned int i;
+ size_t size;
/*
* Normally a device's default domain is only ever attached to that
@@ -2218,32 +2246,60 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
*/
guard(mutex)(&msi_prepare_lock);
- msi_addr &= ~(phys_addr_t)(size - 1);
- list_for_each_entry(msi_page, msi_page_list, list)
- if (msi_page->phys == msi_addr)
+ if (!nr_addrs || nr_addrs > SIZE_MAX / granule)
+ return NULL;
+ size = nr_addrs * granule;
+
+ list_for_each_entry(msi_page, msi_page_list, list) {
+ if (msi_page->phys != phys_addrs[0])
+ continue;
+ if (nr_addrs == 1)
+ return msi_page;
+ if (msi_page->range_size == size &&
+ iommu_dma_msi_range_matches(msi_page_list, msi_page, phys_addrs,
+ nr_addrs, granule))
return msi_page;
+ }
- msi_page = kzalloc_obj(*msi_page);
- if (!msi_page)
- return NULL;
+ for (i = 0; i < nr_addrs; i++) {
+ msi_page = kzalloc_obj(*msi_page);
+ if (!msi_page)
+ goto out_free_pages;
+ list_add_tail(&msi_page->list, &new_msi_pages);
+ }
- iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
- if (!iova)
- goto out_free_page;
+ base_iova = iommu_dma_alloc_iova(domain, size, dma_get_mask(dev), dev);
+ if (!base_iova)
+ goto out_free_pages;
- if (iommu_map(domain, iova, msi_addr, size, prot, GFP_KERNEL))
- goto out_free_iova;
+ i = 0;
+ list_for_each_entry(msi_page, &new_msi_pages, list) {
+ msi_page->phys = phys_addrs[i];
+ msi_page->iova = base_iova + i * granule;
+ if (!i)
+ first_page = msi_page;
+ if (iommu_map(domain, msi_page->iova, msi_page->phys, granule, prot, GFP_KERNEL))
+ goto out_unmap;
+ i++;
+ }
- INIT_LIST_HEAD(&msi_page->list);
- msi_page->phys = msi_addr;
- msi_page->iova = iova;
- list_add(&msi_page->list, msi_page_list);
- return msi_page;
+ first_page->range_size = size;
+ list_splice(&new_msi_pages, msi_page_list);
+ return first_page;
-out_free_iova:
- iommu_dma_free_iova(domain, iova, size, NULL);
-out_free_page:
- kfree(msi_page);
+out_unmap:
+ if (i) {
+ size_t unmap = iommu_unmap(domain, base_iova, i * granule);
+
+ WARN_ON_ONCE(unmap != i * granule);
+ }
+ iommu_dma_free_iova(domain, base_iova, size, NULL);
+out_free_pages:
+ while (!list_empty(&new_msi_pages)) {
+ msi_page = list_first_entry(&new_msi_pages, typeof(*msi_page), list);
+ list_del(&msi_page->list);
+ kfree(msi_page);
+ }
return NULL;
}
@@ -2252,19 +2308,23 @@ int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
{
struct device *dev = msi_desc_to_dev(desc);
const struct iommu_dma_msi_page *msi_page;
+ phys_addr_t phys_addr;
+ size_t granule;
if (!has_msi_cookie(domain)) {
msi_desc_set_iommu_msi_iova(desc, 0, 0);
return 0;
}
+ granule = cookie_msi_granule(domain);
+ phys_addr = ALIGN_DOWN(msi_addr, granule);
+
iommu_group_mutex_assert(dev);
- msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
+ msi_page = iommu_dma_get_msi_page(dev, &phys_addr, 1, granule, domain);
if (!msi_page)
return -ENOMEM;
- msi_desc_set_iommu_msi_iova(desc, msi_page->iova,
- ilog2(cookie_msi_granule(domain)));
+ msi_desc_set_iommu_msi_iova(desc, msi_page->iova, ilog2(granule));
return 0;
}
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 02/17] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
2026-08-31 14:59 ` [PATCH v5 01/17] iommu/dma: Prepare MSI physical address lists Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-09-01 7:52 ` Nutty.Liu
2026-08-31 14:59 ` [PATCH v5 03/17] iommufd: Split software MSI map lookup and allocation Andrew Jones
` (15 subsequent siblings)
17 siblings, 1 reply; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
struct iommufd_sw_msi_maps uses a fixed 64-bit bitmap, limiting each
group and hardware page table to 64 software MSI mappings. RISC-V
interrupt remapping needs a mapping for every possible CPU, so this
limit is insufficient.
Make the bitmap grow on demand and treat IDs beyond its current size
as absent. Cap it at 16K entries to bound allocation size while leaving
ample room for expected software MSI users.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/iommufd/device.c | 3 +-
drivers/iommu/iommufd/driver.c | 37 ++++++++++++++--------
drivers/iommu/iommufd/hw_pagetable.c | 1 +
drivers/iommu/iommufd/iommufd_private.h | 41 +++++++++++++++++++++++--
4 files changed, 66 insertions(+), 16 deletions(-)
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index d488c23fd353..868f1e591208 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -34,6 +34,7 @@ static void iommufd_group_release(struct kref *kref)
NULL, GFP_KERNEL);
iommu_group_put(igroup->group);
mutex_destroy(&igroup->lock);
+ kfree(igroup->required_sw_msi.bitmap);
kfree(igroup);
}
@@ -384,7 +385,7 @@ static int iommufd_group_setup_msi(struct iommufd_group *igroup,
int rc;
if (cur->sw_msi_start != igroup->sw_msi_start ||
- !test_bit(cur->id, igroup->required_sw_msi.bitmap))
+ !iommufd_sw_msi_maps_test_bit(&igroup->required_sw_msi, cur->id))
continue;
rc = iommufd_sw_msi_install(ictx, hwpt_paging, cur);
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 3b8067976eac..d0b05b1fda42 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,25 @@ 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 +296,11 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
if (IS_ERR(msi_map))
return PTR_ERR(msi_map);
+ rc = iommufd_sw_msi_maps_ensure(&handle->idev->igroup->required_sw_msi,
+ msi_map->id);
+ if (rc)
+ return rc;
+
rc = iommufd_sw_msi_install(ictx, hwpt_paging, msi_map);
if (rc)
return rc;
diff --git a/drivers/iommu/iommufd/hw_pagetable.c b/drivers/iommu/iommufd/hw_pagetable.c
index 623cc608ca0c..54873de43eb0 100644
--- a/drivers/iommu/iommufd/hw_pagetable.c
+++ b/drivers/iommu/iommufd/hw_pagetable.c
@@ -32,6 +32,7 @@ void iommufd_hwpt_paging_destroy(struct iommufd_object *obj)
}
__iommufd_hwpt_destroy(&hwpt_paging->common);
+ kfree(hwpt_paging->present_sw_msi.bitmap);
refcount_dec(&hwpt_paging->ioas->obj.users);
}
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 43fbc5bed8de..9ca5f9f92cdf 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -9,6 +9,7 @@
#include <linux/iova_bitmap.h>
#include <linux/maple_tree.h>
#include <linux/rwsem.h>
+#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/xarray.h>
#include <uapi/linux/iommufd.h>
@@ -29,11 +30,47 @@ struct iommufd_sw_msi_map {
unsigned int id;
};
-/* Bitmap of struct iommufd_sw_msi_map::id */
+/* Bitmap of struct iommufd_sw_msi_map::id; starts empty, grows on demand. */
struct iommufd_sw_msi_maps {
- DECLARE_BITMAP(bitmap, 64);
+ unsigned long *bitmap;
+ unsigned int nbits;
};
+/* Large enough for foreseeable SW MSI users while bounding bitmap growth. */
+#define IOMMUFD_SW_MSI_MAX_ID (16U * 1024 - 1)
+
+/* Grow bitmap to accommodate id. Must be called under ictx->sw_msi_lock. */
+static inline int iommufd_sw_msi_maps_ensure(struct iommufd_sw_msi_maps *maps,
+ unsigned int id)
+{
+ unsigned long *new_bitmap;
+ unsigned int new_nbits;
+
+ if (id < maps->nbits)
+ return 0;
+ if (id > IOMMUFD_SW_MSI_MAX_ID)
+ return -EOVERFLOW;
+
+ new_nbits = max(ALIGN(id + 1, BITS_PER_LONG), 64U);
+ new_bitmap = krealloc(maps->bitmap,
+ BITS_TO_LONGS(new_nbits) * sizeof(unsigned long),
+ GFP_KERNEL_ACCOUNT);
+ if (!new_bitmap)
+ return -ENOMEM;
+ bitmap_clear(new_bitmap, maps->nbits, new_nbits - maps->nbits);
+ maps->bitmap = new_bitmap;
+ maps->nbits = new_nbits;
+ return 0;
+}
+
+static inline bool iommufd_sw_msi_maps_test_bit(const struct iommufd_sw_msi_maps *maps,
+ unsigned int id)
+{
+ if (id >= maps->nbits)
+ return false;
+ return test_bit(id, maps->bitmap);
+}
+
#ifdef CONFIG_IRQ_MSI_IOMMU
int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
struct iommufd_hwpt_paging *hwpt_paging,
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 03/17] iommufd: Split software MSI map lookup and allocation
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
2026-08-31 14:59 ` [PATCH v5 01/17] iommu/dma: Prepare MSI physical address lists Andrew Jones
2026-08-31 14:59 ` [PATCH v5 02/17] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 04/17] iommufd: Bound software MSI mappings to the reserved range Andrew Jones
` (14 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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
Separate lookup of an existing software MSI mapping from allocation of a
new mapping. This prepares the code for allocating multiple mappings for
an MSI physical address list.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/iommufd/driver.c | 23 +++++++++++++++++++++--
1 file changed, 21 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index d0b05b1fda42..3856989f5c4b 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -189,6 +189,23 @@ iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
phys_addr_t sw_msi_start)
{
struct iommufd_sw_msi_map *cur;
+
+ lockdep_assert_held(&ictx->sw_msi_lock);
+
+ list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) {
+ if (cur->sw_msi_start != sw_msi_start)
+ continue;
+ if (cur->msi_addr == msi_addr)
+ return cur;
+ }
+ return NULL;
+}
+
+static struct iommufd_sw_msi_map *
+iommufd_sw_msi_alloc_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
+ phys_addr_t sw_msi_start)
+{
+ struct iommufd_sw_msi_map *cur;
unsigned int max_pgoff = 0;
lockdep_assert_held(&ictx->sw_msi_lock);
@@ -199,8 +216,6 @@ iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
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 > IOMMUFD_SW_MSI_MAX_ID ||
@@ -293,6 +308,10 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
msi_map = iommufd_sw_msi_get_map(handle->idev->ictx,
msi_addr & PAGE_MASK,
handle->idev->igroup->sw_msi_start);
+ if (!msi_map)
+ msi_map = iommufd_sw_msi_alloc_map(handle->idev->ictx,
+ msi_addr & PAGE_MASK,
+ handle->idev->igroup->sw_msi_start);
if (IS_ERR(msi_map))
return PTR_ERR(msi_map);
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 04/17] iommufd: Bound software MSI mappings to the reserved range
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (2 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 03/17] iommufd: Split software MSI map lookup and allocation Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 05/17] iommufd: Prepare software MSI maps for address lists Andrew Jones
` (13 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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
Track the start and length of the software MSI reserved region as one
range and pass it to the mapping lookup and allocation helpers.
Reject cached mappings outside the region and new mappings when no
complete page remains. This enforces the reserved region bounds and
prepares the helpers to allocate contiguous MSI address lists.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/iommufd/device.c | 17 +++++++++++------
drivers/iommu/iommufd/driver.c | 21 ++++++++++++---------
drivers/iommu/iommufd/io_pagetable.c | 9 +++++----
drivers/iommu/iommufd/iommufd_private.h | 9 +++++++--
4 files changed, 35 insertions(+), 21 deletions(-)
diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
index 868f1e591208..7b83810a1c90 100644
--- a/drivers/iommu/iommufd/device.c
+++ b/drivers/iommu/iommufd/device.c
@@ -97,7 +97,7 @@ static struct iommufd_group *iommufd_get_group(struct iommufd_ctx *ictx,
kref_init(&new_igroup->ref);
mutex_init(&new_igroup->lock);
xa_init(&new_igroup->pasid_attach);
- new_igroup->sw_msi_start = PHYS_ADDR_MAX;
+ new_igroup->sw_msi_range.start = PHYS_ADDR_MAX;
/* group reference moves into new_igroup */
new_igroup->group = group;
@@ -374,7 +374,7 @@ static int iommufd_group_setup_msi(struct iommufd_group *igroup,
struct iommufd_ctx *ictx = igroup->ictx;
struct iommufd_sw_msi_map *cur;
- if (igroup->sw_msi_start == PHYS_ADDR_MAX)
+ if (igroup->sw_msi_range.start == PHYS_ADDR_MAX)
return 0;
/*
@@ -384,7 +384,7 @@ static int iommufd_group_setup_msi(struct iommufd_group *igroup,
list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) {
int rc;
- if (cur->sw_msi_start != igroup->sw_msi_start ||
+ if (cur->sw_msi_start != igroup->sw_msi_range.start ||
!iommufd_sw_msi_maps_test_bit(&igroup->required_sw_msi, cur->id))
continue;
@@ -414,18 +414,23 @@ static int
iommufd_device_attach_reserved_iova(struct iommufd_device *idev,
struct iommufd_hwpt_paging *hwpt_paging)
{
+ struct iommufd_sw_msi_range sw_msi_range = {
+ .start = PHYS_ADDR_MAX,
+ };
struct iommufd_group *igroup = idev->igroup;
+ bool first_attach;
int rc;
lockdep_assert_held(&igroup->lock);
+ first_attach = iommufd_group_first_attach(igroup, IOMMU_NO_PASID);
rc = iopt_table_enforce_dev_resv_regions(&hwpt_paging->ioas->iopt,
- idev->dev,
- &igroup->sw_msi_start);
+ idev->dev, &sw_msi_range);
if (rc)
return rc;
- if (iommufd_group_first_attach(igroup, IOMMU_NO_PASID)) {
+ if (first_attach) {
+ igroup->sw_msi_range = sw_msi_range;
rc = iommufd_group_setup_msi(igroup, hwpt_paging);
if (rc) {
iopt_remove_reserved_iova(&hwpt_paging->ioas->iopt,
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 3856989f5c4b..8bb47a81fed1 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -186,14 +186,15 @@ EXPORT_SYMBOL_NS_GPL(iommufd_viommu_report_event, "IOMMUFD");
*/
static struct iommufd_sw_msi_map *
iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
- phys_addr_t sw_msi_start)
+ const struct iommufd_sw_msi_range *sw_msi_range)
{
struct iommufd_sw_msi_map *cur;
lockdep_assert_held(&ictx->sw_msi_lock);
list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) {
- if (cur->sw_msi_start != sw_msi_start)
+ if (cur->sw_msi_start != sw_msi_range->start ||
+ cur->pgoff >= sw_msi_range->length / PAGE_SIZE)
continue;
if (cur->msi_addr == msi_addr)
return cur;
@@ -203,7 +204,7 @@ iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
static struct iommufd_sw_msi_map *
iommufd_sw_msi_alloc_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
- phys_addr_t sw_msi_start)
+ const struct iommufd_sw_msi_range *sw_msi_range)
{
struct iommufd_sw_msi_map *cur;
unsigned int max_pgoff = 0;
@@ -211,7 +212,7 @@ iommufd_sw_msi_alloc_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
lockdep_assert_held(&ictx->sw_msi_lock);
list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) {
- if (cur->sw_msi_start != sw_msi_start)
+ if (cur->sw_msi_start != sw_msi_range->start)
continue;
if (cur->pgoff == UINT_MAX)
return ERR_PTR(-EOVERFLOW);
@@ -219,14 +220,16 @@ iommufd_sw_msi_alloc_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
}
if (ictx->sw_msi_id > IOMMUFD_SW_MSI_MAX_ID ||
- max_pgoff > (ULONG_MAX - sw_msi_start) / PAGE_SIZE)
+ max_pgoff > (ULONG_MAX - sw_msi_range->start) / PAGE_SIZE)
return ERR_PTR(-EOVERFLOW);
+ if (max_pgoff >= sw_msi_range->length / PAGE_SIZE)
+ return ERR_PTR(-ENOSPC);
cur = kzalloc_obj(*cur);
if (!cur)
return ERR_PTR(-ENOMEM);
- cur->sw_msi_start = sw_msi_start;
+ cur->sw_msi_start = sw_msi_range->start;
cur->msi_addr = msi_addr;
cur->pgoff = max_pgoff;
cur->id = ictx->sw_msi_id++;
@@ -295,7 +298,7 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
handle = to_iommufd_handle(raw_handle);
/* No IOMMU_RESV_SW_MSI means no change to the msi_msg */
- if (handle->idev->igroup->sw_msi_start == PHYS_ADDR_MAX)
+ if (handle->idev->igroup->sw_msi_range.start == PHYS_ADDR_MAX)
return 0;
ictx = handle->idev->ictx;
@@ -307,11 +310,11 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
*/
msi_map = iommufd_sw_msi_get_map(handle->idev->ictx,
msi_addr & PAGE_MASK,
- handle->idev->igroup->sw_msi_start);
+ &handle->idev->igroup->sw_msi_range);
if (!msi_map)
msi_map = iommufd_sw_msi_alloc_map(handle->idev->ictx,
msi_addr & PAGE_MASK,
- handle->idev->igroup->sw_msi_start);
+ &handle->idev->igroup->sw_msi_range);
if (IS_ERR(msi_map))
return PTR_ERR(msi_map);
diff --git a/drivers/iommu/iommufd/io_pagetable.c b/drivers/iommu/iommufd/io_pagetable.c
index 24d4917105d9..8bf2554af334 100644
--- a/drivers/iommu/iommufd/io_pagetable.c
+++ b/drivers/iommu/iommufd/io_pagetable.c
@@ -1501,7 +1501,7 @@ void iopt_remove_access(struct io_pagetable *iopt,
/* Narrow the valid_iova_itree to include reserved ranges from a device. */
int iopt_table_enforce_dev_resv_regions(struct io_pagetable *iopt,
struct device *dev,
- phys_addr_t *sw_msi_start)
+ struct iommufd_sw_msi_range *sw_msi_range)
{
struct iommu_resv_region *resv;
LIST_HEAD(resv_regions);
@@ -1520,10 +1520,11 @@ int iopt_table_enforce_dev_resv_regions(struct io_pagetable *iopt,
if (resv->type == IOMMU_RESV_DIRECT_RELAXABLE)
continue;
- if (sw_msi_start && resv->type == IOMMU_RESV_MSI)
+ if (sw_msi_range && resv->type == IOMMU_RESV_MSI)
num_hw_msi++;
- if (sw_msi_start && resv->type == IOMMU_RESV_SW_MSI) {
- *sw_msi_start = resv->start;
+ if (sw_msi_range && resv->type == IOMMU_RESV_SW_MSI) {
+ sw_msi_range->start = resv->start;
+ sw_msi_range->length = resv->length;
num_sw_msi++;
}
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 9ca5f9f92cdf..4e2d32809695 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -22,6 +22,11 @@ struct iommu_option;
struct iommufd_device;
struct dma_buf_attachment;
+struct iommufd_sw_msi_range {
+ phys_addr_t start;
+ size_t length;
+};
+
struct iommufd_sw_msi_map {
struct list_head sw_msi_item;
phys_addr_t sw_msi_start;
@@ -171,7 +176,7 @@ void iopt_table_remove_domain(struct io_pagetable *iopt,
struct iommu_domain *domain);
int iopt_table_enforce_dev_resv_regions(struct io_pagetable *iopt,
struct device *dev,
- phys_addr_t *sw_msi_start);
+ struct iommufd_sw_msi_range *sw_msi_range);
int iopt_set_allow_iova(struct io_pagetable *iopt,
struct rb_root_cached *allowed_iova);
int iopt_reserve_iova(struct io_pagetable *iopt, unsigned long start,
@@ -510,7 +515,7 @@ struct iommufd_group {
struct iommu_group *group;
struct xarray pasid_attach;
struct iommufd_sw_msi_maps required_sw_msi;
- phys_addr_t sw_msi_start;
+ struct iommufd_sw_msi_range sw_msi_range;
};
/*
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 05/17] iommufd: Prepare software MSI maps for address lists
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (3 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 04/17] iommufd: Bound software MSI mappings to the reserved range Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 06/17] iommufd: Install software MSI map ranges atomically Andrew Jones
` (12 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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
Teach iommufd_sw_msi_get_map() to match an ordered physical address list
against an existing contiguous IOVA range. Mark the first map with the
range size so an identical list can reuse the allocation.
Teach iommufd_sw_msi_alloc_map() to reserve identifiers and offsets for
the complete list and build its maps on a temporary list. Keep the
existing scalar installation behavior and only publish newly allocated
maps after installation succeeds.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/iommufd/driver.c | 161 ++++++++++++++++++------
drivers/iommu/iommufd/iommufd_private.h | 1 +
2 files changed, 123 insertions(+), 39 deletions(-)
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 8bb47a81fed1..6a0527f4b058 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -179,35 +179,91 @@ EXPORT_SYMBOL_NS_GPL(iommufd_viommu_report_event, "IOMMUFD");
#ifdef CONFIG_IRQ_MSI_IOMMU
/*
- * Get a iommufd_sw_msi_map for the msi physical address requested by the irq
+ * Get an iommufd_sw_msi_map for the msi physical addresses requested by the irq
* layer. The mapping to IOVA is global to the iommufd file descriptor, every
* domain that is attached to a device using the same MSI parameters will use
- * the same IOVA.
+ * the same contiguous IOVA range.
*/
static struct iommufd_sw_msi_map *
-iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
- const struct iommufd_sw_msi_range *sw_msi_range)
+iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, const phys_addr_t *phys_addrs,
+ unsigned int nr_addrs, const struct iommufd_sw_msi_range *sw_msi_range)
{
- struct iommufd_sw_msi_map *cur;
+ struct iommufd_sw_msi_map *cur, *msi_map;
+ unsigned int nr_found;
lockdep_assert_held(&ictx->sw_msi_lock);
- list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) {
- if (cur->sw_msi_start != sw_msi_range->start ||
- cur->pgoff >= sw_msi_range->length / PAGE_SIZE)
+ list_for_each_entry(msi_map, &ictx->sw_msi_list, sw_msi_item) {
+ if (msi_map->sw_msi_start != sw_msi_range->start ||
+ msi_map->msi_addr != phys_addrs[0])
+ continue;
+ if (msi_map->range_size != nr_addrs * PAGE_SIZE)
continue;
- if (cur->msi_addr == msi_addr)
- return cur;
+ if (msi_map->pgoff > sw_msi_range->length / PAGE_SIZE ||
+ nr_addrs > sw_msi_range->length / PAGE_SIZE - msi_map->pgoff)
+ continue;
+
+ nr_found = 0;
+ list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) {
+ unsigned int index;
+
+ if (cur->sw_msi_start != sw_msi_range->start ||
+ cur->pgoff < msi_map->pgoff)
+ continue;
+ index = cur->pgoff - msi_map->pgoff;
+ if (index >= nr_addrs)
+ continue;
+ if (cur->msi_addr != phys_addrs[index])
+ break;
+ nr_found++;
+ }
+ if (nr_found == nr_addrs)
+ return msi_map;
}
return NULL;
}
+static int iommufd_sw_msi_check_alloc(struct iommufd_ctx *ictx,
+ const struct iommufd_sw_msi_range *sw_msi_range,
+ unsigned int first_pgoff, unsigned int nr_addrs,
+ size_t *range_size)
+{
+ unsigned long max_iova_pgoff;
+ unsigned int last_pgoff;
+ unsigned int last_id;
+ size_t range_pages;
+
+ if (!nr_addrs)
+ return -EINVAL;
+ if (sw_msi_range->start > ULONG_MAX)
+ return -EOVERFLOW;
+
+ range_pages = sw_msi_range->length / PAGE_SIZE;
+ max_iova_pgoff = (ULONG_MAX - sw_msi_range->start) / PAGE_SIZE;
+
+ if (check_add_overflow(ictx->sw_msi_id, nr_addrs - 1, &last_id) ||
+ last_id > IOMMUFD_SW_MSI_MAX_ID ||
+ check_add_overflow(first_pgoff, nr_addrs - 1, &last_pgoff) ||
+ last_pgoff > max_iova_pgoff ||
+ check_mul_overflow((size_t)nr_addrs, PAGE_SIZE, range_size))
+ return -EOVERFLOW;
+
+ if (last_pgoff >= range_pages)
+ return -ENOSPC;
+
+ return 0;
+}
+
static struct iommufd_sw_msi_map *
-iommufd_sw_msi_alloc_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
- const struct iommufd_sw_msi_range *sw_msi_range)
+iommufd_sw_msi_alloc_map(struct iommufd_ctx *ictx, const phys_addr_t *phys_addrs,
+ unsigned int nr_addrs, const struct iommufd_sw_msi_range *sw_msi_range,
+ struct list_head *new_msi_maps)
{
- struct iommufd_sw_msi_map *cur;
- unsigned int max_pgoff = 0;
+ struct iommufd_sw_msi_map *cur, *first_map = NULL;
+ unsigned int next_pgoff = 0;
+ unsigned int i;
+ size_t size;
+ int rc;
lockdep_assert_held(&ictx->sw_msi_lock);
@@ -216,25 +272,38 @@ iommufd_sw_msi_alloc_map(struct iommufd_ctx *ictx, phys_addr_t msi_addr,
continue;
if (cur->pgoff == UINT_MAX)
return ERR_PTR(-EOVERFLOW);
- max_pgoff = max(max_pgoff, cur->pgoff + 1);
+ next_pgoff = max(next_pgoff, cur->pgoff + 1);
}
- if (ictx->sw_msi_id > IOMMUFD_SW_MSI_MAX_ID ||
- max_pgoff > (ULONG_MAX - sw_msi_range->start) / PAGE_SIZE)
- return ERR_PTR(-EOVERFLOW);
- if (max_pgoff >= sw_msi_range->length / PAGE_SIZE)
- return ERR_PTR(-ENOSPC);
-
- cur = kzalloc_obj(*cur);
- if (!cur)
- return ERR_PTR(-ENOMEM);
-
- cur->sw_msi_start = sw_msi_range->start;
- cur->msi_addr = msi_addr;
- cur->pgoff = max_pgoff;
- cur->id = ictx->sw_msi_id++;
- list_add_tail(&cur->sw_msi_item, &ictx->sw_msi_list);
- return cur;
+ rc = iommufd_sw_msi_check_alloc(ictx, sw_msi_range, next_pgoff, nr_addrs, &size);
+ if (rc)
+ return ERR_PTR(rc);
+
+ for (i = 0; i < nr_addrs; i++) {
+ cur = kzalloc_obj(*cur);
+ if (!cur)
+ goto err_free;
+
+ cur->sw_msi_start = sw_msi_range->start;
+ cur->msi_addr = phys_addrs[i];
+ cur->pgoff = next_pgoff + i;
+ cur->id = ictx->sw_msi_id + i;
+ if (!i) {
+ cur->range_size = size;
+ first_map = cur;
+ }
+ list_add_tail(&cur->sw_msi_item, new_msi_maps);
+ }
+
+ return first_map;
+
+err_free:
+ while (!list_empty(new_msi_maps)) {
+ cur = list_first_entry(new_msi_maps, typeof(*cur), sw_msi_item);
+ list_del(&cur->sw_msi_item);
+ kfree(cur);
+ }
+ return ERR_PTR(-ENOMEM);
}
int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
@@ -280,7 +349,9 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
struct iommufd_attach_handle *handle;
struct iommufd_sw_msi_map *msi_map;
struct iommufd_ctx *ictx;
+ LIST_HEAD(new_msi_maps);
unsigned long iova;
+ phys_addr_t phys_addr;
int rc;
/*
@@ -308,29 +379,41 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
* 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,
- &handle->idev->igroup->sw_msi_range);
+ phys_addr = msi_addr & PAGE_MASK;
+ msi_map = iommufd_sw_msi_get_map(ictx, &phys_addr, 1, &handle->idev->igroup->sw_msi_range);
if (!msi_map)
- msi_map = iommufd_sw_msi_alloc_map(handle->idev->ictx,
- msi_addr & PAGE_MASK,
- &handle->idev->igroup->sw_msi_range);
+ msi_map = iommufd_sw_msi_alloc_map(ictx, &phys_addr, 1,
+ &handle->idev->igroup->sw_msi_range,
+ &new_msi_maps);
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;
+ goto err_free;
rc = iommufd_sw_msi_install(ictx, hwpt_paging, msi_map);
if (rc)
- return rc;
+ goto err_free;
__set_bit(msi_map->id, handle->idev->igroup->required_sw_msi.bitmap);
+ if (!list_empty(&new_msi_maps)) {
+ list_splice_tail_init(&new_msi_maps, &ictx->sw_msi_list);
+ ictx->sw_msi_id++;
+ }
+
iova = msi_map->sw_msi_start + msi_map->pgoff * PAGE_SIZE;
msi_desc_set_iommu_msi_iova(desc, iova, PAGE_SHIFT);
return 0;
+
+err_free:
+ while (!list_empty(&new_msi_maps)) {
+ msi_map = list_first_entry(&new_msi_maps, typeof(*msi_map), sw_msi_item);
+ list_del(&msi_map->sw_msi_item);
+ kfree(msi_map);
+ }
+ return rc;
}
EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi, "IOMMUFD");
#endif
diff --git a/drivers/iommu/iommufd/iommufd_private.h b/drivers/iommu/iommufd/iommufd_private.h
index 4e2d32809695..797965557281 100644
--- a/drivers/iommu/iommufd/iommufd_private.h
+++ b/drivers/iommu/iommufd/iommufd_private.h
@@ -33,6 +33,7 @@ struct iommufd_sw_msi_map {
phys_addr_t msi_addr;
unsigned int pgoff;
unsigned int id;
+ size_t range_size; /* IOVA range size, or 0 if not the first map */
};
/* Bitmap of struct iommufd_sw_msi_map::id; starts empty, grows on demand. */
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 06/17] iommufd: Install software MSI map ranges atomically
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (4 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 05/17] iommufd: Prepare software MSI maps for address lists Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 07/17] iommufd: Prepare software MSI installation for address lists Andrew Jones
` (11 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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 range installation helper which installs every map in a contiguous
software MSI range and rolls back only mappings added by the failed
operation. Use the helper when replaying range mappings into a new
paging domain.
This prepares iommufd to publish and install physical address lists
without exposing partially installed ranges.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/iommufd/driver.c | 76 ++++++++++++++++++++++++++++++----
1 file changed, 68 insertions(+), 8 deletions(-)
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 6a0527f4b058..a43a25078f87 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -178,6 +178,17 @@ int iommufd_viommu_report_event(struct iommufd_viommu *viommu,
EXPORT_SYMBOL_NS_GPL(iommufd_viommu_report_event, "IOMMUFD");
#ifdef CONFIG_IRQ_MSI_IOMMU
+static bool iommufd_sw_msi_range_index(const struct iommufd_sw_msi_map *map,
+ const struct iommufd_sw_msi_map *base_map,
+ unsigned int nr_addrs, unsigned int *index)
+{
+ if (map->sw_msi_start != base_map->sw_msi_start || map->pgoff < base_map->pgoff)
+ return false;
+
+ *index = map->pgoff - base_map->pgoff;
+ return *index < nr_addrs;
+}
+
/*
* Get an iommufd_sw_msi_map for the msi physical addresses requested by the irq
* layer. The mapping to IOVA is global to the iommufd file descriptor, every
@@ -207,11 +218,7 @@ iommufd_sw_msi_get_map(struct iommufd_ctx *ictx, const phys_addr_t *phys_addrs,
list_for_each_entry(cur, &ictx->sw_msi_list, sw_msi_item) {
unsigned int index;
- if (cur->sw_msi_start != sw_msi_range->start ||
- cur->pgoff < msi_map->pgoff)
- continue;
- index = cur->pgoff - msi_map->pgoff;
- if (index >= nr_addrs)
+ if (!iommufd_sw_msi_range_index(cur, msi_map, nr_addrs, &index))
continue;
if (cur->msi_addr != phys_addrs[index])
break;
@@ -306,9 +313,9 @@ iommufd_sw_msi_alloc_map(struct iommufd_ctx *ictx, const phys_addr_t *phys_addrs
return ERR_PTR(-ENOMEM);
}
-int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
- struct iommufd_hwpt_paging *hwpt_paging,
- struct iommufd_sw_msi_map *msi_map)
+static int iommufd_sw_msi_install_one(struct iommufd_ctx *ictx,
+ struct iommufd_hwpt_paging *hwpt_paging,
+ struct iommufd_sw_msi_map *msi_map)
{
unsigned long iova;
int rc;
@@ -332,6 +339,59 @@ int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
__set_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap);
return 0;
}
+
+int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
+ struct iommufd_hwpt_paging *hwpt_paging,
+ struct iommufd_sw_msi_map *base_map)
+{
+ struct iommufd_sw_msi_map *msi_map;
+ struct list_head *msi_maps = &ictx->sw_msi_list;
+ unsigned long *newly_mapped;
+ unsigned int nr_addrs = base_map->range_size ? base_map->range_size / PAGE_SIZE : 1;
+ unsigned int nr_found = 0;
+ unsigned int index;
+ int rc = 0;
+
+ newly_mapped = bitmap_zalloc(nr_addrs, GFP_KERNEL_ACCOUNT);
+ if (!newly_mapped)
+ return -ENOMEM;
+
+ list_for_each_entry(msi_map, msi_maps, sw_msi_item) {
+ if (!iommufd_sw_msi_range_index(msi_map, base_map, nr_addrs, &index))
+ continue;
+ if (iommufd_sw_msi_maps_test_bit(&hwpt_paging->present_sw_msi, msi_map->id)) {
+ nr_found++;
+ continue;
+ }
+ rc = iommufd_sw_msi_install_one(ictx, hwpt_paging, msi_map);
+ if (rc)
+ goto err_unmap;
+ __set_bit(index, newly_mapped);
+ nr_found++;
+ }
+ if (nr_found != nr_addrs) {
+ rc = -EINVAL;
+ goto err_unmap;
+ }
+
+ bitmap_free(newly_mapped);
+ return 0;
+
+err_unmap:
+ list_for_each_entry(msi_map, msi_maps, sw_msi_item) {
+ unsigned long iova;
+
+ if (!iommufd_sw_msi_range_index(msi_map, base_map, nr_addrs, &index) ||
+ !test_bit(index, newly_mapped))
+ continue;
+
+ iova = msi_map->sw_msi_start + msi_map->pgoff * PAGE_SIZE;
+ WARN_ON_ONCE(iommu_unmap(hwpt_paging->common.domain, iova, PAGE_SIZE) != PAGE_SIZE);
+ __clear_bit(msi_map->id, hwpt_paging->present_sw_msi.bitmap);
+ }
+ bitmap_free(newly_mapped);
+ return rc;
+}
EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi_install, "IOMMUFD_INTERNAL");
/*
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 07/17] iommufd: Prepare software MSI installation for address lists
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (5 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 06/17] iommufd: Install software MSI map ranges atomically Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 08/17] iommu/dma: Introduce iommu_dma_prepare_msi_list() Andrew Jones
` (10 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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
Teach the software MSI path to install every mapping in an address list
before publishing newly allocated maps. Mark the complete list as
required by the group and roll back mappings installed by a failed
operation.
Keep iommufd_sw_msi() as a one-address wrapper so existing callers
retain their current behavior.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/iommufd/driver.c | 101 +++++++++++++++++++++++----------
1 file changed, 72 insertions(+), 29 deletions(-)
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index a43a25078f87..45278c11c688 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -340,14 +340,14 @@ static int iommufd_sw_msi_install_one(struct iommufd_ctx *ictx,
return 0;
}
-int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
- struct iommufd_hwpt_paging *hwpt_paging,
- struct iommufd_sw_msi_map *base_map)
+static int __iommufd_sw_msi_install_range(struct iommufd_ctx *ictx,
+ struct iommufd_hwpt_paging *hwpt_paging,
+ struct list_head *msi_maps,
+ const struct iommufd_sw_msi_map *base_map,
+ unsigned int nr_addrs)
{
struct iommufd_sw_msi_map *msi_map;
- struct list_head *msi_maps = &ictx->sw_msi_list;
unsigned long *newly_mapped;
- unsigned int nr_addrs = base_map->range_size ? base_map->range_size / PAGE_SIZE : 1;
unsigned int nr_found = 0;
unsigned int index;
int rc = 0;
@@ -392,16 +392,36 @@ int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
bitmap_free(newly_mapped);
return rc;
}
+
+int iommufd_sw_msi_install(struct iommufd_ctx *ictx,
+ struct iommufd_hwpt_paging *hwpt_paging,
+ struct iommufd_sw_msi_map *base_map)
+{
+ unsigned int nr_addrs = base_map->range_size ? base_map->range_size / PAGE_SIZE : 1;
+
+ return __iommufd_sw_msi_install_range(ictx, hwpt_paging, &ictx->sw_msi_list, base_map,
+ nr_addrs);
+}
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.
- */
-int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr)
+static void iommufd_sw_msi_set_required(struct iommufd_group *igroup,
+ struct list_head *msi_maps,
+ const struct iommufd_sw_msi_map *base_map,
+ unsigned int nr_addrs)
+{
+ struct iommufd_sw_msi_map *msi_map;
+ unsigned int index;
+
+ list_for_each_entry(msi_map, msi_maps, sw_msi_item) {
+ if (!iommufd_sw_msi_range_index(msi_map, base_map, nr_addrs, &index))
+ continue;
+ __set_bit(msi_map->id, igroup->required_sw_msi.bitmap);
+ }
+}
+
+static int iommufd_sw_msi_list(struct iommu_domain *domain, struct msi_desc *desc,
+ const phys_addr_t *phys_addrs, unsigned int nr_addrs,
+ size_t granule)
{
struct device *dev = msi_desc_to_dev(desc);
struct iommufd_hwpt_paging *hwpt_paging;
@@ -410,10 +430,19 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
struct iommufd_sw_msi_map *msi_map;
struct iommufd_ctx *ictx;
LIST_HEAD(new_msi_maps);
+ struct list_head *msi_maps;
unsigned long iova;
- phys_addr_t phys_addr;
+ unsigned int i;
int rc;
+ if (granule != PAGE_SIZE)
+ return -EOPNOTSUPP;
+ if (!nr_addrs || nr_addrs > SIZE_MAX / PAGE_SIZE)
+ return -EINVAL;
+ for (i = 0; i < nr_addrs; i++)
+ if (!IS_ALIGNED(phys_addrs[i], PAGE_SIZE))
+ return -EINVAL;
+
/*
* 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
@@ -434,33 +463,33 @@ 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.
- */
- phys_addr = msi_addr & PAGE_MASK;
- msi_map = iommufd_sw_msi_get_map(ictx, &phys_addr, 1, &handle->idev->igroup->sw_msi_range);
- if (!msi_map)
- msi_map = iommufd_sw_msi_alloc_map(ictx, &phys_addr, 1,
+ msi_map = iommufd_sw_msi_get_map(ictx, phys_addrs, nr_addrs,
+ &handle->idev->igroup->sw_msi_range);
+ if (msi_map) {
+ msi_maps = &ictx->sw_msi_list;
+ } else {
+ msi_map = iommufd_sw_msi_alloc_map(ictx, phys_addrs, nr_addrs,
&handle->idev->igroup->sw_msi_range,
&new_msi_maps);
- if (IS_ERR(msi_map))
- return PTR_ERR(msi_map);
+ if (IS_ERR(msi_map))
+ return PTR_ERR(msi_map);
+ msi_maps = &new_msi_maps;
+ }
rc = iommufd_sw_msi_maps_ensure(&handle->idev->igroup->required_sw_msi,
- msi_map->id);
+ msi_map->id + nr_addrs - 1);
if (rc)
goto err_free;
- rc = iommufd_sw_msi_install(ictx, hwpt_paging, msi_map);
+ rc = __iommufd_sw_msi_install_range(ictx, hwpt_paging, msi_maps, msi_map, nr_addrs);
if (rc)
goto err_free;
- __set_bit(msi_map->id, handle->idev->igroup->required_sw_msi.bitmap);
+
+ iommufd_sw_msi_set_required(handle->idev->igroup, msi_maps, msi_map, nr_addrs);
if (!list_empty(&new_msi_maps)) {
list_splice_tail_init(&new_msi_maps, &ictx->sw_msi_list);
- ictx->sw_msi_id++;
+ ictx->sw_msi_id += nr_addrs;
}
iova = msi_map->sw_msi_start + msi_map->pgoff * PAGE_SIZE;
@@ -475,6 +504,20 @@ int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
}
return rc;
}
+
+/*
+ * 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.
+ */
+int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
+ phys_addr_t msi_addr)
+{
+ phys_addr_t phys_addr = msi_addr & PAGE_MASK;
+
+ return iommufd_sw_msi_list(domain, desc, &phys_addr, 1, PAGE_SIZE);
+}
EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi, "IOMMUFD");
#endif
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 08/17] iommu/dma: Introduce iommu_dma_prepare_msi_list()
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (6 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 07/17] iommufd: Prepare software MSI installation for address lists Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 09/17] iommu/riscv: Report cache coherency capability Andrew Jones
` (9 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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_prepare_msi_list() to map an ordered physical address list
through the current domain backend. Validate that the requested granule
is a power of two no smaller than PAGE_SIZE and that every address is
aligned to it.
Refactor iommu_dma_prepare_msi() to obtain the backend granule and align
its single address before using the same list dispatcher. Convert the
DMA-IOMMU and iommufd backend entry points to consume address lists
directly.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/dma-iommu.c | 13 ++--
drivers/iommu/dma-iommu.h | 13 +++-
drivers/iommu/iommu-priv.h | 7 ++-
drivers/iommu/iommu.c | 109 +++++++++++++++++++++++++++------
drivers/iommu/iommufd/driver.c | 24 +++-----
include/linux/iommu.h | 8 +++
6 files changed, 126 insertions(+), 48 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 38a0c4e244d6..c9fdc792416c 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2173,7 +2173,7 @@ static bool has_msi_cookie(const struct iommu_domain *domain)
domain->cookie_type == IOMMU_COOKIE_DMA_MSI);
}
-static size_t cookie_msi_granule(const struct iommu_domain *domain)
+size_t iommu_dma_msi_granule(const struct iommu_domain *domain)
{
switch (domain->cookie_type) {
case IOMMU_COOKIE_DMA_IOVA:
@@ -2304,23 +2304,20 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
}
int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr)
+ const phys_addr_t *phys_addrs, unsigned int nr_addrs, size_t granule)
{
struct device *dev = msi_desc_to_dev(desc);
const struct iommu_dma_msi_page *msi_page;
- phys_addr_t phys_addr;
- size_t granule;
if (!has_msi_cookie(domain)) {
msi_desc_set_iommu_msi_iova(desc, 0, 0);
return 0;
}
-
- granule = cookie_msi_granule(domain);
- phys_addr = ALIGN_DOWN(msi_addr, granule);
+ if (granule != iommu_dma_msi_granule(domain))
+ return -EOPNOTSUPP;
iommu_group_mutex_assert(dev);
- msi_page = iommu_dma_get_msi_page(dev, &phys_addr, 1, granule, domain);
+ msi_page = iommu_dma_get_msi_page(dev, phys_addrs, nr_addrs, granule, domain);
if (!msi_page)
return -ENOMEM;
diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 040d00252563..bf9cd4102d35 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -19,8 +19,9 @@ int iommu_dma_init_fq(struct iommu_domain *domain);
void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);
+size_t iommu_dma_msi_granule(const struct iommu_domain *domain);
int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr);
+ const phys_addr_t *phys_addrs, unsigned int nr_addrs, size_t granule);
extern bool iommu_dma_forcedac;
@@ -53,8 +54,14 @@ static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_he
{
}
-static inline int iommu_dma_sw_msi(struct iommu_domain *domain,
- struct msi_desc *desc, phys_addr_t msi_addr)
+static inline size_t iommu_dma_msi_granule(const struct iommu_domain *domain)
+{
+ return 0;
+}
+
+static inline int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
+ const phys_addr_t *phys_addrs, unsigned int nr_addrs,
+ size_t granule)
{
return -ENODEV;
}
diff --git a/drivers/iommu/iommu-priv.h b/drivers/iommu/iommu-priv.h
index aaffad5854fc..1122c99566d5 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -54,10 +54,11 @@ 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);
+ const phys_addr_t *phys_addrs, unsigned int nr_addrs, size_t granule);
#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)
+static inline int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
+ const phys_addr_t *phys_addrs, unsigned int nr_addrs,
+ size_t granule)
{
return -EOPNOTSUPP;
}
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index a3dea7353500..692e793c624e 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -4223,42 +4223,115 @@ 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)
+static int __iommu_dma_prepare_msi_list(struct iommu_group *group, struct msi_desc *desc,
+ const phys_addr_t *phys_addrs, unsigned int nr_addrs,
+ size_t granule)
+{
+ if (!group->domain || group->domain->type == IOMMU_DOMAIN_IDENTITY)
+ return 0;
+
+ switch (group->domain->cookie_type) {
+ case IOMMU_COOKIE_DMA_MSI:
+ case IOMMU_COOKIE_DMA_IOVA:
+ return iommu_dma_sw_msi(group->domain, desc, phys_addrs, nr_addrs, granule);
+ case IOMMU_COOKIE_IOMMUFD:
+ return iommufd_sw_msi(group->domain, desc, phys_addrs, nr_addrs, granule);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int iommu_dma_validate_msi_list(const phys_addr_t *phys_addrs, unsigned int nr_addrs,
+ size_t granule)
+{
+ unsigned int i;
+
+ if (!nr_addrs || granule < PAGE_SIZE || !is_power_of_2(granule) ||
+ nr_addrs > SIZE_MAX / granule)
+ return -EINVAL;
+
+ for (i = 0; i < nr_addrs; i++)
+ if (!IS_ALIGNED(phys_addrs[i], granule))
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * iommu_dma_prepare_msi_list() - Map MSI pages in the IOMMU domain
+ * @desc: MSI descriptor to update with the base IOVA
+ * @phys_addrs: Ordered MSI target physical addresses
+ * @nr_addrs: Number of addresses in @phys_addrs
+ * @granule: Mapping granule for every address
+ *
+ * @nr_addrs must be nonzero and @granule must be a power of two no smaller than
+ * PAGE_SIZE. Every address must be aligned to @granule. The addresses are mapped
+ * in order to one contiguous IOVA range and may repeat. The list is consumed
+ * synchronously and is not retained.
+ *
+ * When a software MSI mapping is required, the backend records the base IOVA
+ * and granule shift in @desc. Otherwise, @desc is left unchanged.
+ *
+ * Return: 0 on success, -EINVAL if the parameters are invalid, -EOPNOTSUPP if
+ * the domain backend cannot provide the mapping, or another negative error
+ * code from the backend.
+ */
+int iommu_dma_prepare_msi_list(struct msi_desc *desc, const phys_addr_t *phys_addrs,
+ unsigned int nr_addrs, size_t granule)
+{
+ struct device *dev = msi_desc_to_dev(desc);
+ struct iommu_group *group = dev->iommu_group;
+ int ret;
+
+ ret = iommu_dma_validate_msi_list(phys_addrs, nr_addrs, granule);
+ if (ret || !group)
+ return ret;
+
+ mutex_lock(&group->mutex);
+ ret = __iommu_dma_prepare_msi_list(group, desc, phys_addrs, nr_addrs, granule);
+ mutex_unlock(&group->mutex);
+ return ret;
+}
+
/**
* iommu_dma_prepare_msi() - Map the MSI page in the IOMMU domain
* @desc: MSI descriptor, will store the MSI page
* @msi_addr: MSI target address to be mapped
*
- * The implementation of sw_msi() should take msi_addr and map it to
- * an IOVA in the domain and call msi_desc_set_iommu_msi_iova() with the
- * mapping information.
- *
* Return: 0 on success or negative error code if the mapping failed.
*/
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;
+ phys_addr_t phys_addr;
+ size_t granule;
int ret = 0;
if (!group)
return 0;
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;
- }
+ if (!group->domain || group->domain->type == IOMMU_DOMAIN_IDENTITY)
+ goto out_unlock;
+
+ switch (group->domain->cookie_type) {
+ case IOMMU_COOKIE_DMA_MSI:
+ case IOMMU_COOKIE_DMA_IOVA:
+ granule = iommu_dma_msi_granule(group->domain);
+ break;
+ case IOMMU_COOKIE_IOMMUFD:
+ granule = PAGE_SIZE;
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
}
+
+ phys_addr = ALIGN_DOWN(msi_addr, granule);
+ ret = __iommu_dma_prepare_msi_list(group, desc, &phys_addr, 1, granule);
+
+out_unlock:
mutex_unlock(&group->mutex);
return ret;
}
diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
index 45278c11c688..72e9ce63a5ee 100644
--- a/drivers/iommu/iommufd/driver.c
+++ b/drivers/iommu/iommufd/driver.c
@@ -419,9 +419,14 @@ static void iommufd_sw_msi_set_required(struct iommufd_group *igroup,
}
}
-static int iommufd_sw_msi_list(struct iommu_domain *domain, struct msi_desc *desc,
- const phys_addr_t *phys_addrs, unsigned int nr_addrs,
- size_t granule)
+/*
+ * Called by the irq code if the platform translates the MSI addresses through the
+ * IOMMU. phys_addrs are the physical addresses of the MSI pages. iommufd will
+ * allocate contiguous fd global iovas for the physical pages that are the same on
+ * all domains and devices.
+ */
+int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
+ const phys_addr_t *phys_addrs, unsigned int nr_addrs, size_t granule)
{
struct device *dev = msi_desc_to_dev(desc);
struct iommufd_hwpt_paging *hwpt_paging;
@@ -505,19 +510,6 @@ static int iommufd_sw_msi_list(struct iommu_domain *domain, struct msi_desc *des
return rc;
}
-/*
- * 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.
- */
-int iommufd_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr)
-{
- phys_addr_t phys_addr = msi_addr & PAGE_MASK;
-
- return iommufd_sw_msi_list(domain, desc, &phys_addr, 1, PAGE_SIZE);
-}
EXPORT_SYMBOL_NS_GPL(iommufd_sw_msi, "IOMMUFD");
#endif
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index ac43b8b93f14..bf08be172e40 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -1561,8 +1561,16 @@ static inline void pci_dev_reset_iommu_done(struct pci_dev *pdev)
#ifdef CONFIG_IRQ_MSI_IOMMU
#ifdef CONFIG_IOMMU_API
+int iommu_dma_prepare_msi_list(struct msi_desc *desc, const phys_addr_t *phys_addrs,
+ unsigned int nr_addrs, size_t granule);
int iommu_dma_prepare_msi(struct msi_desc *desc, phys_addr_t msi_addr);
#else
+static inline int iommu_dma_prepare_msi_list(struct msi_desc *desc, const phys_addr_t *phys_addrs,
+ unsigned int nr_addrs, size_t granule)
+{
+ return 0;
+}
+
static inline int iommu_dma_prepare_msi(struct msi_desc *desc,
phys_addr_t msi_addr)
{
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 09/17] iommu/riscv: Report cache coherency capability
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (7 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 08/17] iommu/dma: Introduce iommu_dma_prepare_msi_list() Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 10/17] iommu/riscv: Reserve an MSI IOVA window for iommufd Andrew Jones
` (8 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
VFIO and iommufd require IOMMU_CAP_CACHE_COHERENCY because they use
IOMMU_CACHE mappings and have no userspace cache-maintenance path.
RISC-V page tables preserve physical memory attributes for IOMMU_CACHE
mappings. Svpbmt marks mappings without IOMMU_CACHE as non-cacheable,
but preserving physical attributes cannot make a noncoherent device
path coherent.
Report the capability only for devices marked DMA coherent. This limits
userspace assignment to paths that can honor cacheable mappings.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
Reviewed-by: Jason Gunthorpe <jgg@nvidia.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 cec3ddd7ab10..e5818ce44515 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -16,6 +16,7 @@
#include <linux/acpi_rimt.h>
#include <linux/compiler.h>
#include <linux/crash_dump.h>
+#include <linux/dma-map-ops.h>
#include <linux/init.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
@@ -1422,6 +1423,16 @@ static struct iommu_group *riscv_iommu_device_group(struct device *dev)
return generic_device_group(dev);
}
+static bool riscv_iommu_capable(struct device *dev, enum iommu_cap cap)
+{
+ switch (cap) {
+ case IOMMU_CAP_CACHE_COHERENCY:
+ return dev_is_dma_coherent(dev);
+ default:
+ return false;
+ }
+}
+
static int riscv_iommu_of_xlate(struct device *dev, const struct of_phandle_args *args)
{
return iommu_fwspec_add_ids(dev, args->args, 1);
@@ -1483,6 +1494,7 @@ static void riscv_iommu_release_device(struct device *dev)
static const struct iommu_ops riscv_iommu_ops = {
.of_xlate = riscv_iommu_of_xlate,
+ .capable = riscv_iommu_capable,
.identity_domain = &riscv_iommu_identity_domain,
.blocked_domain = &riscv_iommu_blocking_domain,
.release_domain = &riscv_iommu_blocking_domain,
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 10/17] iommu/riscv: Reserve an MSI IOVA window for iommufd
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (8 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 09/17] iommu/riscv: Report cache coherency capability Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 11/17] irqchip/riscv-imsic: Add S-mode MSI address list Andrew Jones
` (7 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
iommufd requires an IOMMU_RESV_SW_MSI region to allocate stable IOVAs
for MSI targets. Advertise such a region when the device uses an IMSIC
MSI hierarchy so interrupt remapping can map IMSIC pages instead of
falling back to physical addresses.
Reserve one page per possible CPU, sufficient for each supervisor IMSIC
page. Place the window at 128 MiB, matching the established ARM SMMU MSI
IOVA convention rather than introducing an architecture-specific choice.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/riscv/iommu.c | 20 ++++++++++++++++++++
drivers/iommu/riscv/iommu.h | 4 ++++
drivers/irqchip/irq-riscv-imsic-state.c | 22 ++++++++++++++++++++++
include/linux/irqchip/riscv-imsic.h | 6 ++++++
4 files changed, 52 insertions(+)
diff --git a/drivers/iommu/riscv/iommu.c b/drivers/iommu/riscv/iommu.c
index e5818ce44515..7c4a250d61ca 100644
--- a/drivers/iommu/riscv/iommu.c
+++ b/drivers/iommu/riscv/iommu.c
@@ -20,10 +20,12 @@
#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 "../dma-iommu.h"
#include "../iommu-pages.h"
#include "iommu-bits.h"
#include "iommu.h"
@@ -1492,6 +1494,23 @@ 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)
+{
+ struct iommu_resv_region *region;
+
+ if (imsic_dev_has_imsic_msi_parent(dev)) {
+ /* Each hart has one S-mode IMSIC page, a.k.a MSI target page */
+ 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);
+ }
+
+ iommu_dma_get_resv_regions(dev, head);
+}
+
static const struct iommu_ops riscv_iommu_ops = {
.of_xlate = riscv_iommu_of_xlate,
.capable = riscv_iommu_capable,
@@ -1502,6 +1521,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 46df79dd5495..078ed0ea65bc 100644
--- a/drivers/iommu/riscv/iommu.h
+++ b/drivers/iommu/riscv/iommu.h
@@ -14,9 +14,13 @@
#include <linux/iommu.h>
#include <linux/types.h>
#include <linux/iopoll.h>
+#include <linux/sizes.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
+
struct riscv_iommu_device;
struct riscv_iommu_queue {
diff --git a/drivers/irqchip/irq-riscv-imsic-state.c b/drivers/irqchip/irq-riscv-imsic-state.c
index b8d1bbbf42f7..df38a7670a89 100644
--- a/drivers/irqchip/irq-riscv-imsic-state.c
+++ b/drivers/irqchip/irq-riscv-imsic-state.c
@@ -64,6 +64,28 @@ const struct imsic_global_config *imsic_get_global_config(void)
}
EXPORT_SYMBOL_GPL(imsic_get_global_config);
+/**
+ * imsic_dev_has_imsic_msi_parent - Check for an IMSIC MSI parent
+ * @dev: Device to check
+ *
+ * Return: true if @dev's MSI domain or any parent domain is the IMSIC base
+ * domain.
+ */
+bool imsic_dev_has_imsic_msi_parent(struct device *dev)
+{
+ struct irq_domain *domain;
+
+ if (!imsic || !imsic->base_domain)
+ return false;
+
+ for (domain = dev_get_msi_domain(dev); domain; domain = domain->parent)
+ if (domain == imsic->base_domain)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(imsic_dev_has_imsic_msi_parent);
+
static bool __imsic_eix_read_clear(unsigned long id, bool pend)
{
unsigned long isel, imask;
diff --git a/include/linux/irqchip/riscv-imsic.h b/include/linux/irqchip/riscv-imsic.h
index 61af3a5bea09..662cb0442424 100644
--- a/include/linux/irqchip/riscv-imsic.h
+++ b/include/linux/irqchip/riscv-imsic.h
@@ -81,6 +81,7 @@ struct imsic_global_config {
#ifdef CONFIG_RISCV_IMSIC
const struct imsic_global_config *imsic_get_global_config(void);
+bool imsic_dev_has_imsic_msi_parent(struct device *dev);
#else
@@ -89,6 +90,11 @@ static inline const struct imsic_global_config *imsic_get_global_config(void)
return NULL;
}
+static inline bool imsic_dev_has_imsic_msi_parent(struct device *dev)
+{
+ return false;
+}
+
#endif
#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_RISCV_IMSIC)
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 11/17] irqchip/riscv-imsic: Add S-mode MSI address list
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (9 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 10/17] iommu/riscv: Reserve an MSI IOVA window for iommufd Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-09-01 13:38 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 12/17] irqchip/riscv-imsic: Support IOMMU MSI address lists Andrew Jones
` (6 subsequent siblings)
17 siblings, 1 reply; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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
Upcoming RISC-V IOMMU interrupt remapping needs to map every possible
host IMSIC target into one contiguous MSI IOVA range. MSI message
composition then uses the target CPU's position within that range.
Build an S-mode IMSIC physical address array indexed by logical CPU.
Require every possible CPU to have an initialized IMSIC page before
publishing the array. This preserves physical address zero as a valid
target and guarantees the array has num_possible_cpus() entries.
The array size and each target's index can therefore be derived
directly, without storing duplicate count or per-CPU index metadata.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/irqchip/irq-riscv-imsic-state.c | 32 +++++++++++++++++++++++++
drivers/irqchip/irq-riscv-imsic-state.h | 1 +
2 files changed, 33 insertions(+)
diff --git a/drivers/irqchip/irq-riscv-imsic-state.c b/drivers/irqchip/irq-riscv-imsic-state.c
index df38a7670a89..c7ebddd308ec 100644
--- a/drivers/irqchip/irq-riscv-imsic-state.c
+++ b/drivers/irqchip/irq-riscv-imsic-state.c
@@ -709,6 +709,31 @@ static int __init imsic_get_mmio_resource(struct fwnode_handle *fwnode,
return of_address_to_resource(to_of_node(fwnode), index, res);
}
+static int __init imsic_init_smode_msi_pa(void)
+{
+ struct imsic_global_config *global = &imsic->global;
+ phys_addr_t *smode_msi_pa;
+ unsigned int cpu;
+
+ smode_msi_pa = kcalloc(num_possible_cpus(), sizeof(*smode_msi_pa), GFP_KERNEL);
+ if (!smode_msi_pa)
+ return -ENOMEM;
+
+ for_each_possible_cpu(cpu) {
+ struct imsic_local_config *local = per_cpu_ptr(global->local, cpu);
+
+ if (!local->msi_va) {
+ kfree(smode_msi_pa);
+ return -ENODEV;
+ }
+
+ smode_msi_pa[cpu] = local->msi_pa;
+ }
+
+ imsic->smode_msi_pa = smode_msi_pa;
+ return 0;
+}
+
static int __init imsic_parse_fwnode(struct fwnode_handle *fwnode,
struct imsic_global_config *global,
u32 *nr_parent_irqs,
@@ -959,6 +984,12 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque)
goto out_local_cleanup;
}
+ rc = imsic_init_smode_msi_pa();
+ if (rc) {
+ pr_err("%pfwP: failed to initialize S-mode MSI addresses\n", fwnode);
+ goto out_local_cleanup;
+ }
+
/* Initialize matrix allocator */
rc = imsic_matrix_init();
if (rc) {
@@ -984,6 +1015,7 @@ int __init imsic_setup_state(struct fwnode_handle *fwnode, void *opaque)
out_free_local:
free_percpu(imsic->global.local);
out_free_priv:
+ kfree(imsic->smode_msi_pa);
kfree(imsic);
imsic = NULL;
return rc;
diff --git a/drivers/irqchip/irq-riscv-imsic-state.h b/drivers/irqchip/irq-riscv-imsic-state.h
index c42ee180b305..11352f14e32d 100644
--- a/drivers/irqchip/irq-riscv-imsic-state.h
+++ b/drivers/irqchip/irq-riscv-imsic-state.h
@@ -49,6 +49,7 @@ struct imsic_priv {
/* Global configuration common for all HARTs */
struct imsic_global_config global;
+ phys_addr_t *smode_msi_pa;
/* Per-CPU state */
struct imsic_local_priv __percpu *lpriv;
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 12/17] irqchip/riscv-imsic: Support IOMMU MSI address lists
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (10 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 11/17] irqchip/riscv-imsic: Add S-mode MSI address list Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 13/17] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V Andrew Jones
` (5 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 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
RISC-V IOMMU host interrupt remapping maps every possible host IMSIC
target into one contiguous IOVA range. MSI message composition therefore
needs each target's position within that range.
When the S-mode IMSIC address list is available, prepare the complete
list while allocating an IRQ. This allows the IOMMU backend to map the
pages and cache the contiguous base IOVA in the MSI descriptor. Skip the
optional preparation when no list is available, allowing devices which
do not need IOMMU MSI translation to keep using physical MSI messages.
When the descriptor has an IOMMU MSI mapping, use the selected logical
CPU directly as the page index within the IOVA range. Use the same path
for initial composition and affinity updates. Messages remain physical
when no IOMMU MSI mapping is required.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/irqchip/Kconfig | 1 +
drivers/irqchip/irq-riscv-imsic-platform.c | 25 +++++++++++++++++++---
2 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 42f2278a702d..c7cda52b21eb 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -656,6 +656,7 @@ config RISCV_IMSIC
select IRQ_DOMAIN_HIERARCHY
select GENERIC_IRQ_MATRIX_ALLOCATOR
select GENERIC_MSI_IRQ
+ select IRQ_MSI_IOMMU
select IRQ_MSI_LIB
config RISCV_RPMI_SYSMSI
diff --git a/drivers/irqchip/irq-riscv-imsic-platform.c b/drivers/irqchip/irq-riscv-imsic-platform.c
index 643c8e459611..6e26a31f7503 100644
--- a/drivers/irqchip/irq-riscv-imsic-platform.c
+++ b/drivers/irqchip/irq-riscv-imsic-platform.c
@@ -10,6 +10,7 @@
#include <linux/cpu.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iommu.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/irqdomain.h>
@@ -69,8 +70,10 @@ static void imsic_irq_ack(struct irq_data *d)
irq_move_irq(d);
}
-static void imsic_irq_compose_vector_msg(struct imsic_vector *vec, struct msi_msg *msg)
+static void imsic_irq_compose_vector_msg(struct irq_data *d, struct imsic_vector *vec,
+ struct msi_msg *msg)
{
+ struct msi_desc *desc = irq_data_get_msi_desc(d);
phys_addr_t msi_addr;
if (WARN_ON(!vec))
@@ -79,6 +82,11 @@ static void imsic_irq_compose_vector_msg(struct imsic_vector *vec, struct msi_ms
if (WARN_ON(!imsic_cpu_page_phys(vec->cpu, 0, &msi_addr)))
return;
+ if (desc->iommu_msi_shift) {
+ msi_addr = (desc->iommu_msi_iova << desc->iommu_msi_shift) +
+ vec->cpu * IMSIC_MMIO_PAGE_SZ;
+ }
+
msg->address_hi = upper_32_bits(msi_addr);
msg->address_lo = lower_32_bits(msi_addr);
msg->data = vec->local_id;
@@ -86,7 +94,7 @@ static void imsic_irq_compose_vector_msg(struct imsic_vector *vec, struct msi_ms
static void imsic_irq_compose_msg(struct irq_data *d, struct msi_msg *msg)
{
- imsic_irq_compose_vector_msg(irq_data_get_irq_chip_data(d), msg);
+ imsic_irq_compose_vector_msg(d, irq_data_get_irq_chip_data(d), msg);
}
#ifdef CONFIG_SMP
@@ -94,7 +102,7 @@ static void imsic_msi_update_msg(struct irq_data *d, struct imsic_vector *vec)
{
struct msi_msg msg = { };
- imsic_irq_compose_vector_msg(vec, &msg);
+ imsic_irq_compose_vector_msg(d, vec, &msg);
irq_data_get_irq_chip(d)->irq_write_msi_msg(d, &msg);
}
@@ -225,7 +233,9 @@ static struct irq_chip imsic_irq_base_chip = {
static int imsic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
unsigned int nr_irqs, void *args)
{
+ msi_alloc_info_t *info = args;
struct imsic_vector *vec;
+ int ret;
/* Multi-MSI is not supported yet. */
if (nr_irqs > 1)
@@ -235,6 +245,15 @@ static int imsic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,
if (!vec)
return -ENOSPC;
+ if (imsic->smode_msi_pa) {
+ ret = iommu_dma_prepare_msi_list(info->desc, imsic->smode_msi_pa,
+ num_possible_cpus(), IMSIC_MMIO_PAGE_SZ);
+ if (ret) {
+ imsic_vector_free(vec);
+ return ret;
+ }
+ }
+
irq_domain_set_info(domain, virq, virq, &imsic_irq_base_chip, vec,
handle_edge_irq, NULL, NULL);
irq_set_noprobe(virq);
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 13/17] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (11 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 12/17] irqchip/riscv-imsic: Support IOMMU MSI address lists Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 14/17] vfio: enable IOMMU_TYPE1 for RISC-V Andrew Jones
` (4 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
From: Tomasz Jeznach <tomasz.jeznach@linux.dev>
Enable IOMMU_DMA for 64-bit RISC-V now that the RISC-V
IOMMU driver supports MSI remapping.
Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
drivers/iommu/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 6e07bd69467a..1c9f2ad10f36 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -151,7 +151,7 @@ config OF_IOMMU
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
- def_bool ARM64 || X86 || S390
+ def_bool ARM64 || X86 || S390 || (RISCV && 64BIT)
select DMA_OPS_HELPERS
select IOMMU_API
select IOMMU_IOVA
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 14/17] vfio: enable IOMMU_TYPE1 for RISC-V
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (12 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 13/17] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 15/17] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch Andrew Jones
` (3 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu,
Nutty Liu
From: Tomasz Jeznach <tjeznach@rivosinc.com>
Enable VFIO support on RISC-V architecture, now that the RISC-V IOMMU
driver reports the IOMMU_CAP_CACHE_COHERENCY capability VFIO_TYPE1 and
iommufd both require before allowing a device to be bound.
Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
---
drivers/vfio/Kconfig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/vfio/Kconfig b/drivers/vfio/Kconfig
index ceae52fd7586..ad62205b4e45 100644
--- a/drivers/vfio/Kconfig
+++ b/drivers/vfio/Kconfig
@@ -39,7 +39,7 @@ config VFIO_GROUP
config VFIO_CONTAINER
bool "Support for the VFIO container /dev/vfio/vfio"
- select VFIO_IOMMU_TYPE1 if MMU && (X86 || S390 || ARM || ARM64)
+ select VFIO_IOMMU_TYPE1 if MMU && (X86 || S390 || ARM || ARM64 || RISCV)
depends on VFIO_GROUP
default y
help
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 15/17] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (13 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 14/17] vfio: enable IOMMU_TYPE1 for RISC-V Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 16/17] riscv: defconfig: Enable IOMMUFD and VFIO Andrew Jones
` (2 subsequent siblings)
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu,
Nutty Liu
From: Tomasz Jeznach <tjeznach@rivosinc.com>
Enable KVM/VFIO support on RISC-V architecture, now that VFIO device
assignment is available on RISC-V through VFIO_IOMMU_TYPE1, so a
RISC-V KVM guest can be notified about VFIO-assigned devices.
Signed-off-by: Tomasz Jeznach <tjeznach@rivosinc.com>
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
---
arch/riscv/kvm/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/riscv/kvm/Kconfig b/arch/riscv/kvm/Kconfig
index ec2cee0a39e0..49179aae9504 100644
--- a/arch/riscv/kvm/Kconfig
+++ b/arch/riscv/kvm/Kconfig
@@ -29,6 +29,7 @@ config KVM
select KVM_GENERIC_DIRTYLOG_READ_PROTECT
select KVM_GENERIC_HARDWARE_ENABLING
select KVM_MMIO
+ select KVM_VFIO
select VIRT_XFER_TO_GUEST_WORK
select SCHED_INFO
select GUEST_PERF_EVENTS if PERF_EVENTS
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 16/17] riscv: defconfig: Enable IOMMUFD and VFIO
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (14 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 15/17] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 17/17] selftests/vfio: Allow building on RISC-V Andrew Jones
2026-09-08 13:08 ` [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO fangyu.yu
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
Enable iommufd and VFIO in the default configuration now that the
RISC-V IOMMU provides the DMA and MSI remapping needed for userspace
device access, including assignment to virtual machines.
Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
---
arch/riscv/configs/defconfig | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/riscv/configs/defconfig b/arch/riscv/configs/defconfig
index ed605b5e3162..bae9a0f32f89 100644
--- a/arch/riscv/configs/defconfig
+++ b/arch/riscv/configs/defconfig
@@ -249,6 +249,10 @@ CONFIG_DMADEVICES=y
CONFIG_DMA_SUN6I=m
CONFIG_DW_AXI_DMAC=y
CONFIG_MMP_PDMA=m
+CONFIG_IOMMUFD=m
+CONFIG_VFIO_DEVICE_CDEV=y
+CONFIG_VFIO=m
+CONFIG_VFIO_PCI=m
CONFIG_VIRTIO_PCI=y
CONFIG_VIRTIO_BALLOON=y
CONFIG_VIRTIO_INPUT=y
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH v5 17/17] selftests/vfio: Allow building on RISC-V
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (15 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 16/17] riscv: defconfig: Enable IOMMUFD and VFIO Andrew Jones
@ 2026-08-31 14:59 ` Andrew Jones
2026-09-08 13:08 ` [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO fangyu.yu
17 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-08-31 14:59 UTC (permalink / raw)
To: linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
The generic VFIO selftests, including iommufd coverage, require no
RISC-V-specific source changes. Top-level kselftest builds normalize
riscv64 to riscv, while direct builds may use riscv64. Include both
names in the architecture filter.
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..2c7d8ea81b61 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 riscv64))
# Do nothing on unsupported architectures
include ../lib.mk
else
--
2.43.0
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH v5 02/17] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap
2026-08-31 14:59 ` [PATCH v5 02/17] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
@ 2026-09-01 7:52 ` Nutty.Liu
0 siblings, 0 replies; 21+ messages in thread
From: Nutty.Liu @ 2026-09-01 7:52 UTC (permalink / raw)
To: Andrew Jones, linux-riscv, iommu
Cc: linux-kernel, tomasz.jeznach, tjeznach, jgg, jgg, joro, will,
robin.murphy, pjw, palmer, anup, tglx, kevin.tian, fangyu.yu
On 8/31/2026 10:59 PM, Andrew Jones wrote:
> struct iommufd_sw_msi_maps uses a fixed 64-bit bitmap, limiting each
> group and hardware page table to 64 software MSI mappings. RISC-V
> interrupt remapping needs a mapping for every possible CPU, so this
> limit is insufficient.
>
> Make the bitmap grow on demand and treat IDs beyond its current size
> as absent. Cap it at 16K entries to bound allocation size while leaving
> ample room for expected software MSI users.
>
> Signed-off-by: Andrew Jones <andrew.jones@oss.qualcomm.com>
Reviewed-by: Nutty Liu <nutty.liu@hotmail.com>
Thanks,
Nutty
> ---
> drivers/iommu/iommufd/device.c | 3 +-
> drivers/iommu/iommufd/driver.c | 37 ++++++++++++++--------
> drivers/iommu/iommufd/hw_pagetable.c | 1 +
> drivers/iommu/iommufd/iommufd_private.h | 41 +++++++++++++++++++++++--
> 4 files changed, 66 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/iommu/iommufd/device.c b/drivers/iommu/iommufd/device.c
> index d488c23fd353..868f1e591208 100644
> --- a/drivers/iommu/iommufd/device.c
> +++ b/drivers/iommu/iommufd/device.c
> @@ -34,6 +34,7 @@ static void iommufd_group_release(struct kref *kref)
> NULL, GFP_KERNEL);
> iommu_group_put(igroup->group);
> mutex_destroy(&igroup->lock);
> + kfree(igroup->required_sw_msi.bitmap);
> kfree(igroup);
> }
>
> @@ -384,7 +385,7 @@ static int iommufd_group_setup_msi(struct iommufd_group *igroup,
> int rc;
>
> if (cur->sw_msi_start != igroup->sw_msi_start ||
> - !test_bit(cur->id, igroup->required_sw_msi.bitmap))
> + !iommufd_sw_msi_maps_test_bit(&igroup->required_sw_msi, cur->id))
> continue;
>
> rc = iommufd_sw_msi_install(ictx, hwpt_paging, cur);
> diff --git a/drivers/iommu/iommufd/driver.c b/drivers/iommu/iommufd/driver.c
> index 3b8067976eac..d0b05b1fda42 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,25 @@ 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 +296,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,
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 11/17] irqchip/riscv-imsic: Add S-mode MSI address list
2026-08-31 14:59 ` [PATCH v5 11/17] irqchip/riscv-imsic: Add S-mode MSI address list Andrew Jones
@ 2026-09-01 13:38 ` Andrew Jones
0 siblings, 0 replies; 21+ messages in thread
From: Andrew Jones @ 2026-09-01 13:38 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
A Sashiko review raised the following questions:
> + if (!local->msi_va) {
> + kfree(smode_msi_pa);
> + return -ENODEV;
>
> Does this strict requirement introduce a regression on systems with partial
> IMSIC coverage?
>
> By returning -ENODEV here if any possible CPU lacks an IMSIC mapping, the
> error bubbles up to imsic_setup_state() and fatally tears down the entire
> driver. Previously, imsic_setup_state() allowed partial coverage as long as
> at least one CPU handler was found.
Partial IMSIC coverage is not functionally supported. The IPI and MSI
paths can select any online CPU and unconditionally use that CPU's
local->msi_va or local->msi_pa, so every possible CPU must have an
initialized IMSIC page. Returning -ENODEV during setup therefore
validates this invariant earlier.
>
> + smode_msi_pa[cpu] = local->msi_pa;
>
> Could this assignment cause an out-of-bounds write on systems where the CPU
> mask is sparse?
>
> The smode_msi_pa array is allocated using num_possible_cpus() (the count of
> possible CPUs) but is indexed here by the logical CPU ID. If the CPU mask has
> gaps (for example, if logical CPUs 0 and 2 are present), num_possible_cpus()
> would evaluate to 2, but this loop would attempt to write to index 2,
> overflowing the bounds of the array. Would allocating based on nr_cpu_ids
> prevent this issue?
For RISC-V, num_possible_cpus() == nr_cpu_ids because RISC-V assigns
logical CPU IDs sequentially during both DT and ACPI discovery and
populates the possible mask from those contiguous mappings.
Thanks,
drew
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
` (16 preceding siblings ...)
2026-08-31 14:59 ` [PATCH v5 17/17] selftests/vfio: Allow building on RISC-V Andrew Jones
@ 2026-09-08 13:08 ` fangyu.yu
17 siblings, 0 replies; 21+ messages in thread
From: fangyu.yu @ 2026-09-08 13:08 UTC (permalink / raw)
To: andrew.jones
Cc: anup, fangyu.yu, iommu, jgg, jgg, joro, kevin.tian, linux-kernel,
linux-riscv, palmer, pjw, robin.murphy, tglx, tjeznach,
tomasz.jeznach, will
>This series adds MSI remapping for IMSIC so a device's MSI target gets
>translated the same way its DMA does, allowing RISC-V to enable IOMMU_DMA
>and paging domains by default.
>
>v1[1] used get_resv_regions() with IOMMU_RESV_DIRECT_RELAXABLE to identity
>map IMSIC pages, but that was rejected as only a workaround. v2 through
>v4[2] instead introduced a RISC-V IOMMU IRQ domain which pre-mapped every
>possible IMSIC target and maintained a domain-local PA-to-IOVA table. The
>v4 discussion with Jason identified a simpler way to handle the RISC-V
>requirement that the MSI target address changes with interrupt affinity:
>extend the existing iommu_dma_prepare_msi() model to prepare an ordered
>list of MSI targets as one contiguous IOVA range. v5 is a complete redesign
>around that approach.
>
>The IMSIC driver now builds an array containing the supervisor IMSIC page
>for every possible CPU, indexed by logical CPU number. When allocating an
>IRQ, it passes the complete array to iommu_dma_prepare_msi_list(). The new
>API maps the ordered physical address list into one contiguous IOVA range
>through either DMA-IOMMU or iommufd, then caches the base IOVA and mapping
>granule in the MSI descriptor. MSI composition can therefore select the
>target for the current CPU with simple arithmetic, including during an
>affinity change, without allocating memory or consulting IOMMU-owned state
>in atomic context.
>
>DMA-IOMMU extends its existing per-page MSI cache to recognize and reuse
>complete ranges. iommufd grows its software-MSI bitmap on demand, bounds
>allocations to the reserved MSI window, and prepares, installs, rolls back,
>and replays a range as one unit. This keeps the descriptor's contiguous
>IOVA valid across iommufd paging-domain replacement without exposing a
>partially installed range.
>
>Unlike v4, v5 has no RISC-V IOMMU IRQ domain, no domain-local IMSIC mapping
>table, and no IOMMU lookup during MSI composition. The IMSIC IRQ domain
>owns the target list and message composition, while the IOMMU layers only
>provide the mappings. Devices which do not need IOMMU MSI translation keep
>using physical MSI addresses through the same IMSIC path.
>
>The series also carries the remaining plumbing needed for RISC-V PCIe
>device assignment through VFIO/KVM: the RISC-V IOMMU reports DMA
>cache-coherency capability for coherent devices, VFIO type1 and KVM_VFIO
>are enabled for RISC-V, defconfig enables IOMMUFD/VFIO as modules with cdev
>support, and the generic VFIO/iommufd selftests can be built for RISC-V.
>The RISC-V IOMMU specification does not provide MSI data validation, so
>VFIO device assignment requires the applicable allow_unsafe_interrupts=1
>module parameter. Direct MSI routing to guest interrupt files (irqbypass)
>is not yet supported by this series and will be posted separately on top.
>
Hi Andrew:
I tested this on riscv64 QEMU with an emulated NVMe device and e1000e
network adapter. The fio and iperf3 tests both completed successfully,
and the interrupt status was as expected.
For this series
Tested-by: Fangyu Yu <fangyu.yu@linux.alibaba.com>
>LLM-based coding assistants were used during development for code
>exploration, patch review, test execution, and drafting and editing
>commit messages and this cover letter. I reviewed and finalized all
>resulting code and text. Per-patch Assisted-by tags are omitted in
>light of ongoing discussions about simplifying coding-assistant
>attribution.
>
>Thanks,
>drew
>
>[1] https://lore.kernel.org/all/20260508212339.381933-1-andrew.jones@oss.qualcomm.com/
>[2] https://lore.kernel.org/all/20260820214150.545737-1-andrew.jones@oss.qualcomm.com/
>
_______________________________________________
linux-riscv mailing list
linux-riscv@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-riscv
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2026-09-08 13:08 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 14:59 [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO Andrew Jones
2026-08-31 14:59 ` [PATCH v5 01/17] iommu/dma: Prepare MSI physical address lists Andrew Jones
2026-08-31 14:59 ` [PATCH v5 02/17] iommufd: Convert struct iommufd_sw_msi_maps to a growable bitmap Andrew Jones
2026-09-01 7:52 ` Nutty.Liu
2026-08-31 14:59 ` [PATCH v5 03/17] iommufd: Split software MSI map lookup and allocation Andrew Jones
2026-08-31 14:59 ` [PATCH v5 04/17] iommufd: Bound software MSI mappings to the reserved range Andrew Jones
2026-08-31 14:59 ` [PATCH v5 05/17] iommufd: Prepare software MSI maps for address lists Andrew Jones
2026-08-31 14:59 ` [PATCH v5 06/17] iommufd: Install software MSI map ranges atomically Andrew Jones
2026-08-31 14:59 ` [PATCH v5 07/17] iommufd: Prepare software MSI installation for address lists Andrew Jones
2026-08-31 14:59 ` [PATCH v5 08/17] iommu/dma: Introduce iommu_dma_prepare_msi_list() Andrew Jones
2026-08-31 14:59 ` [PATCH v5 09/17] iommu/riscv: Report cache coherency capability Andrew Jones
2026-08-31 14:59 ` [PATCH v5 10/17] iommu/riscv: Reserve an MSI IOVA window for iommufd Andrew Jones
2026-08-31 14:59 ` [PATCH v5 11/17] irqchip/riscv-imsic: Add S-mode MSI address list Andrew Jones
2026-09-01 13:38 ` Andrew Jones
2026-08-31 14:59 ` [PATCH v5 12/17] irqchip/riscv-imsic: Support IOMMU MSI address lists Andrew Jones
2026-08-31 14:59 ` [PATCH v5 13/17] iommu/dma: Enable IOMMU_DMA for 64-bit RISC-V Andrew Jones
2026-08-31 14:59 ` [PATCH v5 14/17] vfio: enable IOMMU_TYPE1 for RISC-V Andrew Jones
2026-08-31 14:59 ` [PATCH v5 15/17] RISC-V: KVM: Enable KVM_VFIO interfaces on RISC-V arch Andrew Jones
2026-08-31 14:59 ` [PATCH v5 16/17] riscv: defconfig: Enable IOMMUFD and VFIO Andrew Jones
2026-08-31 14:59 ` [PATCH v5 17/17] selftests/vfio: Allow building on RISC-V Andrew Jones
2026-09-08 13:08 ` [PATCH v5 00/17] iommu/riscv: Enable MSI remapping, IOMMU_DMA and VFIO fangyu.yu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox