* [PATCH] iommu: Reserve PCI host bridge MMIO windows in group reserved regions
@ 2026-09-21 7:02 Guanghui Feng
2026-09-21 10:39 ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Guanghui Feng
0 siblings, 1 reply; 6+ messages in thread
From: Guanghui Feng @ 2026-09-21 7:02 UTC (permalink / raw)
To: robin.murphy, joro, will; +Cc: iommu, linux-kernel, alex, kvm
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
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA
2026-09-21 7:02 [PATCH] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
@ 2026-09-21 10:39 ` Guanghui Feng
2026-09-21 10:39 ` [PATCH v2 1/2] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Guanghui Feng @ 2026-09-21 10:39 UTC (permalink / raw)
To: jgg, kevin.tian, alex, joro, robin.murphy, will; +Cc: iommu, kvm, linux-kernel
The DMA IOVA layer reserves PCI host bridge MMIO windows via
iova_reserve_pci_windows() to keep IOVA allocations from overlapping
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. This is especially dangerous when a device sits
behind a PCIe switch and ACS Upstream Forwarding is not fully enabled.
Passthrough paths that let userspace pick IOVAs did not honour these
windows: VFIO type1 and iommufd only excluded IOMMU driver-level
reserved regions (RMRR, unity maps, MSI), not PCI host bridge MMIO
windows.
This series closes that gap:
Patch 1 adds a common helper, iommu_get_pci_resv_windows(), and
reserves PCI host bridge MMIO windows in
iommu_get_group_resv_regions() so all group-level consumers (VFIO
type1, sysfs reserved_regions) avoid them; iova_reserve_pci_windows()
is refactored to reuse the helper.
Patch 2 switches iommufd's iopt_table_enforce_dev_resv_regions() to
iommu_get_group_resv_regions() so IOMMU_IOAS_IOVA_RANGES also avoids
PCI host bridge MMIO windows, aligning iommufd with VFIO type1.
v1: https://lore.kernel.org/all/20260921070234.897736-1-guanghuifeng@linux.alibaba.com/
Changes since v1:
- v1 was a single patch; v2 is a two-patch series.
- Patch 1: build the reservations with iommu_insert_resv_region()
instead of list_add_tail(), so the group reserved list stays sorted
by start address and overlapping same-type regions are merged. The
helper now frees its partial list itself on error and leaves the
caller's list untouched.
- Patch 2 (new): reserve PCI host bridge MMIO windows for the iommufd
IOAS as well, so IOMMU_IOAS_IOVA_RANGES no longer reports IOVAs that
overlap PCI MMIO windows.
Notes on earlier automated review feedback:
- The size_t length of each window matches struct iommu_resv_region's
existing field type; no new truncation is introduced on 64-bit.
- An IOMMU group cannot span multiple PCI host bridges:
pci_device_group() only groups devices within a single host bridge
hierarchy, so scanning the first PCI device of the group is enough.
Guanghui Feng (2):
iommu: Reserve PCI host bridge MMIO windows in group reserved regions
iommufd: Reserve PCI host bridge MMIO windows in IOAS reserved regions
drivers/iommu/dma-iommu.c | 17 ++++--
drivers/iommu/iommu-priv.h | 11 ++++
drivers/iommu/iommu.c | 85 ++++++++++++++++++++++++++++
drivers/iommu/iommufd/io_pagetable.c | 15 ++++-
4 files changed, 121 insertions(+), 7 deletions(-)
--
2.43.7
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 1/2] iommu: Reserve PCI host bridge MMIO windows in group reserved regions
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 ` 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
2 siblings, 0 replies; 6+ messages in thread
From: Guanghui Feng @ 2026-09-21 10:39 UTC (permalink / raw)
To: jgg, kevin.tian, alex, joro, robin.murphy, will; +Cc: iommu, kvm, linux-kernel
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 | 17 +++++---
drivers/iommu/iommu-priv.h | 11 +++++
drivers/iommu/iommu.c | 85 ++++++++++++++++++++++++++++++++++++++
3 files changed, 108 insertions(+), 5 deletions(-)
diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
index 58c624513cd4..c97622826d9d 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,17 +509,23 @@ 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);
+ if (ret)
+ return ret;
- lo = iova_pfn(iovad, window->res->start - window->offset);
- hi = iova_pfn(iovad, window->res->end - window->offset);
+ list_for_each_entry_safe(region, next, &pci_windows, list) {
+ 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);
}
/* Get reserved DMA windows from host bridge */
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..b77e9818e4a4 100644
--- a/drivers/iommu/iommu.c
+++ b/drivers/iommu/iommu.c
@@ -945,6 +945,84 @@ 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 inserts an
+ * IOMMU_RESV_RESERVED region for each one using iommu_insert_resv_region(),
+ * keeping the entries sorted by start address and merging overlapping
+ * regions of the same type. On success the inserted entries are appended
+ * to @head and the caller must free them with kfree() when done. On
+ * failure any entries built so far are freed internally and @head is left
+ * unmodified.
+ *
+ * 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 iommu_resv_region *region, *next;
+ struct resource_entry *window;
+ LIST_HEAD(resv_windows);
+ int ret;
+
+ resource_list_for_each_entry(window, &bridge->windows) {
+ struct iommu_resv_region tmp = {
+ .type = IOMMU_RESV_RESERVED,
+ };
+
+ if (resource_type(window->res) != IORESOURCE_MEM)
+ continue;
+
+ tmp.start = window->res->start - window->offset;
+ tmp.length = window->res->end - window->res->start + 1;
+
+ ret = iommu_insert_resv_region(&tmp, &resv_windows);
+ if (ret)
+ goto err_free;
+ }
+
+ list_splice_tail(&resv_windows, head);
+ return 0;
+
+err_free:
+ list_for_each_entry_safe(region, next, &resv_windows, list) {
+ list_del(®ion->list);
+ kfree(region);
+ }
+ return ret;
+}
+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 +1047,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
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 2/2] iommufd: Reserve PCI host bridge MMIO windows in IOAS reserved regions
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 ` Guanghui Feng
2026-09-21 11:24 ` [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA Robin Murphy
2 siblings, 0 replies; 6+ messages in thread
From: Guanghui Feng @ 2026-09-21 10:39 UTC (permalink / raw)
To: jgg, kevin.tian, alex, joro, robin.murphy, will; +Cc: iommu, kvm, linux-kernel
iommufd builds the reserved IOVA ranges of an IOAS in
iopt_table_enforce_dev_resv_regions() by calling the per-device
iommu_get_resv_regions(), which only returns IOMMU driver-level
reserved regions (RMRR, unity maps, MSI windows, etc.) and does
not include PCI host bridge MMIO windows. As a result, the ranges
reported to userspace by IOMMU_IOAS_IOVA_RANGES may overlap with a
host bridge's MMIO windows, allowing userspace to map IOVAs that a
PCIe switch might misinterpret as peer-to-peer DMA and misroute to
the wrong device.
VFIO type1 avoids this because it collects reserved regions through
iommu_get_group_resv_regions(), which already reserves PCI host
bridge MMIO windows.
Switch iommufd to iommu_get_group_resv_regions() as well so that
the IOAS reserved set includes PCI host bridge MMIO windows and
IOMMU_IOAS_IOVA_RANGES no longer hands out IOVAs overlapping them,
aligning iommufd behaviour with VFIO type1. The per-device @dev is
kept as the reservation owner so that per-device detach cleanup via
__iopt_remove_reserved_iova() continues to work unchanged.
Signed-off-by: Guanghui Feng <guanghuifeng@linux.alibaba.com>
---
drivers/iommu/iommufd/io_pagetable.c | 15 +++++++++++++--
1 file changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/iommu/iommufd/io_pagetable.c b/drivers/iommu/iommufd/io_pagetable.c
index 4e447ce74cf6..7198deba378e 100644
--- a/drivers/iommu/iommufd/io_pagetable.c
+++ b/drivers/iommu/iommufd/io_pagetable.c
@@ -1582,6 +1582,7 @@ int iopt_table_enforce_dev_resv_regions(struct io_pagetable *iopt,
phys_addr_t *sw_msi_start)
{
struct iommu_resv_region *resv;
+ struct iommu_group *group;
LIST_HEAD(resv_regions);
unsigned int num_hw_msi = 0;
unsigned int num_sw_msi = 0;
@@ -1591,8 +1592,16 @@ int iopt_table_enforce_dev_resv_regions(struct io_pagetable *iopt,
return -EINVAL;
down_write(&iopt->iova_rwsem);
- /* FIXME: drivers allocate memory but there is no failure propagated */
- iommu_get_resv_regions(dev, &resv_regions);
+
+ group = iommu_group_get(dev);
+ if (!group) {
+ rc = -ENODEV;
+ goto out_unlock;
+ }
+
+ rc = iommu_get_group_resv_regions(group, &resv_regions);
+ if (rc)
+ goto out_free_resv;
list_for_each_entry(resv, &resv_regions, list) {
if (resv->type == IOMMU_RESV_DIRECT_RELAXABLE)
@@ -1624,6 +1633,8 @@ int iopt_table_enforce_dev_resv_regions(struct io_pagetable *iopt,
__iopt_remove_reserved_iova(iopt, dev);
out_free_resv:
iommu_put_resv_regions(dev, &resv_regions);
+ iommu_group_put(group);
+out_unlock:
up_write(&iopt->iova_rwsem);
return rc;
}
--
2.43.7
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA
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 ` Robin Murphy
2026-09-21 11:55 ` Jason Gunthorpe
2 siblings, 1 reply; 6+ messages in thread
From: Robin Murphy @ 2026-09-21 11:24 UTC (permalink / raw)
To: Guanghui Feng, jgg, kevin.tian, alex, joro, will; +Cc: iommu, kvm, linux-kernel
On 21/09/2026 11:39 am, Guanghui Feng wrote:
> The DMA IOVA layer reserves PCI host bridge MMIO windows via
> iova_reserve_pci_windows() to keep IOVA allocations from overlapping
> 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. This is especially dangerous when a device sits
> behind a PCIe switch and ACS Upstream Forwarding is not fully enabled.
>
> Passthrough paths that let userspace pick IOVAs did not honour these
> windows: VFIO type1 and iommufd only excluded IOMMU driver-level
> reserved regions (RMRR, unity maps, MSI), not PCI host bridge MMIO
> windows.
This was intentional - I forget where the exact discussion happened, but
at the time we decided that exposing reserved regions for entire windows
was the wrong thing to do, as being pessimistically inaccurate might be
OK for the internal DMA API, but restricting userspace is another
matter, and I think the consensus was that it wasn't really the IOMMU
layer's job to get deep into PCI details anyway. Such "accidental" P2P
can only happen within a group that's assigned to VFIO as a whole, so
IIRC the conclusion was that users should already have enough
information at the VFIO level to avoid BAR addresses if they need to.
In fact we did briefly do this once before, and it was explicitly undone
again by cd2c9fcf5c66 ("iommu/dma: Move PCI window region reservation
back into dma specific path."). I think there may also have been a
practical issue with it breaking some standard Qemu setups.
Thanks,
Robin.
>
> This series closes that gap:
>
> Patch 1 adds a common helper, iommu_get_pci_resv_windows(), and
> reserves PCI host bridge MMIO windows in
> iommu_get_group_resv_regions() so all group-level consumers (VFIO
> type1, sysfs reserved_regions) avoid them; iova_reserve_pci_windows()
> is refactored to reuse the helper.
>
> Patch 2 switches iommufd's iopt_table_enforce_dev_resv_regions() to
> iommu_get_group_resv_regions() so IOMMU_IOAS_IOVA_RANGES also avoids
> PCI host bridge MMIO windows, aligning iommufd with VFIO type1.
>
> v1: https://lore.kernel.org/all/20260921070234.897736-1-guanghuifeng@linux.alibaba.com/
>
> Changes since v1:
> - v1 was a single patch; v2 is a two-patch series.
> - Patch 1: build the reservations with iommu_insert_resv_region()
> instead of list_add_tail(), so the group reserved list stays sorted
> by start address and overlapping same-type regions are merged. The
> helper now frees its partial list itself on error and leaves the
> caller's list untouched.
> - Patch 2 (new): reserve PCI host bridge MMIO windows for the iommufd
> IOAS as well, so IOMMU_IOAS_IOVA_RANGES no longer reports IOVAs that
> overlap PCI MMIO windows.
>
> Notes on earlier automated review feedback:
> - The size_t length of each window matches struct iommu_resv_region's
> existing field type; no new truncation is introduced on 64-bit.
> - An IOMMU group cannot span multiple PCI host bridges:
> pci_device_group() only groups devices within a single host bridge
> hierarchy, so scanning the first PCI device of the group is enough.
>
> Guanghui Feng (2):
> iommu: Reserve PCI host bridge MMIO windows in group reserved regions
> iommufd: Reserve PCI host bridge MMIO windows in IOAS reserved regions
>
> drivers/iommu/dma-iommu.c | 17 ++++--
> drivers/iommu/iommu-priv.h | 11 ++++
> drivers/iommu/iommu.c | 85 ++++++++++++++++++++++++++++
> drivers/iommu/iommufd/io_pagetable.c | 15 ++++-
> 4 files changed, 121 insertions(+), 7 deletions(-)
>
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 0/2] iommu: Reserve PCI host bridge MMIO windows for IOVA
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
0 siblings, 0 replies; 6+ messages in thread
From: Jason Gunthorpe @ 2026-09-21 11:55 UTC (permalink / raw)
To: Robin Murphy
Cc: Guanghui Feng, kevin.tian, alex, joro, will, iommu, kvm, linux-kernel
On Mon, Sep 21, 2026 at 12:24:13PM +0100, Robin Murphy wrote:
> On 21/09/2026 11:39 am, Guanghui Feng wrote:
> > The DMA IOVA layer reserves PCI host bridge MMIO windows via
> > iova_reserve_pci_windows() to keep IOVA allocations from overlapping
> > 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. This is especially dangerous when a device sits
> > behind a PCIe switch and ACS Upstream Forwarding is not fully enabled.
> >
> > Passthrough paths that let userspace pick IOVAs did not honour these
> > windows: VFIO type1 and iommufd only excluded IOMMU driver-level
> > reserved regions (RMRR, unity maps, MSI), not PCI host bridge MMIO
> > windows.
>
> This was intentional - I forget where the exact discussion happened, but at
> the time we decided that exposing reserved regions for entire windows was
> the wrong thing to do, as being pessimistically inaccurate might be OK for
> the internal DMA API, but restricting userspace is another matter, and I
> think the consensus was that it wasn't really the IOMMU layer's job to get
> deep into PCI details anyway. Such "accidental" P2P can only happen within a
> group that's assigned to VFIO as a whole, so IIRC the conclusion was that
> users should already have enough information at the VFIO level to avoid BAR
> addresses if they need to.
Yes, but we never did a a very good job to make the information
visible to the VFIO userspace level, and I don't think anyone ever
implemented some sysfs parsing to try to get it.
It would be a nice improvement to expose these pessimistic ranges to
userspace through iommufd so it can choose to opt into them if it
wants. Some kind of helper that the dma-iommu.c and iommufd can call
perhaps.
Jason
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-21 11:55 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-21 7:02 [PATCH] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Guanghui Feng
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
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®