From nobody Thu Sep 24 20:03:24 2026 Received: from out30-124.freemail.mail.aliyun.com (out30-124.freemail.mail.aliyun.com [115.124.30.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BFC6C479882; Mon, 21 Sep 2026 10:39:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.124 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789987186; cv=none; b=kDcpNkyQIK+wErM/VvjdhoUVNXNzU1cnBnIck+oBje2LKMou9RXGVQKh76lH7ypRdJcYMz23NSORu6b0h2D2WQvi+L+N217zShFsXlHri+llsILnMdTPlt9LJcoYVSKiZFzLzqTuzArD0fDqryawhSCIxr7sTs+fN6SzBRwIbZE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789987186; c=relaxed/simple; bh=KB7GsklcK37m0ccOYMXUdW7TrashQmGmn6pEMM9sqJU=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EKfZtoC3ZM5W/iEudTJZ3cPJ55nr3dtWgXTRPivVk8XmGpAtMnQo3pH+uh/yEbMgWVxpGJVVRKEey3GRFir1WEqlkyheo3o82lamzNGDdyxR7pgRVR4r7drbe7yzV+JcbrRyk/XawdThyjNznYS1xqmc4p2vwPAX71we81ZOTS8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=AavaHPwo; arc=none smtp.client-ip=115.124.30.124 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="AavaHPwo" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1789987178; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=qSNppozumsEbPMUAPAntUOPdiTplSobFRAFYxzo9F9o=; b=AavaHPwoIbDrvJbS2nyJ6EaY/It+5zMnj1osrxsjUH5clw8CPzPbzc9TF1ZZnCAIoJx/r5s+/M0kQQ7mHhEVuaEJrAVO6wnFmlThTBTXVM9AjEKmS9lsup7F1razui9BIcHqrFUNC59ITFRR6lctm7R9GfZhj+ye6yOANDy4YFo= X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R381e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam033045098064;MF=guanghuifeng@linux.alibaba.com;NM=1;PH=DS;RN=9;SR=0;TI=SMTPD_---0XBMvKgU_1789987177; Received: from VM20241011-104.tbsite.net(mailfrom:guanghuifeng@linux.alibaba.com fp:SMTPD_---0XBMvKgU_1789987177 cluster:ay36) by smtp.aliyun-inc.com; Mon, 21 Sep 2026 18:39:38 +0800 From: Guanghui Feng To: jgg@ziepe.ca, kevin.tian@intel.com, alex@shazbot.org, joro@8bytes.org, robin.murphy@arm.com, will@kernel.org Cc: iommu@lists.linux.dev, kvm@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 1/2] iommu: Reserve PCI host bridge MMIO windows in group reserved regions Date: Mon, 21 Sep 2026 18:39:21 +0800 Message-ID: <20260921103922.1113752-2-guanghuifeng@linux.alibaba.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260921103922.1113752-1-guanghuifeng@linux.alibaba.com> References: <20260921070234.897736-1-guanghuifeng@linux.alibaba.com> <20260921103922.1113752-1-guanghuifeng@linux.alibaba.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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 @@ =20 #include "dma-iommu.h" #include "iommu-pages.h" +#include "iommu-priv.h" =20 struct iommu_dma_msi_page { struct list_head list; @@ -508,17 +509,23 @@ static int iova_reserve_pci_windows(struct pci_dev *d= ev, struct iova_domain *iovad) { struct pci_host_bridge *bridge =3D pci_find_host_bridge(dev->bus); + struct iommu_resv_region *region, *next; struct resource_entry *window; unsigned long lo, hi; phys_addr_t start =3D 0, end; + LIST_HEAD(pci_windows); + int ret; =20 - resource_list_for_each_entry(window, &bridge->windows) { - if (resource_type(window->res) !=3D IORESOURCE_MEM) - continue; + ret =3D iommu_get_pci_resv_windows(dev, &pci_windows); + if (ret) + return ret; =20 - lo =3D iova_pfn(iovad, window->res->start - window->offset); - hi =3D iova_pfn(iovad, window->res->end - window->offset); + list_for_each_entry_safe(region, next, &pci_windows, list) { + lo =3D iova_pfn(iovad, region->start); + hi =3D iova_pfn(iovad, region->start + region->length - 1); reserve_iova(iovad, lo, hi); + list_del(®ion->list); + kfree(region); } =20 /* 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) =20 #endif /* CONFIG_IOMMU_DEBUG_PAGEALLOC */ =20 +#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; } =20 +#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 =3D 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 =3D { + .type =3D IOMMU_RESV_RESERVED, + }; + + if (resource_type(window->res) !=3D IORESOURCE_MEM) + continue; + + tmp.start =3D window->res->start - window->offset; + tmp.length =3D window->res->end - window->res->start + 1; + + ret =3D 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 =3D iommu_resv_pci_windows(group, head); +#endif + mutex_unlock(&group->mutex); return ret; } --=20 2.43.7 From nobody Thu Sep 24 20:03:24 2026 Received: from out30-112.freemail.mail.aliyun.com (out30-112.freemail.mail.aliyun.com [115.124.30.112]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1ADED4779BE; Mon, 21 Sep 2026 10:39:47 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=115.124.30.112 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789987192; cv=none; b=AzuJp7m3ZWXcQ7JR1Z6QQxXPUlkiA+k7VJJk+K6ZwB7E9bmLi4ipy2IIFQQdTzN+J/2AZDOn3gyrofja9DK3C+Xa+vQUh0n/RWcmAqUkK2kQ1VUFilNZcOS3XeRMiQ8T2SIkQ0EdNvox9//6jWnF/agSk6RuF6WRqPolam17slM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789987192; c=relaxed/simple; bh=UkhjINtAq6pVvDlyfn99Lk1sC5bm0tv9oHYzkNv9AMA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=qkg1rOEEBHOZ6CJ6G5KHJ5ISe4lmqqB3LVgtlBCb+iFa27gkOFbNg4hZR1vueGrnS149Pd89s+WKvkZihsuGSm/LSAzyuJb1RezjQNdgraawrewTB0zFpL3WKFzpgvlwPVGW/gg5dzpsf7d3nQ09BvxrKb7aFTbBPCFHDDeIsAI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com; spf=pass smtp.mailfrom=linux.alibaba.com; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b=a4C2S6rl; arc=none smtp.client-ip=115.124.30.112 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.alibaba.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.alibaba.com header.i=@linux.alibaba.com header.b="a4C2S6rl" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.alibaba.com; s=default; t=1789987179; h=From:To:Subject:Date:Message-ID:MIME-Version; bh=5ZcZPnDGodXH/T9B5tDU4vKRkCmpolgJS5NkZsXR4g0=; b=a4C2S6rl7h3Gbe1sMKRxj/BISdolrBcTVo5Xuxcl3rqW6y71tqI7ZGJ4fjYJzW9tUxee4bQi+QLCX7k6wIBGHCDFcbm4e+KH2kQ7e3IQHRglXLSoo6wnJcllFDd9wRYdagd09E3r88Cle/cMO0g7ldOfNyB3bBlt6x7RxspKf2w= X-Alimail-AntiSpam: AC=PASS;BC=-1|-1;BR=01201311R381e4;CH=green;DM=||false|;DS=||;FP=0|-1|-1|-1|0|-1|-1|-1;HT=maildocker-contentspam011083073210;MF=guanghuifeng@linux.alibaba.com;NM=1;PH=DS;RN=9;SR=0;TI=SMTPD_---0XBMvKgm_1789987178; Received: from VM20241011-104.tbsite.net(mailfrom:guanghuifeng@linux.alibaba.com fp:SMTPD_---0XBMvKgm_1789987178 cluster:ay36) by smtp.aliyun-inc.com; Mon, 21 Sep 2026 18:39:38 +0800 From: Guanghui Feng To: jgg@ziepe.ca, kevin.tian@intel.com, alex@shazbot.org, joro@8bytes.org, robin.murphy@arm.com, will@kernel.org Cc: iommu@lists.linux.dev, kvm@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 2/2] iommufd: Reserve PCI host bridge MMIO windows in IOAS reserved regions Date: Mon, 21 Sep 2026 18:39:22 +0800 Message-ID: <20260921103922.1113752-3-guanghuifeng@linux.alibaba.com> X-Mailer: git-send-email 2.43.7 In-Reply-To: <20260921103922.1113752-1-guanghuifeng@linux.alibaba.com> References: <20260921070234.897736-1-guanghuifeng@linux.alibaba.com> <20260921103922.1113752-1-guanghuifeng@linux.alibaba.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- 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/i= o_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_pag= etable *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 =3D 0; unsigned int num_sw_msi =3D 0; @@ -1591,8 +1592,16 @@ int iopt_table_enforce_dev_resv_regions(struct io_pa= getable *iopt, return -EINVAL; =20 down_write(&iopt->iova_rwsem); - /* FIXME: drivers allocate memory but there is no failure propagated */ - iommu_get_resv_regions(dev, &resv_regions); + + group =3D iommu_group_get(dev); + if (!group) { + rc =3D -ENODEV; + goto out_unlock; + } + + rc =3D iommu_get_group_resv_regions(group, &resv_regions); + if (rc) + goto out_free_resv; =20 list_for_each_entry(resv, &resv_regions, list) { if (resv->type =3D=3D IOMMU_RESV_DIRECT_RELAXABLE) @@ -1624,6 +1633,8 @@ int iopt_table_enforce_dev_resv_regions(struct io_pag= etable *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; } --=20 2.43.7