From: Andrew Jones <andrew.jones@oss.qualcomm.com>
To: linux-riscv@lists.infradead.org, iommu@lists.linux.dev
Cc: linux-kernel@vger.kernel.org, tomasz.jeznach@linux.dev,
tjeznach@rivosinc.com, jgg@ziepe.ca, jgg@nvidia.com,
joro@8bytes.org, will@kernel.org, robin.murphy@arm.com,
pjw@kernel.org, palmer@dabbelt.com, anup@brainfault.org,
tglx@kernel.org, kevin.tian@intel.com,
fangyu.yu@linux.alibaba.com
Subject: [PATCH v5 01/17] iommu/dma: Prepare MSI physical address lists
Date: Mon, 31 Aug 2026 16:59:27 +0200 [thread overview]
Message-ID: <20260831145943.313726-2-andrew.jones@oss.qualcomm.com> (raw)
In-Reply-To: <20260831145943.313726-1-andrew.jones@oss.qualcomm.com>
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
next prev parent reply other threads:[~2026-08-31 14:59 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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-09-23 11:55 ` Anup Patel
2026-08-31 14:59 ` [PATCH v5 12/17] irqchip/riscv-imsic: Support IOMMU MSI address lists Andrew Jones
2026-09-23 12:01 ` Anup Patel
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260831145943.313726-2-andrew.jones@oss.qualcomm.com \
--to=andrew.jones@oss.qualcomm.com \
--cc=anup@brainfault.org \
--cc=fangyu.yu@linux.alibaba.com \
--cc=iommu@lists.linux.dev \
--cc=jgg@nvidia.com \
--cc=jgg@ziepe.ca \
--cc=joro@8bytes.org \
--cc=kevin.tian@intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-riscv@lists.infradead.org \
--cc=palmer@dabbelt.com \
--cc=pjw@kernel.org \
--cc=robin.murphy@arm.com \
--cc=tglx@kernel.org \
--cc=tjeznach@rivosinc.com \
--cc=tomasz.jeznach@linux.dev \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®