From: Guanghui Feng <guanghuifeng@linux.alibaba.com>
To: robin.murphy@arm.com, joro@8bytes.org, will@kernel.org
Cc: iommu@lists.linux.dev, linux-kernel@vger.kernel.org,
alex@shazbot.org, kvm@vger.kernel.org
Subject: [PATCH] iommu: Reserve PCI host bridge MMIO windows in group reserved regions
Date: Mon, 21 Sep 2026 15:02:34 +0800 [thread overview]
Message-ID: <20260921070234.897736-1-guanghuifeng@linux.alibaba.com> (raw)
The DMA IOVA layer reserves PCI host bridge MMIO windows via
iova_reserve_pci_windows() to prevent IOVA allocations from
overlapping with those address ranges. As commit fade1ec055dc
("iommu/dma: Avoid PCI host bridge windows") explains, a host
bridge may interpret addresses falling within its MMIO windows as
peer-to-peer DMA, leading to faults, data corruption, or DMA
transactions being misrouted to the wrong PCIe device.
However, VFIO type1 obtains reserved regions through
iommu_get_group_resv_regions() and reports the available IOVA
ranges to userspace via VFIO_IOMMU_GET_INFO. Previously
iommu_get_group_resv_regions() only returned IOMMU driver-level
reserved regions (RMRR, Unity Maps, MSI regions, etc.) and did
not include PCI host bridge MMIO windows. This allowed userspace
to choose IOVA addresses that overlap with bridge windows.
The problem is especially dangerous when a device sits behind a
PCIe switch and ACS Upstream Forwarding is not fully enabled: the
switch may route DMA TLPs as peer-to-peer traffic to other
downstream devices instead of forwarding them upstream to the
root complex for IOMMU translation.
Fix this by:
- Introducing iommu_get_pci_resv_windows(), a common helper
that walks a PCI host bridge's MMIO windows and creates
IOMMU_RESV_RESERVED region entries for each one.
- Calling iommu_resv_pci_windows() from
iommu_get_group_resv_regions() so that all group-level
consumers (VFIO type1, sysfs reserved_regions) automatically
receive the PCI window reservations.
- Refactoring iova_reserve_pci_windows() to reuse
iommu_get_pci_resv_windows(), eliminating the duplicated
bridge window enumeration logic.
Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
---
drivers/iommu/dma-iommu.c | 21 +++++++----
drivers/iommu/iommu-priv.h | 11 ++++++
drivers/iommu/iommu.c | 73 ++++++++++++++++++++++++++++++++++++++
3 files changed, 99 insertions(+), 6 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 58c624513cd4..bccb53043462 100644
--- a/drivers/iommu/dma-iommu.c
+++ b/drivers/iommu/dma-iommu.c
@@ -37,6 +37,7 @@
#include "dma-iommu.h"
#include "iommu-pages.h"
+#include "iommu-priv.h"
struct iommu_dma_msi_page {
struct list_head list;
@@ -508,18 +509,26 @@ static int iova_reserve_pci_windows(struct pci_dev *dev,
struct iova_domain *iovad)
{
struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
+ struct iommu_resv_region *region, *next;
struct resource_entry *window;
unsigned long lo, hi;
phys_addr_t start = 0, end;
+ LIST_HEAD(pci_windows);
+ int ret;
- resource_list_for_each_entry(window, &bridge->windows) {
- if (resource_type(window->res) != IORESOURCE_MEM)
- continue;
+ ret = iommu_get_pci_resv_windows(dev, &pci_windows);
- lo = iova_pfn(iovad, window->res->start - window->offset);
- hi = iova_pfn(iovad, window->res->end - window->offset);
- reserve_iova(iovad, lo, hi);
+ list_for_each_entry_safe(region, next, &pci_windows, list) {
+ if (!ret) {
+ lo = iova_pfn(iovad, region->start);
+ hi = iova_pfn(iovad, region->start + region->length - 1);
+ reserve_iova(iovad, lo, hi);
+ }
+ list_del(®ion->list);
+ kfree(region);
}
+ 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 aaffad5854fc..bc7fab0c827e 100644
--- a/drivers/iommu/iommu-priv.h
+++ b/drivers/iommu/iommu-priv.h
@@ -123,4 +123,15 @@ static inline void iommu_debug_init(void)
#endif /* CONFIG_IOMMU_DEBUG_PAGEALLOC */
+#ifdef CONFIG_PCI
+struct pci_dev;
+int iommu_get_pci_resv_windows(struct pci_dev *dev, struct list_head *head);
+#else
+static inline int iommu_get_pci_resv_windows(struct pci_dev *dev,
+ struct list_head *head)
+{
+ return 0;
+}
+#endif /* CONFIG_PCI */
+
#endif /* __LINUX_IOMMU_PRIV_H */
diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
index cd1bca7ede9a..de675ed27753 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -945,6 +945,72 @@ iommu_insert_device_resv_regions(struct list_head *dev_resv_regions,
return ret;
}
+#ifdef CONFIG_PCI
+/**
+ * iommu_get_pci_resv_windows - collect PCI host bridge MMIO windows as
+ * reserved regions
+ * @dev: PCI device whose host bridge to scan
+ * @head: list head to append iommu_resv_region entries to
+ *
+ * Walks the MMIO windows of @dev's PCI host bridge and creates an
+ * IOMMU_RESV_RESERVED region for each one. The caller must free the
+ * returned entries with kfree() when done.
+ *
+ * Returns 0 on success, negative errno on failure.
+ */
+int iommu_get_pci_resv_windows(struct pci_dev *dev, struct list_head *head)
+{
+ struct pci_host_bridge *bridge = pci_find_host_bridge(dev->bus);
+ struct resource_entry *window;
+
+ resource_list_for_each_entry(window, &bridge->windows) {
+ struct iommu_resv_region *region;
+ phys_addr_t start;
+ size_t length;
+
+ if (resource_type(window->res) != IORESOURCE_MEM)
+ continue;
+
+ start = window->res->start - window->offset;
+ length = window->res->end - window->res->start + 1;
+
+ region = iommu_alloc_resv_region(start, length, 0,
+ IOMMU_RESV_RESERVED,
+ GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+
+ list_add_tail(®ion->list, head);
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_get_pci_resv_windows);
+
+/*
+ * Reserve PCI host bridge MMIO windows as IOMMU_RESV_RESERVED regions.
+ * This prevents IOVA allocations from overlapping with PCI MMIO address
+ * ranges, which could cause PCIe switches to misroute DMA transactions.
+ *
+ * All PCI devices within the same IOMMU group share the same host bridge,
+ * so we only need to find the first PCI device.
+ *
+ * Caller must hold group->mutex.
+ */
+static int iommu_resv_pci_windows(struct iommu_group *group,
+ struct list_head *head)
+{
+ struct group_device *gdev;
+
+ for_each_group_device(group, gdev) {
+ if (!dev_is_pci(gdev->dev))
+ continue;
+ return iommu_get_pci_resv_windows(to_pci_dev(gdev->dev),
+ head);
+ }
+ return 0;
+}
+#endif /* CONFIG_PCI */
+
int iommu_get_group_resv_regions(struct iommu_group *group,
struct list_head *head)
{
@@ -969,6 +1035,13 @@ int iommu_get_group_resv_regions(struct iommu_group *group,
if (ret)
break;
}
+
+ /* Reserve PCI host bridge MMIO windows to prevent IOVA conflicts */
+#ifdef CONFIG_PCI
+ if (!ret)
+ ret = iommu_resv_pci_windows(group, head);
+#endif
+
mutex_unlock(&group->mutex);
return ret;
}
--
2.43.7
next reply other threads:[~2026-09-21 7:02 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-21 7:02 Guanghui Feng [this message]
2026-09-21 10:39 ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Guanghui Feng
2026-09-21 10:39 ` [PATCH v2 1/2] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
2026-09-21 10:39 ` [PATCH v2 2/2] iommufd: Reserve PCI host bridge MMIO windows in IOAS " Guanghui Feng
2026-09-21 11:24 ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Robin Murphy
2026-09-21 11:55 ` Jason Gunthorpe
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=20260921070234.897736-1-guanghuifeng@linux.alibaba.com \
--to=guanghuifeng@linux.alibaba.com \
--cc=alex@shazbot.org \
--cc=iommu@lists.linux.dev \
--cc=joro@8bytes.org \
--cc=kvm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=robin.murphy@arm.com \
--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®