From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 DCDAB44C658; Thu, 6 Aug 2026 11:25:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015532; cv=none; b=EUNFh6Ll5wqoOK3GVTmEvl7J+DFpbZftdDN2x2XmlUwGMFTgczgcoKM4uYXlxr0wDwyHVJcpSQtbZpuwwA2+JnkIPIFd+iTSsEcLvw76fdJZnlo+peuBPu6w37J6grU/HDFJwsJhXWYBqxxZIFtH6IRpPqRGJ5fVQ7p/WoiK6vg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015532; c=relaxed/simple; bh=99jZ7x1IG/AFSnDKR/6q7IQ+i0GsGQQouOLoG1JdPGg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ELJ6bmJ7i0xL4xeUc3qydxKCmb6odBdHZvqZ/iR+Mxoz7uA/p4SifYPaayXSbvEKfXZzjNqM1HH6Tfcm4OvE01VOG6rVXaKYQzAeNKRGt8j991h5aKtQSsymp/YDWIFonI7PEKgrIDSWoP9srVqqNtB5XNijO/tQRgqDuR26OD4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KHSYOrP/; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="KHSYOrP/" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B398F1F000E9; Thu, 6 Aug 2026 11:25:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015530; bh=7zoGTEOX19ReumS4MvaPnoxz/k38LD5OPaxQjUc1Okk=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=KHSYOrP/SGq/Oe7mPSfm3/o7FZoqwfZS6d3qj77IZIuLB4KvBU1GM/ZIZKsyG5yhC /oE+sQhCVFmzQrZUyAGL7ins5bAyXQdqy9q/H7tS6wd0jyl6msufkbBm8fTm/fv3eO whUcoFtmYv3sHG8CbJq86PRtQtQbtBi8w5c12Z2Km1Z1d0IxeGVI328HU502VUa1oY GxZOKQvlUcjvB9PatTfodGktw4XNNW55nr8Es6Jvn3r2zai7IXKhw8zLTmCIBuTwVD RFYH/q4eFIi4I3UBVWA4sJB0WXGi89wGY56C0wJY0byfDB7sxl3BH8mnY4/CG/fuaH Jo/Cfc4LkZDEA== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v2 01/13] PCI/P2PDMA: Safely terminate ACS redirect lists Date: Thu, 6 Aug 2026 14:24:08 +0300 Message-ID: <20260806-fix-p2p-acs-v2-1-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky seq_buf marks an overflow by setting len to size + 1. The ACS diagnostic path unconditionally writes a terminator to buffer[len - 1], so a path with enough ACS ports to fill the 128-byte buffer writes one byte beyond the buffer when verbose diagnostics are requested. Use seq_buf_str() to terminate truncated output safely and remove the final semicolon only when the buffer did not overflow. Fixes: 52916982af48 ("PCI/P2PDMA: Support peer-to-peer memory") Reviewed-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- drivers/pci/p2pdma.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index b2d5266f8653..2d3b380cc578 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -766,11 +766,13 @@ calc_map_type_and_dist(struct pci_dev *provider, stru= ct pci_dev *client, } =20 if (verbose) { - acs_list.buffer[acs_list.len-1] =3D 0; /* drop final semicolon */ + /* Drop the final semicolon; the list is not empty here. */ + if (!seq_buf_has_overflowed(&acs_list)) + acs_list.buffer[acs_list.len - 1] =3D '\0'; pci_warn(client, "ACS redirect is set between the client and provider (%= s)\n", pci_name(provider)); pci_warn(client, "to disable ACS redirect for this path, add the kernel = parameter: pci=3Ddisable_acs_redir=3D%s\n", - acs_list.buffer); + seq_buf_str(&acs_list)); } acs_redirects =3D true; =20 --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 721CC42AFA7; Thu, 6 Aug 2026 11:25:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015556; cv=none; b=lrv44JfZjbc3bheph/P0GY69fDzNfLWahBJ0WNCHmPl1KFovuWqOpRwf6FOnlanVu/i5MPcneJznwPsWbrDNdExj2KUAmtestKcUNt42pKf0C+ePkWuYi6T4nEuEpsQ+68KkPDL1J5c9Fg/M6Tp2OGhJw/rY9NzSFc0vH6roLnA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015556; c=relaxed/simple; bh=Rsztl0wRdH7emGkUIVxDm9nPwiptRTJ33/0ZalySuAQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VhGSKc+WMpMZ3c9NadsvVeEb40CgT+tRcGPfsOZZz/oLHG1xG67Hy+9dn2vv6yND4+7KTV0MPsMuDi4+y8oiu0OAC5o0VsURcbJokKdoc6VEAxC2N6H+rL/svtAlRq+7k9zUpKasT5lEVoVu1tLsSK0Wp3fTIukuZA9/u8poVx8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lKX1Yfl2; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="lKX1Yfl2" Received: by smtp.kernel.org (Postfix) with ESMTPSA id D55771F000E9; Thu, 6 Aug 2026 11:25:53 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015555; bh=kRCVsJQxui+jwy9YKej4wasXhPcjnuXQRvBcbVRYV74=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=lKX1Yfl2F23rsy1fdZadFzgSegMHYx2q3Cld49itRQHjU+riMrE+RoU1niTabuzMg oViJorx4sVJXiLuzrbRAZ+ZdItaRs3yHy+o7KVka7cKCJY9eDHdSxQ+31Au/OL4Fs/ S1mLMeX2cTC8BBuQF92q+z6k/X9uUe9f+xOaSQRHiWeBuZ2ZyEFGmY1ttwmqxVAj/N Rm4pPY6LlCqghTcupPwcV5CXhz3D349S3vMWH1yBWbJuNGLj/ReVKNxC1U+sGLvvpx Tv6oYaz0qpmxL09KUHEBj7TSbT78vAkhUoywNCHrZFHaVnzaD31P9tL0MoSrMMzwjz qf2+gprzlDn1g== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v2 02/13] PCI/P2PDMA: Report ACS ports when the paths share no upstream bridge Date: Thu, 6 Aug 2026 14:24:09 +0300 Message-ID: <20260806-fix-p2p-acs-v2-2-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky A provider and a client below different Root Ports share no upstream bridge, so the search ends without a match and the walk takes the host-bridge route directly. Neither path is examined for ACS, so no redirect diagnostic is emitted and the host-bridge whitelist warning stays suppressed. Examine the paths before taking that route, so every redirecting port is named in the pci=3Ddisable_acs_redir=3D hint. The mapping type is unchanged. Reviewed-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- drivers/pci/p2pdma.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 2d3b380cc578..801c183ae6b7 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -703,6 +703,7 @@ calc_map_type_and_dist(struct pci_dev *provider, struct= pci_dev *client, { enum pci_p2pdma_map_type map_type =3D PCI_P2PDMA_MAP_THRU_HOST_BRIDGE; struct pci_dev *a =3D provider, *b =3D client, *bb; + bool no_common_upstream =3D false; bool acs_redirects =3D false; struct pci_p2pdma *p2pdma; struct seq_buf acs_list; @@ -740,8 +741,12 @@ calc_map_type_and_dist(struct pci_dev *provider, struc= t pci_dev *client, dist_a++; } =20 - *dist =3D dist_a + dist_b; - goto map_through_host_bridge; + /* + * The paths share no upstream bridge, so the request can only reach + * the peer through the host bridge. Examine the client path anyway, + * so the diagnostics below name every ACS port on both paths. + */ + no_common_upstream =3D true; =20 check_b_path_acs: bb =3D b; @@ -761,6 +766,9 @@ calc_map_type_and_dist(struct pci_dev *provider, struct= pci_dev *client, *dist =3D dist_a + dist_b; =20 if (!acs_cnt) { + if (no_common_upstream) + goto map_through_host_bridge; + map_type =3D PCI_P2PDMA_MAP_BUS_ADDR; goto done; } --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 90A734398E6; Thu, 6 Aug 2026 11:25:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015536; cv=none; b=tPxBMCNSPWSP0Ijjba6xyHkNpRDJ52nq0bLRi4rsQMf7X3NQFwmbtm6n1ROhbv/TRoRTmFgIVmDwVtdyBEqSc1QE6GpP1K37NrXaMGpFk9x6tIp9ww7AnzsndBwxS2GP0wAwrMoW5o0Cql7l49+XAPYmaSGQQgehqA20C7NRUD8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015536; c=relaxed/simple; bh=9HF2lPn0NIDL6Gu6Wq3oDuRXpGaDHTFZB6Si15MNHVo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=WjKNVX3i1TRSfd1wU61ARPXoAiTsHHHh3dz1ejC9bTZlkxOIFdV8IpNYfFRcj23t29ws77PPffV7hpGnK5/xX4I0KCS1MNLLN7QGvtrmqGOZ2oRJRHBIJa3PRIBjIowuftLir6oCUz1D64QjPw1OT99Vt9mq0kiHjD4fR8L1aeM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GAkp2Pom; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="GAkp2Pom" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7B8211F000E9; Thu, 6 Aug 2026 11:25:34 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015535; bh=pVaFz6uX7Pk8ar0hwI/FKExVZXpiFksW/uzSdZJQdio=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=GAkp2PomR+HtokwzKbPnCcUQBYBsPP1awuJjO/gXKjceTqzEBU/bpWQearZI8OKSq PVaaBhK3rLQz8qo//rzFPAlr8sVIiz5HKvvvwuNb51V3gTyPrM0+CpaEs8g25oILlk uNfyxwHNsdGU4A1DeYeXhdlNgCwiEeD4ePqLhhOXSAqDGcYmoP5keIZ04QPiW5Xyeg rJ0MBHh0kvCzKeqVqrJyhocbg/1bLWND+liqKRNRrropoJRdX+iRvEVA0bX+IKcjZ1 7z1r+3UNzdist8OXC0kIAfsmeUd/6ie0iMpChVwGB5CZ0Bgb/WWZswZHYWD88nRK+T 8Zq0K9PqP/xSQ== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v2 03/13] PCI/P2PDMA: Document the Address Type assumption Date: Thu, 6 Aug 2026 14:24:10 +0300 Message-ID: <20260806-fix-p2p-acs-v2-3-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky P2PDMA selects a mapping from the ACS controls that govern Requests carrying an Untranslated address. PCIe r7.0, sec 6.12.3 routes a Request carrying a Translated address directly to the peer whenever ACS Direct Translated P2P is enabled, regardless of P2P Request Redirect and P2P Egress Control. An ATS capable client can therefore reach the peer on the direct path whichever mapping is selected. Record that assumption. Reviewed-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- Documentation/driver-api/pci/p2pdma.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Documentation/driver-api/pci/p2pdma.rst b/Documentation/driver= -api/pci/p2pdma.rst index d3f406cca694..192e9717b62d 100644 --- a/Documentation/driver-api/pci/p2pdma.rst +++ b/Documentation/driver-api/pci/p2pdma.rst @@ -15,6 +15,12 @@ then based on the ACS settings the transaction can route= entirely within the PCIe hierarchy and never reach the root port. The kernel will evaluate the PCIe topology and always permit P2P in these well-defined cases. =20 +This evaluation covers the ACS controls that govern Requests carrying an +Untranslated address. A Port with ACS Direct Translated P2P enabled routes +a Request carrying a Translated address directly to the peer regardless of +those controls, so an ATS capable client may reach the peer on the direct +path whichever mapping the kernel selects. + However, if the P2P transaction reaches the host bridge then it might have= to hairpin back out the same root port, be routed inside the CPU SOC to anoth= er PCIe root port, or routed internally to the SOC. --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 BA2454398E6; Thu, 6 Aug 2026 11:25:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015540; cv=none; b=sR4bolD/5BwLRGVhtCeuWhKvSrBhfYARfYsH3WZOgocaDXez/Qvp1fzuxH0DOgvIBvwyRwINf1//7NVew+9wlNP7tUKDu2yFywFmje2f1ZnM83P2BdVp2TlNFIK4TE2W3VJV+OsOcFksm5BFOzZi8iaiI43FjQR25Z3xmLPrMNU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015540; c=relaxed/simple; bh=YcTPNaanc4zi3EnHUuMUJFdaiYNjCbvS3ExEPIrRpRM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=r2SGD8NpIgl8/X0uadfPIFl4LtNsF06WBZsq/ZAnhIvbad8WJyV9vTK1MAA69bggnHhI/qNwaFd8V7cS3xTQXNF/ONohTPzScWezgNjwfhc2u+a4DXhon1lRGQIMW/aC3mEz90vvHnCA7nVVWsBMRTUSPiaUglT6TXGSLU/nGew= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=hZ8X2W6C; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="hZ8X2W6C" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7C02D1F000E9; Thu, 6 Aug 2026 11:25:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015539; bh=Hdl2AAiA9M9dHtxvXCxfpdCmuaqrjic2cUmo6rODBtI=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=hZ8X2W6CIhAD+pQo2WQ/n+aOpBoQp7CdhWisQLJxG9kuti9J2MgeiF2S813rrZIyS FHOuLHmR3XydZH7SozbXbW0oeJsm/GAxj/Yh7UeoTIJi3GVgZv/qzmDMVq93D2L2g/ 0Kpil/cklu1h8fg6U/X8wOY3ycU+XuuLadIFzv9rA86sLnnt6N96rxtqZfY+AI5geN aGo26EYMK1bNUJyKfSswxuQF2FDSYEmZwERcGZP1ER4eOj+Hra7mnmXng4suH1fcCw mt4HElacJq79Ydrc62x22iAYB/abFcWKoafbP8QwmKa8OScOETK9wEahnRJab5ge5M CHLKrW6AHNaSQ== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v2 04/13] PCI: Account for Direct Translated P2P in ACS isolation checks Date: Thu, 6 Aug 2026 14:24:11 +0300 Message-ID: <20260806-fix-p2p-acs-v2-4-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky PCIe r7.0, sec 6.12.3: peer-to-peer Memory Requests whose Address Type (AT) field indicates a Translated address must be routed to the peer Port/Function without redirection, regardless of ACS P2P Request Redirect and ACS P2P Egress Control settings. Request Redirect therefore does not isolate devices below a Port with ACS Direct Translated P2P enabled. Sec 6.12.1.1 makes such a Request an ACS Violation once Translation Blocking is enabled, and that error "must take precedence over ... ACS P2P control mechanisms". Report isolation only in that case. Without Translation Blocking, devices below such a Port now share an IOMMU group. Fixes: ad805758c0eb ("PCI: add ACS validation utility") Reviewed-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- drivers/pci/pci.c | 10 ++++++++++ drivers/pci/quirks.c | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 77b17b13ee61..c586481949dc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3547,6 +3547,7 @@ void pci_configure_ari(struct pci_dev *dev) =20 static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) { + bool request_redirect =3D acs_flags & PCI_ACS_RR; int pos; u16 ctrl; =20 @@ -3562,6 +3563,15 @@ static bool pci_acs_flags_enabled(struct pci_dev *pd= ev, u16 acs_flags) acs_flags &=3D (pdev->acs_capabilities | PCI_ACS_EC); =20 pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); + + /* + * Direct Translated P2P routes a Translated Request to the peer + * regardless of Request Redirect, so Request Redirect does not + * isolate unless Translation Blocking rejects the request first. + */ + if (request_redirect && (ctrl & PCI_ACS_DT) && !(ctrl & PCI_ACS_TB)) + return false; + return (ctrl & acs_flags) =3D=3D acs_flags; } =20 diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index b09f27f7846f..bb4c09cbbd10 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4978,6 +4978,7 @@ static bool pci_quirk_intel_spt_pch_acs_match(struct = pci_dev *dev) =20 static int pci_quirk_intel_spt_pch_acs(struct pci_dev *dev, u16 acs_flags) { + bool request_redirect =3D acs_flags & PCI_ACS_RR; int pos; u32 cap, ctrl; =20 @@ -4994,6 +4995,10 @@ static int pci_quirk_intel_spt_pch_acs(struct pci_de= v *dev, u16 acs_flags) =20 pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl); =20 + /* Direct Translated P2P may bypass Request Redirect. */ + if (request_redirect && (ctrl & PCI_ACS_DT) && !(ctrl & PCI_ACS_TB)) + return 0; + return pci_acs_ctrl_enabled(acs_flags, ctrl); } =20 --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 46B8C43BDCD; Thu, 6 Aug 2026 11:25:44 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015545; cv=none; b=AQM8rVrbqWlQujwZpngfSrO7hGm4m2sR+mnoTAx/WdEM06HXxQBT0v2OfMxLAAWQMyBi6B9SvmSNKqIJyEyqR343/qdsXZEhscF+K5/wjYos9HuKdjsyZCvSrjkVFftSdfEL6h0YaNGzVRT8vT2yQ/CBBvlHT77WTyoZUXgfc/U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015545; c=relaxed/simple; bh=nynccnadfP7oBkpYJx7nlv1LiYLH9K/Ri7uFu4DBtzw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=V/hjEjOfbTmo+kT0FhHw7EAtygEUYy/qjGLYMTupgyQA1jFERwh0aaEitNXQ7OWk6Sy7XAfzkb/DTF5+VknZS8qkDwyGyTYok0eJdL6hBz9bjMyc4VV7tAwn18bcpfNyjjSs0QrzT5T7lwv2OHbmoDjw+xoEc06eGWkeNS5pO5g= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=UnMpCbhC; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="UnMpCbhC" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 4B3471F000E9; Thu, 6 Aug 2026 11:25:43 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015544; bh=Xf1hpnFV1gAlg+YZ6rAGiEiOotBe5WvmqLB9CnbDV3A=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=UnMpCbhCThGD1Qf5sXU6UL90VqBh+J/Wpssa160kng1wZPpUelpZreCor2JX7MtIS JkbrLOpTNMQGx1HpwTzln/+ZiFCgn8Pv4pz99aHicFP0v33xeCBdCmdNXYpMP23vCc AeLYgE+XSl7tqZTzuxDU4zg9HD61MCc9wkAtehPFxVEZ/Hq1nynpvKTSmRX84BviMM OXG71GsInGVnAildrHEi06luFoQXQUhtPSuNVu0IAL8mKbrrA45X9dvaA+Vd2y0I2c 1W8AHFuFMLUfHdBSSkrcQXCjCwKYijNuWe+DlcwVVJAYEn3bjZ++GkiuVLSbtxRrcc vsgzd60KnOrYQ== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v2 05/13] PCI: Add ACS egress control vector accessor Date: Thu, 6 Aug 2026 14:24:12 +0300 Message-ID: <20260806-fix-p2p-acs-v2-5-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky Whether ACS P2P Egress Control routes, redirects, or blocks a peer-to-peer request depends on the Egress Control Vector bit for the target port, not on the enable bit alone (PCIe r7.0, sec 6.12.3). Provide a helper to read that bit for a peer Root or Switch Downstream Port. Report an unreadable or uncovered vector as an error rather than as a clear bit, so callers do not mistake it for permission to route directly. Reviewed-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- drivers/pci/pci.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++= ++++ drivers/pci/pci.h | 1 + 2 files changed, 59 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c586481949dc..154eb08036ad 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3545,6 +3545,64 @@ void pci_configure_ari(struct pci_dev *dev) } } =20 +/* + * PCIe r7.0, sec 7.7.12: only for Root Ports and Switch Downstream Ports = does + * each Egress Control Vector bit correspond to a Port Number. Elsewhere = the + * vector is indexed by Function or Function Group Number, so a Link + * Capabilities Port Number must not be used to select a bit. + * + * pcie_downstream_port() is too permissive here because it also accepts a + * PCI/PCI-X to PCIe Bridge. + */ +static bool pci_acs_egress_vector_port(const struct pci_dev *dev) +{ + int type =3D pci_pcie_type(dev); + + return type =3D=3D PCI_EXP_TYPE_ROOT_PORT || + type =3D=3D PCI_EXP_TYPE_DOWNSTREAM; +} + +/** + * pci_acs_egress_ctrl_set - Read an ACS Egress Control Vector bit + * @pdev: ingress Root or Switch Downstream Port + * @target: target Root or Switch Downstream Port + * + * Return: 1 if @pdev's Egress Control Vector bit for @target is set, 0 if + * it is clear, or a negative errno if the bit cannot be determined. + */ +int pci_acs_egress_ctrl_set(struct pci_dev *pdev, struct pci_dev *target) +{ + unsigned int vector_size; + u32 lnkcap, vector; + u8 target_port; + int ret; + + if (!(pdev->acs_capabilities & PCI_ACS_EC) || + !pci_acs_egress_vector_port(pdev) || + !pci_acs_egress_vector_port(target)) + return -EOPNOTSUPP; + + ret =3D pcie_capability_read_dword(target, PCI_EXP_LNKCAP, &lnkcap); + if (ret) + return pcibios_err_to_errno(ret); + + target_port =3D FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap); + vector_size =3D pdev->acs_capabilities >> 8; + + /* An Egress Control Vector Size of 0 encodes 256 bits. */ + if (vector_size && target_port >=3D vector_size) + return -ERANGE; + + ret =3D pci_read_config_dword(pdev, + pdev->acs_cap + PCI_ACS_EGRESS_CTL_V + + (target_port / 32) * sizeof(vector), + &vector); + if (ret) + return pcibios_err_to_errno(ret); + + return !!(vector & BIT(target_port % 32)); +} + static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) { bool request_redirect =3D acs_flags & PCI_ACS_RR; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 4469e1a77f3c..5da067f8abc3 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1045,6 +1045,7 @@ resource_size_t pci_min_window_alignment(struct pci_b= us *bus, =20 void pci_acs_init(struct pci_dev *dev); void pci_enable_acs(struct pci_dev *dev); +int pci_acs_egress_ctrl_set(struct pci_dev *pdev, struct pci_dev *target); #ifdef CONFIG_PCI_QUIRKS int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); int pci_dev_specific_enable_acs(struct pci_dev *dev); --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 86A14448B84; Thu, 6 Aug 2026 11:25:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015550; cv=none; b=njsAaENBV/MYbPr4scKNMtpQaCRzPG/dwqYOqS2Ieuh9IgPJ3UUMtobQIqqFGxaMATtQeNQ/2SAqDBugwA63O71ZOKs60j3vDqKBs6QksD4gSFXdcP2dpFyXAXL0iROD7b3bk+zh6MDeaZhlBG9EzEWUxLwnC/1bZJiwjQ/C09c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015550; c=relaxed/simple; bh=r38pIzC0p+LQUUpxadctWU7eSlja8Oao0spWZDX3pTM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=A3I/Iatz/GmiVOzEwTvCxxCBWfLEHQ/2w93Gh+39Fu0sqgkD+VrvyyoUFFoDtHHKD5F0Ie0j1IK9/ft3bS8U+lTd6FeP1w7ihC4FzFJDB7cGCMLDMd+yRSEytVcwU00sqdDYhjoiODn6ywygRtBrORD5qk6ADLTEedDzQ+kWBKM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=KH+Q5NuG; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="KH+Q5NuG" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 80E3B1F00A3A; Thu, 6 Aug 2026 11:25:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015549; bh=8qB2IxVto5TkweZOPktEIgL7ELDxeJGNd3yvs7+VilI=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=KH+Q5NuG7c/74UeyxzObqoS9owWjYw2KrL9zl5RsLTt7zfN4+bZJDL6ANku8FBh1C ZxPcG0Pg4K4XhcczcN9iFYFd9Kz9Zivr6gUAjDHzDodMUhrnmB3LtAmsMo6VXuRm24 Mi28+DkasIJ2wDNd/m272Pt1HzlRUCfG8yZNxwWFzQyJuxFcbnk8Y2wOWRCGYYPjKk AqnXsehZ2wSU7Nw+/VsDn6yj9lJSyAAzu5gdSjrE0ET2uGi2sLuePAootfUegspvpZ GwdmJ17FS+c4NHDRheotdwPvBpSwlnzKjXVGyDzbkn8C30PBldKTIFB5Mim5udZxPw GqN4Z9Hg9SJ1g== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v2 06/13] PCI: Account for ACS egress control in isolation checks Date: Thu, 6 Aug 2026 14:24:13 +0300 Message-ID: <20260806-fix-p2p-acs-v2-6-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky pci_acs_enabled() treats P2P Request Redirect as effective whenever its control bit is set. PCIe r7.0, sec 6.12.3, table 6-11 lets an enabled Egress Control Vector override it: a clear vector bit routes the request directly. IOMMU grouping uses this check to prove peer requests cannot bypass the IOMMU, but cannot know every applicable vector bit, so Request Redirect gives no such guarantee while Egress Control is enabled. Report Request Redirect as ineffective there, merging the devices into one IOMMU group, and report no isolation when the register cannot be read. Apply the same rule to the Intel SPT PCH quirk. Reviewed-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- drivers/pci/pci.c | 12 +++++++++++- drivers/pci/quirks.c | 10 ++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 154eb08036ad..bc1c3b68c131 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3620,7 +3620,8 @@ static bool pci_acs_flags_enabled(struct pci_dev *pde= v, u16 acs_flags) */ acs_flags &=3D (pdev->acs_capabilities | PCI_ACS_EC); =20 - pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); + if (pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl)) + return false; =20 /* * Direct Translated P2P routes a Translated Request to the peer @@ -3630,6 +3631,15 @@ static bool pci_acs_flags_enabled(struct pci_dev *pd= ev, u16 acs_flags) if (request_redirect && (ctrl & PCI_ACS_DT) && !(ctrl & PCI_ACS_TB)) return false; =20 + /* + * Egress Control can override Request Redirect for peer requests. + * This target-independent check cannot prove that every applicable + * Egress Control Vector bit is set, so RR does not guarantee isolation + * while EC is enabled. + */ + if (request_redirect && (ctrl & PCI_ACS_EC)) + return false; + return (ctrl & acs_flags) =3D=3D acs_flags; } =20 diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index bb4c09cbbd10..ebfe902b0118 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -4990,15 +4990,21 @@ static int pci_quirk_intel_spt_pch_acs(struct pci_d= ev *dev, u16 acs_flags) return -ENOTTY; =20 /* see pci_acs_flags_enabled() */ - pci_read_config_dword(dev, pos + PCI_ACS_CAP, &cap); + if (pci_read_config_dword(dev, pos + PCI_ACS_CAP, &cap)) + return 0; acs_flags &=3D (cap | PCI_ACS_EC); =20 - pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl); + if (pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl)) + return 0; =20 /* Direct Translated P2P may bypass Request Redirect. */ if (request_redirect && (ctrl & PCI_ACS_DT) && !(ctrl & PCI_ACS_TB)) return 0; =20 + /* Egress Control may override Request Redirect for peer requests. */ + if (request_redirect && (ctrl & PCI_ACS_EC)) + return 0; + return pci_acs_ctrl_enabled(acs_flags, ctrl); } =20 --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 2E8DA448D18; Thu, 6 Aug 2026 11:26:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015581; cv=none; b=i/PYgwwVcqm8BzDbIkVe5eD+9lO7Qeglxa/tHtZaP9905p1QQMkAWOhq+zZ0Uz+2z02rUTAx3xk/N+zzeKpbhMUe9+yEX9Ju7SFCn6XCuz4CxLmnEoemy8YZawp9/c+RwRz3/269vgwfg1pBP2e52M2vltNxLSwd4DPlyI65dYo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015581; c=relaxed/simple; bh=xu7bKRo5RLOqBjK5bDL/T/Kebb6ZO73Tv2zy4XFJYbY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=dYfMW/qTgQwe8FSeelEc9A3k93fhCdYp4SzWbhq1YQKG7w4UKTveIUG+3Spgik6ARYhvm0o85BoTZ6+bDNNQpa4YjNVaq0xMfWjvfz3sPeczr43AT4xsToaPKQv/QsgjpaiJlCjHSIluRSP3ulnUJa9EWyRQKVh3Uu2CI8yGgP8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=PEJVpLDF; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="PEJVpLDF" Received: by smtp.kernel.org (Postfix) with ESMTPSA id B3BCB1F000E9; Thu, 6 Aug 2026 11:26:18 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015579; bh=Z0pIvbWo9F46ShccSQVXE4zIzpt48rOwf1cluc8Lgss=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=PEJVpLDF9+0TaXoAJZ0YslhtOjKIxBbnA6vlbHnqmBOPojd91sRw1QiatFnqza16n 0mbyfTzmHMovuvyy8mxjmyN9SCvFchMh27QHm7XGOOwG7Yi9c8Dy939OnbmTdtLhlO rAUSdvsdp/x+im4PKULDXQq9yBcmsSP5UGtoRf7OZxV7Omo9kKAwhJMynsT6jEXY2I eQeaHxiRYUxsL1s7YsSZO+aORNKXkIOcXYPmuKTN1cn7+OBxrsmD79iUAiinYwY5/R Mq5yCSGPJrz4EDMY1BPx/SeEVvCFUH/UFmLSG2BsxVmcSa3oXae4as0XeLmAKte4bR ZyZAv5ZWSV7XA== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v2 07/13] PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits Date: Thu, 6 Aug 2026 14:24:14 +0300 Message-ID: <20260806-fix-p2p-acs-v2-7-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky The redirect test reports an upstream redirect for any ACS redirect or egress control bit. PCIe r7.0, sec 6.12.3, table 6-11 ties the outcome to the control bits and the target vector, and an enabled egress control bit alone does not redirect. Compute the result from the control bits so the vector can be honored next, using the host-bridge route while the peer target is unknown. Reviewed-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- drivers/pci/p2pdma.c | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 801c183ae6b7..079cec6c0509 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -21,6 +21,8 @@ #include #include =20 +#include "pci.h" + struct pci_p2pdma { struct gen_pool *pool; bool p2pmem_published; @@ -489,26 +491,45 @@ static struct pci_dev *find_parent_pci_dev(struct dev= ice *dev) return NULL; } =20 -/* - * Check if a PCI bridge has its ACS redirection bits set to redirect P2P - * TLPs upstream via ACS. Returns 1 if the packets will be redirected - * upstream, 0 otherwise. - */ -static int pci_bridge_has_acs_redir(struct pci_dev *pdev) +enum pci_acs_p2pdma_state { + PCI_ACS_P2PDMA_DIRECT, + PCI_ACS_P2PDMA_REDIRECT, +}; + +static enum pci_acs_p2pdma_state +pci_acs_p2pdma_state(struct pci_dev *pdev, struct pci_dev *target) { int pos; u16 ctrl; =20 pos =3D pdev->acs_cap; if (!pos) - return 0; + return PCI_ACS_P2PDMA_DIRECT; =20 - pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl); + if (pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl)) + return PCI_ACS_P2PDMA_REDIRECT; =20 - if (ctrl & (PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_EC)) - return 1; + if (!(ctrl & PCI_ACS_EC)) + return ctrl & (PCI_ACS_RR | PCI_ACS_CR) ? + PCI_ACS_P2PDMA_REDIRECT : PCI_ACS_P2PDMA_DIRECT; =20 - return 0; + /* + * The vector cannot be read without the peer target, so redirect + * upstream until the paths diverge. + */ + if (!target) + return PCI_ACS_P2PDMA_REDIRECT; + + /* + * PCIe r7.0, sec 6.12.3, table 6-11: a set or indeterminate egress + * control vector bit keeps the request off the direct path; a clear + * bit permits it, subject only to completion redirect. + */ + if (pci_acs_egress_ctrl_set(pdev, target)) + return PCI_ACS_P2PDMA_REDIRECT; + + return ctrl & PCI_ACS_CR ? PCI_ACS_P2PDMA_REDIRECT : + PCI_ACS_P2PDMA_DIRECT; } =20 static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *p= dev) @@ -722,7 +743,8 @@ calc_map_type_and_dist(struct pci_dev *provider, struct= pci_dev *client, while (a) { dist_b =3D 0; =20 - if (pci_bridge_has_acs_redir(a)) { + if (pci_acs_p2pdma_state(a, NULL) =3D=3D + PCI_ACS_P2PDMA_REDIRECT) { seq_buf_print_bus_devfn(&acs_list, a); acs_cnt++; } @@ -755,7 +777,8 @@ calc_map_type_and_dist(struct pci_dev *provider, struct= pci_dev *client, if (a =3D=3D bb) break; =20 - if (pci_bridge_has_acs_redir(bb)) { + if (pci_acs_p2pdma_state(bb, NULL) =3D=3D + PCI_ACS_P2PDMA_REDIRECT) { seq_buf_print_bus_devfn(&acs_list, bb); acs_cnt++; } --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 58EEF3D47DD; Thu, 6 Aug 2026 11:26:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015561; cv=none; b=fxPBohkPc+0Nh9TS/xGeacaYLJWvMfByxH1dJ8G7aO4AYwFTf2NHoouSykyBbtIhTrEWVygZpoYqTxMpiUfDNkJ1DECIdR9bkBfiwRPQWQHiBD2F9v1KfZg+fzzH6CJTISqRKLr671iqoWEz01xcBF37yQO3PCqUuO3pDSXwoFI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015561; c=relaxed/simple; bh=7o98YhqKMcoXCho5RulWmmSOddxkpcdNjx2CaHaXgso=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=Cp8UCfCR/1AjmslvL2d0KdGIQR9mEWoM0FiHsbIlZDjBtxnfTj4bwn9MEBMSDfk2m9PJ0lbF/NXzle3lzYxYWSTZHyRuMiB7FbzOhb3GF55/5a7mn7fp21/zWb0tc8eQro42pI+ajwLgWl5o9q5QG3XHFGYTy7BiMblhewVLyAw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SwBGYLD5; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="SwBGYLD5" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2CB501F000E9; Thu, 6 Aug 2026 11:25:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015560; bh=szizLkZEdfCMxYc6R09G8coDVy8b9sJOXHQ1jnLYqaY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=SwBGYLD56wL7CQv3KFsEOTEfIOzGHi5++PZq7vRpgZFXi8MihLnbnhzFomYphKG1p H3jrDzMfUThBJyXE3Xae4tmKcZCP2yAZIc/pTduQca6u8QeQ1oKZWC1El5FxE2S0qg nYTpfHOzKAXr9jtf+VJgjPzuw5p6gyQRhc0EtuVA7IU+I31OrcDN1L/mJuDKafIevk CHGqtwD9lAsd9CBFrDSv1FloKnWlxgLiQjlh2BnOyKIVS/fuAHe9h9rOypYfarBJQP OpinxL+CcRnjNs4TXC/0rzAYni3Osw1cygFmp8jkt51mz9zlHssF5ipsHZH+leAteV VUFrgUb2+Zt8w== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v2 08/13] PCI/P2PDMA: Honor ACS egress control vectors Date: Thu, 6 Aug 2026 14:24:15 +0300 Message-ID: <20260806-fix-p2p-acs-v2-8-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky An enabled Egress Control bit does not itself send a peer request upstream. PCIe r7.0, sec 6.12.3, table 6-11 makes the outcome depend on the Egress Control Vector bit for the target port: a clear bit routes the request directly regardless of P2P Request Redirect. Read the vector where the paths diverge below their common upstream port. Keep a clear vector bit on the direct path, subject to P2P Completion Redirect. A set bit with Request Redirect clear is an ACS Violation. ACS acts only on peer-to-peer Requests, so route it, and an indeterminate vector, through the host bridge. Fixes: 52916982af48 ("PCI/P2PDMA: Support peer-to-peer memory") Reviewed-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- drivers/pci/p2pdma.c | 105 ++++++++++++++++++++++++++++++++---------------= ---- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index 079cec6c0509..dbccc1d0c4e3 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -494,12 +494,13 @@ static struct pci_dev *find_parent_pci_dev(struct dev= ice *dev) enum pci_acs_p2pdma_state { PCI_ACS_P2PDMA_DIRECT, PCI_ACS_P2PDMA_REDIRECT, + PCI_ACS_P2PDMA_NOT_SUPPORTED, }; =20 static enum pci_acs_p2pdma_state pci_acs_p2pdma_state(struct pci_dev *pdev, struct pci_dev *target) { - int pos; + int pos, ret; u16 ctrl; =20 pos =3D pdev->acs_cap; @@ -507,26 +508,26 @@ pci_acs_p2pdma_state(struct pci_dev *pdev, struct pci= _dev *target) return PCI_ACS_P2PDMA_DIRECT; =20 if (pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl)) - return PCI_ACS_P2PDMA_REDIRECT; + return PCI_ACS_P2PDMA_NOT_SUPPORTED; =20 - if (!(ctrl & PCI_ACS_EC)) + /* EC applies only at the path divergence where the target is known. */ + if (!target || !(ctrl & PCI_ACS_EC)) return ctrl & (PCI_ACS_RR | PCI_ACS_CR) ? PCI_ACS_P2PDMA_REDIRECT : PCI_ACS_P2PDMA_DIRECT; =20 /* - * The vector cannot be read without the peer target, so redirect - * upstream until the paths diverge. + * PCIe r7.0, sec 6.12.3, table 6-11: a set Egress Control Vector + * bit redirects the request only when Request Redirect is set. With + * Request Redirect clear, the request is handled as an ACS Violation. + * A clear vector bit permits direct routing, subject to Completion + * Redirect. */ - if (!target) - return PCI_ACS_P2PDMA_REDIRECT; - - /* - * PCIe r7.0, sec 6.12.3, table 6-11: a set or indeterminate egress - * control vector bit keeps the request off the direct path; a clear - * bit permits it, subject only to completion redirect. - */ - if (pci_acs_egress_ctrl_set(pdev, target)) - return PCI_ACS_P2PDMA_REDIRECT; + ret =3D pci_acs_egress_ctrl_set(pdev, target); + if (ret < 0) + return PCI_ACS_P2PDMA_NOT_SUPPORTED; + if (ret) + return ctrl & PCI_ACS_RR ? PCI_ACS_P2PDMA_REDIRECT : + PCI_ACS_P2PDMA_NOT_SUPPORTED; =20 return ctrl & PCI_ACS_CR ? PCI_ACS_P2PDMA_REDIRECT : PCI_ACS_P2PDMA_DIRECT; @@ -707,9 +708,9 @@ static unsigned long map_types_idx(struct pci_dev *clie= nt) * then to Device B. The mapping type returned depends on the ACS * redirection setting of the ports along the path. * - * If ACS redirect is set on any port in the path, traffic between the - * devices will go through the host bridge, so return - * PCI_P2PDMA_MAP_THRU_HOST_BRIDGE; otherwise return + * If ACS redirects traffic on any port in the path, or blocks the direct + * path or leaves its routing indeterminate, return + * PCI_P2PDMA_MAP_THRU_HOST_BRIDGE. Otherwise, return * PCI_P2PDMA_MAP_BUS_ADDR. * * Any two devices that have a data path that goes through the host bridge @@ -723,12 +724,15 @@ calc_map_type_and_dist(struct pci_dev *provider, stru= ct pci_dev *client, int *dist, bool verbose) { enum pci_p2pdma_map_type map_type =3D PCI_P2PDMA_MAP_THRU_HOST_BRIDGE; - struct pci_dev *a =3D provider, *b =3D client, *bb; + struct pci_dev *a =3D provider, *b =3D client, *bb, *target; + struct pci_dev *a_child =3D NULL, *b_child =3D NULL; + struct pci_dev *acs_unsupported =3D NULL; + enum pci_acs_p2pdma_state state; bool no_common_upstream =3D false; bool acs_redirects =3D false; struct pci_p2pdma *p2pdma; struct seq_buf acs_list; - int acs_cnt =3D 0; + int acs_redirect_cnt =3D 0; int dist_a =3D 0; int dist_b =3D 0; char buf[128]; @@ -742,23 +746,19 @@ calc_map_type_and_dist(struct pci_dev *provider, stru= ct pci_dev *client, */ while (a) { dist_b =3D 0; - - if (pci_acs_p2pdma_state(a, NULL) =3D=3D - PCI_ACS_P2PDMA_REDIRECT) { - seq_buf_print_bus_devfn(&acs_list, a); - acs_cnt++; - } - + b_child =3D NULL; bb =3D b; =20 while (bb) { if (a =3D=3D bb) - goto check_b_path_acs; + goto check_paths_acs; =20 + b_child =3D bb; bb =3D pci_upstream_bridge(bb); dist_b++; } =20 + a_child =3D a; a =3D pci_upstream_bridge(a); dist_a++; } @@ -770,25 +770,44 @@ calc_map_type_and_dist(struct pci_dev *provider, stru= ct pci_dev *client, */ no_common_upstream =3D true; =20 -check_b_path_acs: - bb =3D b; +check_paths_acs: + *dist =3D dist_a + dist_b; + bb =3D provider; =20 while (bb) { + target =3D bb =3D=3D a_child ? b_child : NULL; + state =3D pci_acs_p2pdma_state(bb, target); + if (state !=3D PCI_ACS_P2PDMA_DIRECT) { + seq_buf_print_bus_devfn(&acs_list, bb); + if (state =3D=3D PCI_ACS_P2PDMA_REDIRECT) + acs_redirect_cnt++; + else if (!acs_unsupported) + acs_unsupported =3D bb; + } + if (a =3D=3D bb) break; =20 - if (pci_acs_p2pdma_state(bb, NULL) =3D=3D - PCI_ACS_P2PDMA_REDIRECT) { + bb =3D pci_upstream_bridge(bb); + } + + bb =3D client; + + while (bb && a !=3D bb) { + target =3D bb =3D=3D b_child ? a_child : NULL; + state =3D pci_acs_p2pdma_state(bb, target); + if (state !=3D PCI_ACS_P2PDMA_DIRECT) { seq_buf_print_bus_devfn(&acs_list, bb); - acs_cnt++; + if (state =3D=3D PCI_ACS_P2PDMA_REDIRECT) + acs_redirect_cnt++; + else if (!acs_unsupported) + acs_unsupported =3D bb; } =20 bb =3D pci_upstream_bridge(bb); } =20 - *dist =3D dist_a + dist_b; - - if (!acs_cnt) { + if (!acs_unsupported && !acs_redirect_cnt) { if (no_common_upstream) goto map_through_host_bridge; =20 @@ -796,13 +815,21 @@ calc_map_type_and_dist(struct pci_dev *provider, stru= ct pci_dev *client, goto done; } =20 + /* + * ACS controls only act on Requests routed peer-to-peer, so a blocked + * or indeterminate direct path still leaves the host-bridge route. + */ if (verbose) { /* Drop the final semicolon; the list is not empty here. */ if (!seq_buf_has_overflowed(&acs_list)) acs_list.buffer[acs_list.len - 1] =3D '\0'; - pci_warn(client, "ACS redirect is set between the client and provider (%= s)\n", - pci_name(provider)); - pci_warn(client, "to disable ACS redirect for this path, add the kernel = parameter: pci=3Ddisable_acs_redir=3D%s\n", + if (acs_unsupported) + pci_warn(client, "ACS blocks the direct P2P path to provider %s at %s\n= ", + pci_name(provider), pci_name(acs_unsupported)); + else + pci_warn(client, "ACS redirect is set between the client and provider (= %s)\n", + pci_name(provider)); + pci_warn(client, "to disable ACS controls for this path, add the kernel = parameter: pci=3Ddisable_acs_redir=3D%s\n", seq_buf_str(&acs_list)); } acs_redirects =3D true; --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 36940445AFA; Thu, 6 Aug 2026 11:26:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015566; cv=none; b=es1U6g22Zxj1ULO79lfb3aJzlzTmZx2IUiNgI1Abn17fJggMqAReNMGKhobVwXOpxXGUT55dDisCZOSOlWKIZ6LvasX8DQ0y8CVmpXa7wd0Pd8GLZ5vx7KLQCvjsfgcNKQYwvKUAL7qg+ibk3kkqo1zCFtByTt5SIuJpfX2apSc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015566; c=relaxed/simple; bh=/qB9EPt1SuVzuKHk/38dt/1egMHZPkRfIW0RsxVoujE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=qdgzYNnxUR53vpNwmrsJ2LBK0UhmQ6OhHrJgVm+UFTiloGtl3Z+r4Gwr7Vf2iI8SawQWnoJD6CPoIXCa7nwZUjeIyN+zM/3IfSOlThgAUEBMBW/jl4om3cQIBeNnKmaWH0PQ0LJPOUWODQUfSPcjR71Vux5qZN9gJODG4Ohooo4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SNolqjCU; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="SNolqjCU" Received: by smtp.kernel.org (Postfix) with ESMTPSA id F2E8F1F00A3A; Thu, 6 Aug 2026 11:26:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015564; bh=OZZXkIc0GQwiMsud/EAnsqCEGP0vg8WdJvCkLVJM+5A=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=SNolqjCU87djA/Ve1uQV7gC44+woalX04ijCJIEk0SscLiy/35hDGWZC8zQSaOM2Q we94pBLRWaxGSFUFjueLX+WFuTmHOChWuWwuWophc92K39lwEARfEyAvRbCar0WNgK 4+SSS9VvhQk2L7eEAdMvXbbVAGfqORKVAFfmkOg75W9JSkMC8qUJotEiI2GzMKZrau KwtY7dxcpV/sR6nm9pp/A05vsVxhraDg8DlKmjnW8rzG9crtXRGOe92qBLa2KfSmuN 7NCaJrocYBts73cDrzzNzhTGVvK1MdkP+zLzjHUuYADxTZWkz3mi/H28kY1nwTN1YO pGSvthjvjcY/Q== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org Subject: [PATCH v2 09/13] PCI/P2PDMA: Document ACS egress control handling Date: Thu, 6 Aug 2026 14:24:16 +0300 Message-ID: <20260806-fix-p2p-acs-v2-9-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky Document the ACS P2P Egress Control outcomes used by P2PDMA: a clear target vector bit permits direct routing, a set bit with Request Redirect enabled sends the request upstream, and a set bit with Request Redirect disabled causes an ACS Violation that P2PDMA rejects. Also record that pci=3Ddisable_acs_redir=3D clears P2P Request Redirect, Completion Redirect, and Egress Control. Reviewed-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- Documentation/admin-guide/kernel-parameters.txt | 9 +++++---- Documentation/driver-api/pci/p2pdma.rst | 8 ++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentatio= n/admin-guide/kernel-parameters.txt index b5493a7f8f22..5c3ed4fd439c 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5226,10 +5226,11 @@ Kernel parameters disable_acs_redir=3D[; ...] Specify one or more PCI devices (in the format specified above) separated by semicolons. - Each device specified will have the PCI ACS - redirect capabilities forced off which will - allow P2P traffic between devices through - bridges without forcing it upstream. Note: + Each device specified will have the PCI ACS P2P + Request Redirect, Completion Redirect, and Egress + Control features forced off. This may allow P2P + traffic through bridges that would otherwise be + redirected upstream or blocked. Note: this removes isolation between devices and may put more devices in an IOMMU group. config_acs=3D diff --git a/Documentation/driver-api/pci/p2pdma.rst b/Documentation/driver= -api/pci/p2pdma.rst index 192e9717b62d..79bfe8055b43 100644 --- a/Documentation/driver-api/pci/p2pdma.rst +++ b/Documentation/driver-api/pci/p2pdma.rst @@ -15,6 +15,14 @@ then based on the ACS settings the transaction can route= entirely within the PCIe hierarchy and never reach the root port. The kernel will evaluate the PCIe topology and always permit P2P in these well-defined cases. =20 +ACS P2P Egress Control does not, by itself, force a transaction upstream. A +clear Egress Control Vector bit for the peer port permits direct routing; a +set bit redirects the request upstream when P2P Request Redirect is enable= d. +When Request Redirect is disabled, a set vector bit causes an ACS Violation +instead. The kernel evaluates these controls together and routes P2P DMA +through the host bridge when the direct path is blocked or cannot be +determined. + This evaluation covers the ACS controls that govern Requests carrying an Untranslated address. A Port with ACS Direct Translated P2P enabled routes a Request carrying a Translated address directly to the peer regardless of --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 4688A4570E9; Thu, 6 Aug 2026 11:26:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015571; cv=none; b=bRdSUVjcTBV+FOA6vY0b90DgJtV9mGA6vwvl5PUwqE1IV5DyBmdV9/YyL88BwNsi37eEsT46uP/ZLW9ARAJ0PHPnGuCq/q9gbB7VTMcFilxTIRKjAWiJ6BunwzJkP+jxd8p+6f7iEo4UUGabOaP108QgjVtmlh246cugO9RXgTI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015571; c=relaxed/simple; bh=08QF7/NyMeJ8Z1Owdt0cl1n4v8HEpigCWIq2fHmMnEc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=p1mGjA0g5EK9zdRUS3buwWk/gDtB/aa2/gYZZv4JRLEtcQ5cncqjwClj+8Z59Vv9YrfZsoKFhcFJmRM4T675nJ4OMMm4HSrkefIJC05GawmwOyraepewHH4OrVWaf//v6uUo+UQS6zVetjYg5+H7jnKmSI70TmyUXfm2tUuqCTE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=I+VrQYLs; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="I+VrQYLs" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2A2E61F00A3A; Thu, 6 Aug 2026 11:26:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015569; bh=1par2UqLfoeEOPAhysKCelPck2+IUM4dpO7qCEIDzJI=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=I+VrQYLsdy+RWmtFVVnBCtV3ZJ+5tAUaU0fyLet9XGR+VLjzezKwgjwSma1tGvzhD EZLfIp88HKx1xIFs/M2w3ucA6BuPza/uO8qzENvf+3LEJPwDE+ACNLRIAj2OrWLutu H4IsTxTU6nW6LhGLpzvfHWKRQFlH5TSLuRigso64r3DHSMqiZWvh1p1Uti4MtKXyC2 euCYNu5DPr88GELNivmFh0n69zKYeeHi3oPN96XwvT+mXH58USQU1gtTePKU9LFBWW fuGdESxlyarruM1MkrNHpefeju2lPmTm3WPAhR3zzdXeFO7bc7UHX1G/JnqwL4of76 CbjTIxr8g1LWQ== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, "Claude Opus 4.8" Subject: [PATCH v2 10/13] PCI/P2PDMA: Extract pure ACS routing decision helpers Date: Thu, 6 Aug 2026 14:24:17 +0300 Message-ID: <20260806-fix-p2p-acs-v2-10-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky The ACS Egress Control routing decision (PCIe r7.0, sec 6.12.3, table 6-11) and the Egress Control Vector Size rule were embedded in functions that also perform config-space I/O and walk the PCIe hierarchy. That made the branch-heavy logic -- in particular the paths that require an Egress Control Vector, which are unreachable on most hardware -- difficult to exercise in isolation. Factor the logic into two pure helpers: - pci_acs_p2pdma_decision() maps the ACS control word, whether the target port is known, and the target's Egress Control Vector bit to a routing state. - pci_acs_egress_port_valid() applies the "a vector size of 0 encodes 256 bits" rule to decide whether a target port is within the vector. pci_acs_p2pdma_state() and pci_acs_egress_ctrl_set() now call these. No functional change intended: pci_acs_egress_ctrl_set() still checks the port range before reading the vector DWORD. The helpers are exposed under CONFIG_KUNIT via VISIBLE_IF_KUNIT so the following patch can unit-test them. Reviewed-by: Logan Gunthorpe Assisted-by: Claude Opus 4.8 Signed-off-by: Leon Romanovsky --- drivers/pci/p2pdma.c | 60 +++++++++++++++++++++++++++++-------------------= ---- drivers/pci/pci.c | 26 +++++++++++++++++++---- drivers/pci/pci.h | 17 +++++++++++++++ 3 files changed, 73 insertions(+), 30 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index dbccc1d0c4e3..c7680964d8a6 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -491,16 +491,40 @@ static struct pci_dev *find_parent_pci_dev(struct dev= ice *dev) return NULL; } =20 -enum pci_acs_p2pdma_state { - PCI_ACS_P2PDMA_DIRECT, - PCI_ACS_P2PDMA_REDIRECT, - PCI_ACS_P2PDMA_NOT_SUPPORTED, -}; +/* + * PCIe r7.0, sec 6.12.3, table 6-11: decide how a peer-to-peer request at= an + * ACS-capable ingress port routes, given its Egress Control register @ctr= l, + * whether the target port is known (@has_target), and that target's Egress + * Control Vector bit (@egress: 1 set, 0 clear, negative if it could not be + * read). + * + * Egress Control applies only where the target is known (the path diverge= nce). + * There, a set vector bit redirects the request only when Request Redirec= t is + * set; with Request Redirect clear it is an ACS Violation. A clear vecto= r bit + * permits direct routing, subject to Completion Redirect. + */ +VISIBLE_IF_KUNIT enum pci_acs_p2pdma_state +pci_acs_p2pdma_decision(u16 ctrl, bool has_target, int egress) +{ + if (!has_target || !(ctrl & PCI_ACS_EC)) + return ctrl & (PCI_ACS_RR | PCI_ACS_CR) ? + PCI_ACS_P2PDMA_REDIRECT : PCI_ACS_P2PDMA_DIRECT; + + if (egress < 0) + return PCI_ACS_P2PDMA_NOT_SUPPORTED; + if (egress) + return ctrl & PCI_ACS_RR ? PCI_ACS_P2PDMA_REDIRECT : + PCI_ACS_P2PDMA_NOT_SUPPORTED; + + return ctrl & PCI_ACS_CR ? PCI_ACS_P2PDMA_REDIRECT : + PCI_ACS_P2PDMA_DIRECT; +} +EXPORT_SYMBOL_IF_KUNIT(pci_acs_p2pdma_decision); =20 static enum pci_acs_p2pdma_state pci_acs_p2pdma_state(struct pci_dev *pdev, struct pci_dev *target) { - int pos, ret; + int pos, egress =3D 0; u16 ctrl; =20 pos =3D pdev->acs_cap; @@ -510,27 +534,11 @@ pci_acs_p2pdma_state(struct pci_dev *pdev, struct pci= _dev *target) if (pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl)) return PCI_ACS_P2PDMA_NOT_SUPPORTED; =20 - /* EC applies only at the path divergence where the target is known. */ - if (!target || !(ctrl & PCI_ACS_EC)) - return ctrl & (PCI_ACS_RR | PCI_ACS_CR) ? - PCI_ACS_P2PDMA_REDIRECT : PCI_ACS_P2PDMA_DIRECT; + /* Egress Control is evaluated only where the target is known. */ + if (target && (ctrl & PCI_ACS_EC)) + egress =3D pci_acs_egress_ctrl_set(pdev, target); =20 - /* - * PCIe r7.0, sec 6.12.3, table 6-11: a set Egress Control Vector - * bit redirects the request only when Request Redirect is set. With - * Request Redirect clear, the request is handled as an ACS Violation. - * A clear vector bit permits direct routing, subject to Completion - * Redirect. - */ - ret =3D pci_acs_egress_ctrl_set(pdev, target); - if (ret < 0) - return PCI_ACS_P2PDMA_NOT_SUPPORTED; - if (ret) - return ctrl & PCI_ACS_RR ? PCI_ACS_P2PDMA_REDIRECT : - PCI_ACS_P2PDMA_NOT_SUPPORTED; - - return ctrl & PCI_ACS_CR ? PCI_ACS_P2PDMA_REDIRECT : - PCI_ACS_P2PDMA_DIRECT; + return pci_acs_p2pdma_decision(ctrl, target, egress); } =20 static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *p= dev) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bc1c3b68c131..c9e64003f8d2 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3562,6 +3562,26 @@ static bool pci_acs_egress_vector_port(const struct = pci_dev *dev) type =3D=3D PCI_EXP_TYPE_DOWNSTREAM; } =20 +/** + * pci_acs_egress_port_valid - Is a target port within the Egress Control = Vector + * @acs_caps: the ingress port's ACS Capability register + * @target_port: the target Downstream Port number + * + * The Egress Control Vector Size occupies bits 15:8 of the ACS Capability + * register (PCIe r7.0, sec 7.7.12). A size of 0 encodes 256 bits, so + * every port number is addressable. + * + * Return: %true if @target_port has a bit in the Egress Control Vector. + */ +VISIBLE_IF_KUNIT +bool pci_acs_egress_port_valid(u16 acs_caps, u8 target_port) +{ + unsigned int vector_size =3D acs_caps >> 8; + + return !vector_size || target_port < vector_size; +} +EXPORT_SYMBOL_IF_KUNIT(pci_acs_egress_port_valid); + /** * pci_acs_egress_ctrl_set - Read an ACS Egress Control Vector bit * @pdev: ingress Root or Switch Downstream Port @@ -3572,7 +3592,6 @@ static bool pci_acs_egress_vector_port(const struct p= ci_dev *dev) */ int pci_acs_egress_ctrl_set(struct pci_dev *pdev, struct pci_dev *target) { - unsigned int vector_size; u32 lnkcap, vector; u8 target_port; int ret; @@ -3587,10 +3606,8 @@ int pci_acs_egress_ctrl_set(struct pci_dev *pdev, st= ruct pci_dev *target) return pcibios_err_to_errno(ret); =20 target_port =3D FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap); - vector_size =3D pdev->acs_capabilities >> 8; =20 - /* An Egress Control Vector Size of 0 encodes 256 bits. */ - if (vector_size && target_port >=3D vector_size) + if (!pci_acs_egress_port_valid(pdev->acs_capabilities, target_port)) return -ERANGE; =20 ret =3D pci_read_config_dword(pdev, @@ -3602,6 +3619,7 @@ int pci_acs_egress_ctrl_set(struct pci_dev *pdev, str= uct pci_dev *target) =20 return !!(vector & BIT(target_port % 32)); } +EXPORT_SYMBOL_IF_KUNIT(pci_acs_egress_ctrl_set); =20 static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5da067f8abc3..6f40b43d3c3f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -2,6 +2,7 @@ #ifndef DRIVERS_PCI_H #define DRIVERS_PCI_H =20 +#include #include #include #include @@ -1046,6 +1047,22 @@ resource_size_t pci_min_window_alignment(struct pci_= bus *bus, void pci_acs_init(struct pci_dev *dev); void pci_enable_acs(struct pci_dev *dev); int pci_acs_egress_ctrl_set(struct pci_dev *pdev, struct pci_dev *target); + +/* + * Peer-to-peer routing decision for an ACS-capable ingress port, per + * PCIe r7.0, sec 6.12.3, table 6-11. + */ +enum pci_acs_p2pdma_state { + PCI_ACS_P2PDMA_DIRECT, /* peer-to-peer permitted directly */ + PCI_ACS_P2PDMA_REDIRECT, /* redirected upstream to host bridge */ + PCI_ACS_P2PDMA_NOT_SUPPORTED, /* no usable peer-to-peer route */ +}; + +#if IS_ENABLED(CONFIG_KUNIT) +bool pci_acs_egress_port_valid(u16 acs_caps, u8 target_port); +enum pci_acs_p2pdma_state pci_acs_p2pdma_decision(u16 ctrl, bool has_targe= t, + int egress); +#endif #ifdef CONFIG_PCI_QUIRKS int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); int pci_dev_specific_enable_acs(struct pci_dev *dev); --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 C437245C6F8; Thu, 6 Aug 2026 11:26:14 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015576; cv=none; b=IjtTa/y2IzQnrf67RF7AyS6ud73PfWK86fJVi9kVMZUgVXcnDwi+5vTWOhhpCKWhUncQLXWezXR7KSk4f3NMLyPdYnKKjm0g6peOQ52rPg1VTRFyuuXLxWNutap+r9wRhuxXy2Rv3kHmgAsxo0TBGQlQ+xV6gPOwBauFSpB/5Kw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015576; c=relaxed/simple; bh=XsYFDkOxq9sLhVBNfR50O53saTFx29040arh4AB/zLs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=YTkQFprXJdPRijnbDSRl9bbUWa8fiE/R4jI1k4miE5PINIDFfCitlI3HMeK3G/e11ffikccWTO3T+BJj3Y1FRB28zYaFN3vEaGfIDBSkCuEGXyJI0TETZ3PkW7sMVbtUv7vZYjW9U0LOcB51vKou24PYRfx5Nm/BjF4+UFerVks= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=kyEQR7Z0; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="kyEQR7Z0" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 7ADF71F000E9; Thu, 6 Aug 2026 11:26:13 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015574; bh=sptYvfA/PIiRWim1H51k6YGSeLQxWEEaK/HXAO4RCdg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=kyEQR7Z01ebPIwfA4j0iGH8nk6YBuQ1JRponHsdiAp5LttMYsvJAJxPGUtRiVnMpB uU1mVtm6HJzmZ9oMjrQK+0NDot4fhj3L9YOXkx4xTpEyNElVlfMwFDzs0CFjLNkYQM HUt34jI2uVK1YNqd7VftebIpAHhP/mM3uI2SkuyXM/jNO05/XeX0ZCUIrUPcOLIaWC Y68aZyC28fF89dbcijkszU0bkQZh2DhB/yNC6wK2kDIln2dksYeop3A17Fkw5lQZOo 7rMPVBkodpS5E4y4xTyILO72xLhgf7/5u/IWMwMRMR3UNCQg4lK6ln0YoV0vQtKbiR 11Wu0FQe++Ijg== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, "Claude Opus 4.8" Subject: [PATCH v2 11/13] PCI/P2PDMA: Add KUnit tests for ACS routing decisions Date: Thu, 6 Aug 2026 14:24:18 +0300 Message-ID: <20260806-fix-p2p-acs-v2-11-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky Add a KUnit suite exercising the ACS peer-to-peer routing logic: - pci_acs_p2pdma_decision(): the full PCIe table 6-11 truth table, including the Egress Control Vector branches (bit set/clear, with and without Request Redirect and Completion Redirect) that require a switch implementing the Egress Control Vector and so cannot be reached on commonly available hardware. - pci_acs_egress_port_valid(): the vector-size boundary, including the "size 0 encodes 256 bits" case. - pci_acs_egress_ctrl_set(): driven through a fake pci_ops returning canned config space, covering target Port Number extraction from LNKCAP, the vector DWORD offset (target_port / 32), the bit position (target_port % 32), the -ERANGE bound, and the unsupported-port guards -- all without real hardware. Run with: cat > /tmp/pci-acs.kunitconfig <<'EOF' CONFIG_KUNIT=3Dy CONFIG_PCI=3Dy CONFIG_ZONE_DEVICE=3Dy CONFIG_MEMORY_HOTPLUG=3Dy CONFIG_MEMORY_HOTREMOVE=3Dy CONFIG_SPARSEMEM_VMEMMAP=3Dy CONFIG_PCI_P2PDMA=3Dy CONFIG_PCI_ACS_KUNIT_TEST=3Dy EOF ./tools/testing/kunit/kunit.py run --arch=3Dx86_64 \ --kunitconfig=3D/tmp/pci-acs.kunitconfig --jobs=3D$(nproc) pci_acs Assisted-by: Claude Opus 4.8 Reviewd-by: Logan Gunthorpe Signed-off-by: Leon Romanovsky --- drivers/pci/Kconfig | 15 ++ drivers/pci/Makefile | 1 + drivers/pci/pci_acs_test.c | 373 +++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 389 insertions(+) diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 0c7408509ba2..30ad7f407c6f 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -226,6 +226,21 @@ config PCI_P2PDMA =20 If unsure, say N. =20 +config PCI_ACS_KUNIT_TEST + tristate "KUnit tests for PCI ACS P2P routing" if !KUNIT_ALL_TESTS + depends on PCI_P2PDMA && KUNIT + default KUNIT_ALL_TESTS + help + Enable KUnit tests for the PCI ACS peer-to-peer routing decision + logic (PCIe ACS Egress Control, table 6-11), including the code + paths that require an ACS Egress Control Vector and so cannot be + exercised on typical peer-to-peer hardware. + + For more information on KUnit and unit tests in general, refer to + the KUnit documentation in Documentation/dev-tools/kunit/. + + If unsure, say N. + config PCI_LABEL def_bool y if (DMI || ACPI) select NLS diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 41ebc3b9a518..6305d128d3df 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_PCI_STUB) +=3D pci-stub.o obj-$(CONFIG_PCI_PF_STUB) +=3D pci-pf-stub.o obj-$(CONFIG_PCI_ECAM) +=3D ecam.o obj-$(CONFIG_PCI_P2PDMA) +=3D p2pdma.o +obj-$(CONFIG_PCI_ACS_KUNIT_TEST) +=3D pci_acs_test.o obj-$(CONFIG_XEN_PCIDEV_FRONTEND) +=3D xen-pcifront.o obj-$(CONFIG_VGA_ARB) +=3D vgaarb.o obj-$(CONFIG_PCI_DOE) +=3D doe.o diff --git a/drivers/pci/pci_acs_test.c b/drivers/pci/pci_acs_test.c new file mode 100644 index 000000000000..7227136c8cff --- /dev/null +++ b/drivers/pci/pci_acs_test.c @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KUnit tests for PCI ACS peer-to-peer routing decision logic. + * + * These exercise the pure helpers factored out of the ACS Egress Control + * handling (PCIe r7.0, sec 6.12.3, table 6-11). They cover the code paths + * that require an ACS Egress Control Vector, which cannot be reached on t= he + * peer-to-peer hardware commonly available for testing. + */ +#include + +#include +#include + +#include "pci.h" + +/* pci_acs_p2pdma_decision(): the table 6-11 truth table. */ + +struct acs_decision_case { + const char *desc; + u16 ctrl; + bool has_target; + int egress; + enum pci_acs_p2pdma_state expect; +}; + +/* Shorthands to keep the table below readable. */ +#define ACS_DIRECT PCI_ACS_P2PDMA_DIRECT +#define ACS_REDIR PCI_ACS_P2PDMA_REDIRECT +#define ACS_NO_P2P PCI_ACS_P2PDMA_NOT_SUPPORTED + +static const struct acs_decision_case acs_decision_cases[] =3D { + /* No target known: Egress Control is ignored, RR/CR decide. */ + { "no_target/none", 0, false, 0, ACS_DIRECT }, + { "no_target/rr", PCI_ACS_RR, false, 0, ACS_REDIR }, + { "no_target/cr", PCI_ACS_CR, false, 0, ACS_REDIR }, + { "no_target/ec_only", PCI_ACS_EC, false, 0, ACS_DIRECT }, + + /* Target known but EC clear: RR/CR decide, egress not consulted. */ + { "ec_clear/none", 0, true, 0, ACS_DIRECT }, + { "ec_clear/rr", PCI_ACS_RR, true, 0, ACS_REDIR }, + { "ec_clear/cr", PCI_ACS_CR, true, 0, ACS_REDIR }, + { "ec_clear/rr_cr", PCI_ACS_RR | PCI_ACS_CR, true, 0, ACS_REDIR }, + + /* EC set but vector unreadable: never a usable P2P route. */ + { "ec/eopnotsupp", PCI_ACS_EC | PCI_ACS_RR, true, -EOPNOTSUPP, ACS_NO_P2P= }, + { "ec/erange", PCI_ACS_EC | PCI_ACS_CR, true, -ERANGE, ACS_NO_P2P }, + + /* EC set, vector bit set: redirect iff RR, else ACS Violation. */ + { "ec/vec_set/none", PCI_ACS_EC, true, 1, ACS_NO_P2P }, + { "ec/vec_set/cr", PCI_ACS_EC | PCI_ACS_CR, true, 1, ACS_NO_P2P }, + { "ec/vec_set/rr", PCI_ACS_EC | PCI_ACS_RR, true, 1, ACS_REDIR }, + { "ec/vec_set/rr_cr", PCI_ACS_EC | PCI_ACS_RR | PCI_ACS_CR, true, 1, + ACS_REDIR }, + + /* EC set, vector bit clear: direct unless CR redirects. */ + { "ec/vec_clear/none", PCI_ACS_EC, true, 0, ACS_DIRECT }, + { "ec/vec_clear/rr", PCI_ACS_EC | PCI_ACS_RR, true, 0, ACS_DIRECT }, + { "ec/vec_clear/cr", PCI_ACS_EC | PCI_ACS_CR, true, 0, ACS_REDIR }, + { "ec/vec_clear/rr_cr", PCI_ACS_EC | PCI_ACS_RR | PCI_ACS_CR, true, 0, + ACS_REDIR }, +}; + +#undef ACS_DIRECT +#undef ACS_REDIR +#undef ACS_NO_P2P + +static void acs_decision_desc(const struct acs_decision_case *c, char *des= c) +{ + strscpy(desc, c->desc, KUNIT_PARAM_DESC_SIZE); +} + +KUNIT_ARRAY_PARAM(acs_decision, acs_decision_cases, acs_decision_desc); + +static void pci_acs_p2pdma_decision_test(struct kunit *test) +{ + const struct acs_decision_case *c =3D test->param_value; + + KUNIT_EXPECT_EQ(test, + pci_acs_p2pdma_decision(c->ctrl, c->has_target, c->egress), + c->expect); +} + +/* pci_acs_egress_port_valid(): the Egress Control Vector Size rule. */ + +struct egress_valid_case { + const char *desc; + u16 acs_caps; + u8 target_port; + bool expect; +}; + +static const struct egress_valid_case egress_valid_cases[] =3D { + /* A Vector Size of 0 encodes 256 bits, so every port is addressable. */ + { "size0/port0", 0x0000, 0, true }, + { "size0/port255", 0x0000, 255, true }, + /* Vector Size N (bits 15:8): ports [0, N) are addressable. */ + { "size1/port0", 0x0100, 0, true }, + { "size1/port1", 0x0100, 1, false }, + { "size8/port7", 0x0800, 7, true }, + { "size8/port8", 0x0800, 8, false }, + { "size255/port254", 0xff00, 254, true }, + { "size255/port255", 0xff00, 255, false }, +}; + +static void egress_valid_desc(const struct egress_valid_case *c, char *des= c) +{ + strscpy(desc, c->desc, KUNIT_PARAM_DESC_SIZE); +} + +KUNIT_ARRAY_PARAM(egress_valid, egress_valid_cases, egress_valid_desc); + +static void pci_acs_egress_port_valid_test(struct kunit *test) +{ + const struct egress_valid_case *c =3D test->param_value; + + KUNIT_EXPECT_EQ(test, + pci_acs_egress_port_valid(c->acs_caps, c->target_port), + c->expect); +} + +/* + * pci_acs_egress_ctrl_set(): drive the config-space reads with a fake pci= _ops + * so the Egress Control Vector lookup is exercised without real hardware = -- + * the target Port Number from LNKCAP, the vector DWORD at target_port/32,= and + * the bit at target_port%32. + */ + +/* PCIe Capabilities register value: device/port @type, capability version= 2. */ +#define ACS_TEST_PCIE_FLAGS(type) (((type) << 4) | 0x2) +#define ACS_DOWNSTREAM ACS_TEST_PCIE_FLAGS(PCI_EXP_TYPE_DOWNSTREAM) +#define ACS_ENDPOINT ACS_TEST_PCIE_FLAGS(PCI_EXP_TYPE_ENDPOINT) +#define ACS_ROOT_PORT ACS_TEST_PCIE_FLAGS(PCI_EXP_TYPE_ROOT_PORT) +#define ACS_PCIE_BRIDGE ACS_TEST_PCIE_FLAGS(PCI_EXP_TYPE_PCIE_BRIDGE) + +struct acs_fake_cfg { + unsigned int pdev_devfn; + unsigned int target_devfn; + u16 pdev_acs_cap; + u8 target_pcie_cap; + u8 target_port; + u32 egress_vector[8]; /* full 256-bit vector */ +}; + +static int acs_fake_cfg_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct acs_fake_cfg *cfg =3D bus->sysdata; + + *val =3D 0; + if (size !=3D 4) + return PCIBIOS_SUCCESSFUL; + + if (devfn =3D=3D cfg->target_devfn && + where =3D=3D cfg->target_pcie_cap + PCI_EXP_LNKCAP) { + *val =3D FIELD_PREP(PCI_EXP_LNKCAP_PN, cfg->target_port); + } else if (devfn =3D=3D cfg->pdev_devfn) { + int base =3D cfg->pdev_acs_cap + PCI_ACS_EGRESS_CTL_V; + + if (where >=3D base && + where < base + (int)sizeof(cfg->egress_vector)) + *val =3D cfg->egress_vector[(where - base) / 4]; + } + return PCIBIOS_SUCCESSFUL; +} + +static int acs_fake_cfg_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops acs_fake_ops =3D { + .read =3D acs_fake_cfg_read, + .write =3D acs_fake_cfg_write, +}; + +static struct acs_fake_cfg acs_base_cfg(void) +{ + return (struct acs_fake_cfg){ + .pdev_devfn =3D PCI_DEVFN(0, 0), + .target_devfn =3D PCI_DEVFN(1, 0), + .pdev_acs_cap =3D 0x100, + .target_pcie_cap =3D 0x40, + }; +} + +static int acs_egress_ctrl_set(struct kunit *test, struct acs_fake_cfg *cf= g, + u16 pdev_acs_caps, u16 pdev_flags, u16 target_flags) +{ + struct pci_bus *bus =3D kunit_kzalloc(test, sizeof(*bus), GFP_KERNEL); + struct pci_dev *pdev =3D kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL); + struct pci_dev *target =3D kunit_kzalloc(test, sizeof(*target), GFP_KERNE= L); + + KUNIT_ASSERT_NOT_NULL(test, bus); + KUNIT_ASSERT_NOT_NULL(test, pdev); + KUNIT_ASSERT_NOT_NULL(test, target); + + bus->ops =3D &acs_fake_ops; + bus->sysdata =3D cfg; + + pdev->bus =3D bus; + pdev->devfn =3D cfg->pdev_devfn; + pdev->acs_cap =3D cfg->pdev_acs_cap; + pdev->acs_capabilities =3D pdev_acs_caps; + pdev->pcie_cap =3D 0x40; + pdev->pcie_flags_reg =3D pdev_flags; + + target->bus =3D bus; + target->devfn =3D cfg->target_devfn; + target->pcie_cap =3D cfg->target_pcie_cap; + target->pcie_flags_reg =3D target_flags; + + return pci_acs_egress_ctrl_set(pdev, target); +} + +static void acs_egress_vector_bit_set_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + cfg.target_port =3D 5; + cfg.egress_vector[0] =3D BIT(5); + + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, PCI_ACS_EC | (32 << 8), + ACS_DOWNSTREAM, ACS_DOWNSTREAM), + 1); +} + +static void acs_egress_vector_bit_clear_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + cfg.target_port =3D 5; /* vector left all-zero */ + + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, PCI_ACS_EC | (32 << 8), + ACS_DOWNSTREAM, ACS_DOWNSTREAM), + 0); +} + +static void acs_egress_high_port_index_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + /* Port 40 lives in vector DWORD 1, bit 8: exercises target_port/32. */ + cfg.target_port =3D 40; + cfg.egress_vector[1] =3D BIT(40 % 32); + + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, PCI_ACS_EC | (64 << 8), + ACS_DOWNSTREAM, ACS_DOWNSTREAM), + 1); +} + +static void acs_egress_port_out_of_range_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + /* Vector Size 8, port 40 is beyond it. */ + cfg.target_port =3D 40; + + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, PCI_ACS_EC | (8 << 8), + ACS_DOWNSTREAM, ACS_DOWNSTREAM), + -ERANGE); +} + +static void acs_egress_no_ec_cap_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + cfg.target_port =3D 5; + + /* acs_capabilities without PCI_ACS_EC: unsupported. */ + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, 32 << 8, + ACS_DOWNSTREAM, ACS_DOWNSTREAM), + -EOPNOTSUPP); +} + +static void acs_egress_pdev_not_downstream_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + cfg.target_port =3D 5; + + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, PCI_ACS_EC | (32 << 8), + ACS_ENDPOINT, ACS_DOWNSTREAM), + -EOPNOTSUPP); +} + +static void acs_egress_target_not_downstream_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + cfg.target_port =3D 5; + + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, PCI_ACS_EC | (32 << 8), + ACS_DOWNSTREAM, ACS_ENDPOINT), + -EOPNOTSUPP); +} + +/* + * The vector is indexed by Port Number only for Root Ports and Switch + * Downstream Ports, so a PCI/PCI-X to PCIe Bridge must not be indexed by = its + * Link Capabilities Port Number. + */ +static void acs_egress_pdev_pcie_bridge_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + cfg.target_port =3D 5; + + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, PCI_ACS_EC | (32 << 8), + ACS_PCIE_BRIDGE, ACS_DOWNSTREAM), + -EOPNOTSUPP); +} + +static void acs_egress_target_pcie_bridge_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + cfg.target_port =3D 5; + + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, PCI_ACS_EC | (32 << 8), + ACS_DOWNSTREAM, ACS_PCIE_BRIDGE), + -EOPNOTSUPP); +} + +/* A Root Port is a valid ingress and egress port for the vector. */ +static void acs_egress_root_port_test(struct kunit *test) +{ + struct acs_fake_cfg cfg =3D acs_base_cfg(); + + cfg.target_port =3D 5; + cfg.egress_vector[0] =3D BIT(5); + + KUNIT_EXPECT_EQ(test, + acs_egress_ctrl_set(test, &cfg, PCI_ACS_EC | (32 << 8), + ACS_ROOT_PORT, ACS_ROOT_PORT), + 1); +} + +static struct kunit_case pci_acs_test_cases[] =3D { + KUNIT_CASE_PARAM(pci_acs_p2pdma_decision_test, acs_decision_gen_params), + KUNIT_CASE_PARAM(pci_acs_egress_port_valid_test, egress_valid_gen_params), + KUNIT_CASE(acs_egress_vector_bit_set_test), + KUNIT_CASE(acs_egress_vector_bit_clear_test), + KUNIT_CASE(acs_egress_high_port_index_test), + KUNIT_CASE(acs_egress_port_out_of_range_test), + KUNIT_CASE(acs_egress_no_ec_cap_test), + KUNIT_CASE(acs_egress_pdev_not_downstream_test), + KUNIT_CASE(acs_egress_target_not_downstream_test), + KUNIT_CASE(acs_egress_pdev_pcie_bridge_test), + KUNIT_CASE(acs_egress_target_pcie_bridge_test), + KUNIT_CASE(acs_egress_root_port_test), + {} +}; + +static struct kunit_suite pci_acs_test_suite =3D { + .name =3D "pci_acs", + .test_cases =3D pci_acs_test_cases, +}; +kunit_test_suite(pci_acs_test_suite); + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("KUnit tests for PCI ACS peer-to-peer routing decisions= "); --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 DFB42448B84; Thu, 6 Aug 2026 11:26:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015592; cv=none; b=lGFgnIbk0etfI8b4pBgt1Lny9wnNzTFf+aD45ZSvVPZW/ouMNLFPPGmGwxNVDuOeczav9kAbAQ0vxqAchijbz285nDBMT2MW6erupGzgsSY0Ok+d+ANEitT2OkiE7Bn+86Pa4Bb6jwxW2DRI7djLmfW4idatWQQJRZVybXMxqFs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015592; c=relaxed/simple; bh=zYpjnOEBpQYNVpPLDUQEy0unepNmahsd854tLCILByo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=OAOJ/TL5BpCdolFHpuRrgxG/g6Z8vSiT8CiNWLL9K7cJKmsBMdQLv82oLpmy5da/7Off2wmkb7D5DNQTU+G8dT4WRIU5RNrfy3EDIPQ/nVSYWfj8pT5D4I8LqWRek0W8gC0FpbaOXj77fHjMbWQEXq2vlPXRlgX9DG81ZjqVXj0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ddnsudwB; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ddnsudwB" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A74851F000E9; Thu, 6 Aug 2026 11:26:29 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015590; bh=HWC+sXjFtiSRErNQ1ocugxz6z8/8px/I4e9UjzIQKEg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=ddnsudwBLDbQgqGlaj9yes5y3G/LA54QHT/oFd3+KTlAsqd+QpvUAm4JHoD3vgpMm UYfZp+lAsTY+JTau8+WVwnmn4V0/KZtwrbSohEyb9KhC/zP6WflaeJELEVRUP/Rsxl 4YOD/79m0pxtJqu6AQ4Ol0MKErFODG5U6K1HVKMyucm+IhGOxN4kDkmqrwBf4TnUkk 2xZlkG396Ey2W86+vlpv58PIGDWOSkLaUAVZJ9cQrwZinI7UldS4wVKS2J+ZSGfVpj Ur273ckj2Sk75E7dyM8a+/+yhl1ShtA/+qZ7TKIxUE+JnX3X4XkL93HiIe3lbk0Ku9 W/q9RELkL+3tw== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, "Claude Opus 4.8" Subject: [PATCH v2 12/13] PCI/P2PDMA: Add KUnit coverage for the ACS P2P routing walk Date: Thu, 6 Aug 2026 14:24:19 +0300 Message-ID: <20260806-fix-p2p-acs-v2-12-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky Extend the ACS KUnit suite with end-to-end coverage of calc_map_type_and_dist(), the provider-to-client hierarchy walk. A fabricated PCIe fabric (host bridge, Root Port, Switch Upstream Port, two Switch Downstream Ports and the provider/client endpoints) with a fake pci_ops backing the ACS Control, Egress Control Vector and LNKCAP reads lets the walk run without real hardware. The tests assert: - BUS_ADDR when no port on the path enables ACS; - THRU_HOST_BRIDGE when a Downstream Port's Egress Control Vector routes the peer with Request Redirect clear (an ACS Violation) at the path divergence, leaving only the host-bridge route; - BUS_ADDR when Egress Control is enabled but the peer's vector bit is clear; - THRU_HOST_BRIDGE when Request Redirect redirects the request and the host bridge is whitelisted. calc_map_type_and_dist() is exposed under CONFIG_KUNIT via VISIBLE_IF_KUNIT. Reviewed-by: Logan Gunthorpe Assisted-by: Claude Opus 4.8 Signed-off-by: Leon Romanovsky --- drivers/pci/p2pdma.c | 3 +- drivers/pci/pci.h | 4 + drivers/pci/pci_acs_test.c | 204 +++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 210 insertions(+), 1 deletion(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index c7680964d8a6..18e5339bc49e 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -727,7 +727,7 @@ static unsigned long map_types_idx(struct pci_dev *clie= nt) * ports per above. If the device is not in the whitelist, return * PCI_P2PDMA_MAP_NOT_SUPPORTED. */ -static enum pci_p2pdma_map_type +VISIBLE_IF_KUNIT enum pci_p2pdma_map_type calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client, int *dist, bool verbose) { @@ -859,6 +859,7 @@ calc_map_type_and_dist(struct pci_dev *provider, struct= pci_dev *client, rcu_read_unlock(); return map_type; } +EXPORT_SYMBOL_IF_KUNIT(calc_map_type_and_dist); =20 /** * pci_p2pdma_distance_many - Determine the cumulative distance between diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6f40b43d3c3f..6f200d40d69e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -6,6 +6,7 @@ #include #include #include +#include #include =20 struct pcie_tlp_log; @@ -1062,6 +1063,9 @@ enum pci_acs_p2pdma_state { bool pci_acs_egress_port_valid(u16 acs_caps, u8 target_port); enum pci_acs_p2pdma_state pci_acs_p2pdma_decision(u16 ctrl, bool has_targe= t, int egress); +enum pci_p2pdma_map_type calc_map_type_and_dist(struct pci_dev *provider, + struct pci_dev *client, + int *dist, bool verbose); #endif #ifdef CONFIG_PCI_QUIRKS int pci_dev_specific_acs_enabled(struct pci_dev *dev, u16 acs_flags); diff --git a/drivers/pci/pci_acs_test.c b/drivers/pci/pci_acs_test.c index 7227136c8cff..b258fc46fcaa 100644 --- a/drivers/pci/pci_acs_test.c +++ b/drivers/pci/pci_acs_test.c @@ -10,6 +10,7 @@ #include =20 #include +#include #include =20 #include "pci.h" @@ -346,6 +347,205 @@ static void acs_egress_root_port_test(struct kunit *t= est) 1); } =20 +/* + * calc_map_type_and_dist(): drive the full provider->client hierarchy walk + * over a fabricated PCIe fabric matching the canonical "two devices behin= d one + * switch" tree: + * + * host bridge / root bus + * Root Port + * Switch Upstream Port + * Switch Downstream Port 0 -- provider + * Switch Downstream Port 1 -- client + * + * A fake pci_ops answers the ACS Control, Egress Control Vector and LNKCAP + * reads for the two downstream ports, so the ACS Egress Control evaluated= at + * the path divergence (Downstream Port 0 targeting Downstream Port 1) dec= ides + * the mapping without any real hardware. + */ + +struct acs_dn_cfg { + u16 acs_ctrl; /* ACS Control register value */ + u8 port; /* this port's LNKCAP Port Number */ + u32 egress[8]; /* Egress Control Vector (256 bits) */ +}; + +struct acs_fabric { + struct pci_dev *provider; + struct pci_dev *client; + struct pci_dev *dn0; /* Downstream Port 0 (provider side) */ + struct pci_dev *dn1; /* Downstream Port 1 (client side) */ + struct acs_dn_cfg dn0_cfg; + struct acs_dn_cfg dn1_cfg; +}; + +static void acs_dn_read(struct pci_dev *dn, struct acs_dn_cfg *c, + int where, int size, u32 *val) +{ + int vec =3D dn->acs_cap + PCI_ACS_EGRESS_CTL_V; + + if (size =3D=3D 4 && where =3D=3D dn->pcie_cap + PCI_EXP_LNKCAP) + *val =3D FIELD_PREP(PCI_EXP_LNKCAP_PN, c->port); + else if (dn->acs_cap && size =3D=3D 2 && where =3D=3D dn->acs_cap + PCI_A= CS_CTRL) + *val =3D c->acs_ctrl; + else if (dn->acs_cap && size =3D=3D 4 && + where >=3D vec && where < vec + (int)sizeof(c->egress)) + *val =3D c->egress[(where - vec) / 4]; +} + +static int acs_fabric_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct acs_fabric *f =3D bus->sysdata; + + *val =3D 0; + if (bus =3D=3D f->dn0->bus && devfn =3D=3D f->dn0->devfn) + acs_dn_read(f->dn0, &f->dn0_cfg, where, size, val); + else if (bus =3D=3D f->dn1->bus && devfn =3D=3D f->dn1->devfn) + acs_dn_read(f->dn1, &f->dn1_cfg, where, size, val); + return PCIBIOS_SUCCESSFUL; +} + +static int acs_fabric_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops acs_fabric_ops =3D { + .read =3D acs_fabric_read, + .write =3D acs_fabric_write, +}; + +static struct pci_bus *acs_add_bus(struct kunit *test, struct pci_bus *par= ent, + struct pci_dev *self, u8 nr, void *sysdata) +{ + struct pci_bus *bus =3D kunit_kzalloc(test, sizeof(*bus), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, bus); + bus->parent =3D parent; + bus->self =3D self; + bus->number =3D nr; + bus->ops =3D &acs_fabric_ops; + bus->sysdata =3D sysdata; + INIT_LIST_HEAD(&bus->devices); + return bus; +} + +static struct pci_dev *acs_add_dev(struct kunit *test, struct pci_bus *bus, + unsigned int devfn, int pcie_type) +{ + struct pci_dev *dev =3D kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, dev); + dev->bus =3D bus; + dev->devfn =3D devfn; + dev->pcie_cap =3D 0x40; + dev->pcie_flags_reg =3D ACS_TEST_PCIE_FLAGS(pcie_type); + list_add_tail(&dev->bus_list, &bus->devices); + return dev; +} + +static void acs_build_fabric(struct kunit *test, struct acs_fabric *f) +{ + struct pci_bus *bus0, *bus1, *bus2, *bus3, *bus4; + struct pci_dev *rootport, *swup; + struct pci_host_bridge *host; + + host =3D kunit_kzalloc(test, sizeof(*host), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, host); + + bus0 =3D acs_add_bus(test, NULL, NULL, 0, f); /* root bus */ + /* The Root Port doubles as the whitelisted host-bridge device. */ + rootport =3D acs_add_dev(test, bus0, PCI_DEVFN(0, 0), + PCI_EXP_TYPE_ROOT_PORT); + rootport->vendor =3D PCI_VENDOR_ID_GOOGLE; + rootport->device =3D 0x1234; + host->bus =3D bus0; + bus0->bridge =3D &host->dev; + + bus1 =3D acs_add_bus(test, bus0, rootport, 1, f); + swup =3D acs_add_dev(test, bus1, PCI_DEVFN(0, 0), PCI_EXP_TYPE_UPSTREAM); + + bus2 =3D acs_add_bus(test, bus1, swup, 2, f); + f->dn0 =3D acs_add_dev(test, bus2, PCI_DEVFN(0, 0), PCI_EXP_TYPE_DOWNSTRE= AM); + f->dn1 =3D acs_add_dev(test, bus2, PCI_DEVFN(1, 0), PCI_EXP_TYPE_DOWNSTRE= AM); + + bus3 =3D acs_add_bus(test, bus2, f->dn0, 3, f); + f->provider =3D acs_add_dev(test, bus3, PCI_DEVFN(0, 0), + PCI_EXP_TYPE_ENDPOINT); + + bus4 =3D acs_add_bus(test, bus2, f->dn1, 4, f); + f->client =3D acs_add_dev(test, bus4, PCI_DEVFN(0, 0), + PCI_EXP_TYPE_ENDPOINT); +} + +static enum pci_p2pdma_map_type acs_walk_map(struct acs_fabric *f) +{ + int dist; + + return calc_map_type_and_dist(f->provider, f->client, &dist, false); +} + +static void acs_walk_bus_addr_test(struct kunit *test) +{ + struct acs_fabric f =3D {}; + + acs_build_fabric(test, &f); + /* No ACS on the path: peer-to-peer is allowed directly. */ + KUNIT_EXPECT_EQ(test, acs_walk_map(&f), PCI_P2PDMA_MAP_BUS_ADDR); +} + +static void acs_walk_ec_violation_test(struct kunit *test) +{ + struct acs_fabric f =3D {}; + + acs_build_fabric(test, &f); + /* + * Downstream Port 0 has Egress Control enabled with the vector bit for + * the client's Downstream Port 1 set and Request Redirect clear: an ACS + * Violation, so the direct path is unusable and the request has to take + * the host-bridge route. + */ + f.dn0->acs_cap =3D 0x100; + f.dn0->acs_capabilities =3D PCI_ACS_EC | (64 << 8); + f.dn0_cfg.acs_ctrl =3D PCI_ACS_EC; + f.dn1_cfg.port =3D 5; + f.dn0_cfg.egress[0] =3D BIT(5); + + KUNIT_EXPECT_EQ(test, acs_walk_map(&f), + PCI_P2PDMA_MAP_THRU_HOST_BRIDGE); +} + +static void acs_walk_ec_vector_clear_test(struct kunit *test) +{ + struct acs_fabric f =3D {}; + + acs_build_fabric(test, &f); + /* Egress Control enabled but the vector bit for the peer is clear. */ + f.dn0->acs_cap =3D 0x100; + f.dn0->acs_capabilities =3D PCI_ACS_EC | (64 << 8); + f.dn0_cfg.acs_ctrl =3D PCI_ACS_EC; + f.dn1_cfg.port =3D 5; /* egress vector left all-zero */ + + KUNIT_EXPECT_EQ(test, acs_walk_map(&f), PCI_P2PDMA_MAP_BUS_ADDR); +} + +static void acs_walk_thru_host_bridge_test(struct kunit *test) +{ + struct acs_fabric f =3D {}; + + acs_build_fabric(test, &f); + /* Request Redirect set: traffic is redirected up to the host bridge. */ + f.dn0->acs_cap =3D 0x100; + f.dn0->acs_capabilities =3D PCI_ACS_RR; + f.dn0_cfg.acs_ctrl =3D PCI_ACS_RR; + + /* The Google root port is whitelisted, so the host-bridge path is OK. */ + KUNIT_EXPECT_EQ(test, acs_walk_map(&f), + PCI_P2PDMA_MAP_THRU_HOST_BRIDGE); +} + static struct kunit_case pci_acs_test_cases[] =3D { KUNIT_CASE_PARAM(pci_acs_p2pdma_decision_test, acs_decision_gen_params), KUNIT_CASE_PARAM(pci_acs_egress_port_valid_test, egress_valid_gen_params), @@ -359,6 +559,10 @@ static struct kunit_case pci_acs_test_cases[] =3D { KUNIT_CASE(acs_egress_pdev_pcie_bridge_test), KUNIT_CASE(acs_egress_target_pcie_bridge_test), KUNIT_CASE(acs_egress_root_port_test), + KUNIT_CASE(acs_walk_bus_addr_test), + KUNIT_CASE(acs_walk_ec_violation_test), + KUNIT_CASE(acs_walk_ec_vector_clear_test), + KUNIT_CASE(acs_walk_thru_host_bridge_test), {} }; =20 --=20 2.55.0 From nobody Fri Oct 2 01:10:34 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 BD85A44E652; Thu, 6 Aug 2026 11:26:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015587; cv=none; b=fOx8OyWB2Pzn6KLgW2jh6Cx+RbhLdKWXJWVhWWCk8qB7drudlZfxCDdhjxNv2Zr+NpmYxQGyZg8qm45iTKKTCM/4ZbT180nUn2rkLA77TCHhzHv0JH7gAo0wD77PDDKGbcrroLYkxOIiOhUJOuF19SA4YGLge49KrgURvPAmPJI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1786015587; c=relaxed/simple; bh=jpyb0zXGp6TIE9MJcV8IbSL5hMgZ6UCje6nRWEOHUFY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=fwM0l1aDEKsJhfWO1P8VA7ljEhFRbLLgL6h+ot3gATdsYu89SY28zU1wZjhcO6fyLBHGoryFOEk1wpXs46rKCqaxovBxXwKqSzk/OzV/j2R8M+CvmRShU12JO3m7Am7ZKjzIKEU025ZUHvZgpZdwSMuuqEY+Su684OZZ3v3aI3w= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CCOPlHG1; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="CCOPlHG1" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2418F1F00A3A; Thu, 6 Aug 2026 11:26:23 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1786015585; bh=1wa5DouB3whHlnz+gysX/2bxWYa8g3Tko7em60mg5KE=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=CCOPlHG1EZiX/VwQ6zXlZhKst9G0xHai6YuJw7g5QFkT+u1rwIHH4rxb3Ad9B58Yy hpIsQmH+h6iUEZRz5qetMhcGFdZa5WXRVQOyPczbkHFfkvl5Kq9dxzVZPGyz49MENo ElpqBgUte7J7iT1hjedvMjwbYtg9VkQBNkIPhMvXZf1XAoxaPIMkmJaK9M22X6st1F 4DJhod1sfqWHBpVAQDR2b035P0mgOxmbTb1fW10cc5+/cJc2wh5Nn/LYvUQiZTJPU5 vDywcFeALDoRzt1EYyppVmGGRvagxuggugSgGDfBjmPSY6VWFh0sN/UlB0wRrjoKNm 0uGhJ6CsYIsXQ== From: Leon Romanovsky To: Bjorn Helgaas , Logan Gunthorpe , Jonathan Corbet , Shuah Khan , Alex Williamson Cc: linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Claude Opus 5 Subject: [PATCH v2 13/13] PCI: Add KUnit coverage for ACS isolation checks Date: Thu, 6 Aug 2026 14:24:20 +0300 Message-ID: <20260806-fix-p2p-acs-v2-13-0cec14812965@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> References: <20260806-fix-p2p-acs-v2-0-0cec14812965@nvidia.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Mailer: b4 0.15-dev-18f8f Content-Transfer-Encoding: quoted-printable From: Leon Romanovsky Direct Translated P2P and Egress Control both let a peer request reach the peer without Request Redirect, and whether Request Redirect still isolates depends further on Translation Blocking and on the flags the caller requests. Firmware owns these bits, so the combinations are not reachable on a given machine. Drive pci_acs_flags_enabled() with a fake pci_ops supplying the ACS Control register and check each combination. The function is exposed under CONFIG_KUNIT via VISIBLE_IF_KUNIT. Reviewed-by: Logan Gunthorpe Assisted-by: Claude Opus 5 Signed-off-by: Leon Romanovsky --- drivers/pci/pci.c | 4 +- drivers/pci/pci.h | 1 + drivers/pci/pci_acs_test.c | 97 ++++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 101 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c9e64003f8d2..0b5d6ad21582 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3621,7 +3621,8 @@ int pci_acs_egress_ctrl_set(struct pci_dev *pdev, str= uct pci_dev *target) } EXPORT_SYMBOL_IF_KUNIT(pci_acs_egress_ctrl_set); =20 -static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) +VISIBLE_IF_KUNIT +bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags) { bool request_redirect =3D acs_flags & PCI_ACS_RR; int pos; @@ -3660,6 +3661,7 @@ static bool pci_acs_flags_enabled(struct pci_dev *pde= v, u16 acs_flags) =20 return (ctrl & acs_flags) =3D=3D acs_flags; } +EXPORT_SYMBOL_IF_KUNIT(pci_acs_flags_enabled); =20 /** * pci_acs_enabled - test ACS against required flags for a given device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6f200d40d69e..f519ee774300 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -1060,6 +1060,7 @@ enum pci_acs_p2pdma_state { }; =20 #if IS_ENABLED(CONFIG_KUNIT) +bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags); bool pci_acs_egress_port_valid(u16 acs_caps, u8 target_port); enum pci_acs_p2pdma_state pci_acs_p2pdma_decision(u16 ctrl, bool has_targe= t, int egress); diff --git a/drivers/pci/pci_acs_test.c b/drivers/pci/pci_acs_test.c index b258fc46fcaa..b01e6ca6c7d9 100644 --- a/drivers/pci/pci_acs_test.c +++ b/drivers/pci/pci_acs_test.c @@ -120,6 +120,102 @@ static void pci_acs_egress_port_valid_test(struct kun= it *test) c->expect); } =20 +/* + * pci_acs_flags_enabled(): Direct Translated P2P and Egress Control both = let a + * peer request reach the peer without Request Redirect, so neither may re= port + * isolation. Translation Blocking rejects a Translated Request before it= is + * routed, which restores the Request Redirect guarantee. A fake pci_ops + * supplies the ACS Control register. + */ + +/* Flags an IOMMU asks for; see REQ_ACS_FLAGS in drivers/iommu/iommu.c. */ +#define ACS_REQ_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) +#define ACS_ALL_CAPS (PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR | PCI_ACS_CR | \ + PCI_ACS_UF | PCI_ACS_EC | PCI_ACS_DT) +#define ACS_TEST_CAP 0x100 + +struct acs_ctrl_cfg { + unsigned int devfn; + u16 ctrl; +}; + +static int acs_ctrl_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct acs_ctrl_cfg *cfg =3D bus->sysdata; + + *val =3D 0; + if (devfn =3D=3D cfg->devfn && size =3D=3D 2 && + where =3D=3D ACS_TEST_CAP + PCI_ACS_CTRL) + *val =3D cfg->ctrl; + return PCIBIOS_SUCCESSFUL; +} + +static int acs_ctrl_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops acs_ctrl_ops =3D { + .read =3D acs_ctrl_read, + .write =3D acs_ctrl_write, +}; + +struct acs_isolation_case { + const char *desc; + u16 ctrl; /* ACS Control register */ + u16 req; /* flags the caller asks for */ + bool expect; /* isolation reported? */ +}; + +static const struct acs_isolation_case acs_isolation_cases[] =3D { + { "plain_rr", ACS_REQ_FLAGS, ACS_REQ_FLAGS, true }, + /* Direct Translated P2P bypasses Request Redirect ... */ + { "dt", ACS_REQ_FLAGS | PCI_ACS_DT, ACS_REQ_FLAGS, false }, + /* ... unless Translation Blocking rejects the Translated Request. */ + { "dt_tb", ACS_REQ_FLAGS | PCI_ACS_DT | PCI_ACS_TB, ACS_REQ_FLAGS, true }, + { "tb_only", ACS_REQ_FLAGS | PCI_ACS_TB, ACS_REQ_FLAGS, true }, + /* Egress Control can override Request Redirect as well. */ + { "ec", ACS_REQ_FLAGS | PCI_ACS_EC, ACS_REQ_FLAGS, false }, + { "ec_dt_tb", ACS_REQ_FLAGS | PCI_ACS_EC | PCI_ACS_DT | PCI_ACS_TB, + ACS_REQ_FLAGS, false }, + /* Without Request Redirect requested, neither bit is consulted. */ + { "no_rr_dt", PCI_ACS_SV | PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_DT, + PCI_ACS_SV | PCI_ACS_CR | PCI_ACS_UF, true }, + /* A control bit the caller asked for is simply missing. */ + { "rr_not_enabled", PCI_ACS_SV | PCI_ACS_CR | PCI_ACS_UF, ACS_REQ_FLAGS, + false }, +}; + +static void acs_isolation_desc(const struct acs_isolation_case *c, char *d= esc) +{ + strscpy(desc, c->desc, KUNIT_PARAM_DESC_SIZE); +} + +KUNIT_ARRAY_PARAM(acs_isolation, acs_isolation_cases, acs_isolation_desc); + +static void pci_acs_flags_enabled_test(struct kunit *test) +{ + const struct acs_isolation_case *c =3D test->param_value; + struct acs_ctrl_cfg cfg =3D { .devfn =3D PCI_DEVFN(0, 0), .ctrl =3D c->ct= rl }; + struct pci_bus *bus =3D kunit_kzalloc(test, sizeof(*bus), GFP_KERNEL); + struct pci_dev *pdev =3D kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, bus); + KUNIT_ASSERT_NOT_NULL(test, pdev); + + bus->ops =3D &acs_ctrl_ops; + bus->sysdata =3D &cfg; + + pdev->bus =3D bus; + pdev->devfn =3D cfg.devfn; + pdev->acs_cap =3D ACS_TEST_CAP; + pdev->acs_capabilities =3D ACS_ALL_CAPS; + + KUNIT_EXPECT_EQ(test, pci_acs_flags_enabled(pdev, c->req), c->expect); +} + /* * pci_acs_egress_ctrl_set(): drive the config-space reads with a fake pci= _ops * so the Egress Control Vector lookup is exercised without real hardware = -- @@ -549,6 +645,7 @@ static void acs_walk_thru_host_bridge_test(struct kunit= *test) static struct kunit_case pci_acs_test_cases[] =3D { KUNIT_CASE_PARAM(pci_acs_p2pdma_decision_test, acs_decision_gen_params), KUNIT_CASE_PARAM(pci_acs_egress_port_valid_test, egress_valid_gen_params), + KUNIT_CASE_PARAM(pci_acs_flags_enabled_test, acs_isolation_gen_params), KUNIT_CASE(acs_egress_vector_bit_set_test), KUNIT_CASE(acs_egress_vector_bit_clear_test), KUNIT_CASE(acs_egress_high_port_index_test), --=20 2.55.0