[PATCH v2 03/14] iommu/dma: Add iommu_dma_sw_map_msi()

From: Andrew Jones

Date: Fri Jul 24 2026 - 11:40:22 EST


Add a descriptor-free counterpart to iommu_dma_sw_msi(). The existing
function is tied to a struct msi_desc and stores the result in the
descriptor. This variant returns the IOVA directly so callers can
pre-map MSI targets before any descriptor has been allocated.

Callers may pass a required mapping size so MSI doorbells that must
not share a larger IOMMU leaf, such as RISC-V IMSIC files, can fail
before a mapping is installed.

Signed-off-by: Andrew Jones <andrew.jones@xxxxxxxxxxxxxxxx>
---
drivers/iommu/dma-iommu.c | 39 +++++++++++++++++++++++++++++++--------
drivers/iommu/dma-iommu.h | 12 ++++++++++++
2 files changed, 43 insertions(+), 8 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 9abaec0703ef..c26b071d744a 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -2234,24 +2234,47 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
return NULL;
}

-int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
- phys_addr_t msi_addr)
+int iommu_dma_sw_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift)
{
- struct device *dev = msi_desc_to_dev(desc);
const struct iommu_dma_msi_page *msi_page;
+ size_t size;

- if (!has_msi_cookie(domain)) {
- msi_desc_set_iommu_msi_iova(desc, 0, 0);
+ *msi_iova = 0;
+ *msi_shift = 0;
+
+ if (!has_msi_cookie(domain))
return 0;
- }
+
+ size = cookie_msi_granule(domain);
+ if (required_size && size != required_size)
+ return -EOPNOTSUPP;

iommu_group_mutex_assert(dev);
msi_page = iommu_dma_get_msi_page(dev, msi_addr, domain);
if (!msi_page)
return -ENOMEM;

- msi_desc_set_iommu_msi_iova(desc, msi_page->iova,
- ilog2(cookie_msi_granule(domain)));
+ *msi_iova = msi_page->iova;
+ *msi_shift = ilog2(size);
+ return 0;
+}
+
+int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
+ phys_addr_t msi_addr)
+{
+ struct device *dev = msi_desc_to_dev(desc);
+ dma_addr_t msi_iova;
+ unsigned int msi_shift;
+ int ret;
+
+ ret = iommu_dma_sw_map_msi(domain, dev, msi_addr, 0, &msi_iova, &msi_shift);
+ if (ret)
+ return ret;
+
+ msi_desc_set_iommu_msi_iova(desc, msi_iova, msi_shift);
return 0;
}

diff --git a/drivers/iommu/dma-iommu.h b/drivers/iommu/dma-iommu.h
index 040d00252563..3e1adeafdcac 100644
--- a/drivers/iommu/dma-iommu.h
+++ b/drivers/iommu/dma-iommu.h
@@ -19,6 +19,10 @@ int iommu_dma_init_fq(struct iommu_domain *domain);

void iommu_dma_get_resv_regions(struct device *dev, struct list_head *list);

+int iommu_dma_sw_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift);
int iommu_dma_sw_msi(struct iommu_domain *domain, struct msi_desc *desc,
phys_addr_t msi_addr);

@@ -53,6 +57,14 @@ static inline void iommu_dma_get_resv_regions(struct device *dev, struct list_he
{
}

+static inline int iommu_dma_sw_map_msi(struct iommu_domain *domain,
+ struct device *dev, phys_addr_t msi_addr,
+ size_t required_size, dma_addr_t *msi_iova,
+ unsigned int *msi_shift)
+{
+ return -ENODEV;
+}
+
static inline int iommu_dma_sw_msi(struct iommu_domain *domain,
struct msi_desc *desc, phys_addr_t msi_addr)
{
--
2.43.0