[PATCH v6 1/3] iommu: Add iommu_for_each_pci_mmio_window() iterator

From: Guanghui Feng

Date: Wed Oct 28 2026 - 05:05:26 EST


Add a callback-based iterator that walks a PCI device's host bridge
MMIO windows and invokes a caller-supplied function with each window's
bus address range. This consolidates the bridge window enumeration
logic into a single shared algorithm in the IOMMU core.

Refactor iova_reserve_pci_windows() to use the new iterator, replacing
its open-coded resource_list_for_each_entry loop. The callback
receives phys_addr_t start/end directly, so the DMA path reserves the
same ranges as before with zero intermediate allocation, and window
lengths never pass through a size_t field (which would truncate on
32-bit PAE systems).

Signed-off-by: Guanghui Feng <guanghuifeng@xxxxxxxxxxxxxxxxx>
---
drivers/iommu/dma-iommu.c | 22 ++++++++++++++--------
drivers/iommu/iommu-priv.h | 17 +++++++++++++++++
drivers/iommu/iommu.c | 37 +++++++++++++++++++++++++++++++++++++
3 files changed, 68 insertions(+), 8 deletions(-)

diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 78742aeba932..5b73e71c312e 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -505,6 +505,15 @@ static int iommu_dma_ranges_sort(void *priv, const struct list_head *a,
return res_a->res->start > res_b->res->start;
}

+static int iova_reserve_pci_window_fn(phys_addr_t start, phys_addr_t end,
+ void *data)
+{
+ struct iova_domain *iovad = data;
+
+ reserve_iova(iovad, iova_pfn(iovad, start), iova_pfn(iovad, end));
+ return 0;
+}
+
static int iova_reserve_pci_windows(struct pci_dev *dev,
struct iova_domain *iovad)
{
@@ -512,15 +521,12 @@ static int iova_reserve_pci_windows(struct pci_dev *dev,
struct resource_entry *window;
unsigned long lo, hi;
phys_addr_t start = 0, end;
+ int ret;

- resource_list_for_each_entry(window, &bridge->windows) {
- if (resource_type(window->res) != IORESOURCE_MEM)
- continue;
-
- lo = iova_pfn(iovad, window->res->start - window->offset);
- hi = iova_pfn(iovad, window->res->end - window->offset);
- reserve_iova(iovad, lo, hi);
- }
+ ret = iommu_for_each_pci_mmio_window(dev, iova_reserve_pci_window_fn,
+ iovad);
+ if (ret)
+ return ret;

/* Get reserved DMA windows from host bridge */
list_sort(NULL, &bridge->dma_ranges, iommu_dma_ranges_sort);
diff --git a/drivers/iommu/iommu-priv.h b/drivers/iommu/iommu-priv.h
index 1f9f886b2c10..81c5b5ce67ec 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -127,4 +127,21 @@ static inline void iommu_debug_init(void)

#endif /* CONFIG_IOMMU_DEBUG_PAGEALLOC */

+struct pci_dev;
+
+typedef int (*iommu_pci_mmio_window_fn_t)(phys_addr_t start, phys_addr_t end,
+ void *data);
+
+#ifdef CONFIG_PCI
+int iommu_for_each_pci_mmio_window(struct pci_dev *dev,
+ iommu_pci_mmio_window_fn_t fn, void *data);
+#else
+static inline int iommu_for_each_pci_mmio_window(struct pci_dev *dev,
+ iommu_pci_mmio_window_fn_t fn,
+ void *data)
+{
+ return 0;
+}
+#endif /* CONFIG_PCI */
+
#endif /* __LINUX_IOMMU_PRIV_H */
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index b486b8bbd1fc..6b0e2b08ceba 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -945,6 +945,43 @@ iommu_insert_device_resv_regions(struct list_head *dev_resv_regions,
return ret;
}

+#ifdef CONFIG_PCI
+/**
+ * iommu_for_each_pci_mmio_window - iterate over PCI host bridge MMIO windows
+ * @dev: PCI device whose host bridge to scan
+ * @fn: callback invoked with each window's bus address range (inclusive)
+ * @data: opaque pointer passed to @fn
+ *
+ * Iteration stops early if @fn returns non-zero, and that value is
+ * propagated to the caller.
+ *
+ * Return: 0 on success, negative errno on failure (including from @fn).
+ */
+int iommu_for_each_pci_mmio_window(struct pci_dev *dev,
+ iommu_pci_mmio_window_fn_t fn, void *data)
+{
+ struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
+ struct resource_entry *window;
+
+ if (WARN_ON(!bridge))
+ return -ENODEV;
+
+ resource_list_for_each_entry(window, &bridge->windows) {
+ int ret;
+
+ if (resource_type(window->res) != IORESOURCE_MEM)
+ continue;
+
+ ret = fn(window->res->start - window->offset,
+ window->res->end - window->offset, data);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_for_each_pci_mmio_window);
+#endif /* CONFIG_PCI */
+
int iommu_get_group_resv_regions(struct iommu_group *group,
struct list_head *head)
{
--
2.43.7