From nobody Fri Oct 2 10:08:32 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 C81493BBA05; Sun, 2 Aug 2026 15:10:08 +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=1785683412; cv=none; b=SldLymsmZLaIVaOauCQtj7FthUyj5aP5yoyHoouau4CLEsMEn2OZxWmw8D9Cj3YLmPMQkfPibFzY5Ko+Du0lDgUwbEnjM79gMfPOJGRbREvIbM+oCpLyiXiMerd/kiOtAyeOsdFAqoEiShpCNNo+tJ8FrhOFPKAbtkUu9Sa7l/I= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683412; c=relaxed/simple; bh=99bqr/k13EcQwJy2ZnAjwyVCotEV4X6Mc7Ew0quyT3E=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ILbn9gkjInqZ8Lt1sDJxjowCtZP1A0609vJw4RCULv4pecxVEOf3emw1wH0H0orQhCrZm5ihougsPOIXbfDwswQw9xM6NPbP5mJi15UCzViDDDY7LiQnV/BdWC7UrEb+f18xnQKWiNJB6xm7MKNRqHPM3VMqvIvh9OQ2m+4ie+o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=CUJG6U3D; 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="CUJG6U3D" Received: by smtp.kernel.org (Postfix) with ESMTPSA id EEF8C1F000E9; Sun, 2 Aug 2026 15:10:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683404; bh=TVTa7+WmRi9ddbsfIzMHeqJOKPOtkT6w8ehz3GN72jY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=CUJG6U3DXiGLW6TDTAPgchsJUsZ9BUOw18WDgIYLMnEcPSndn8/HFWyb04/GLBQbX UPFjmgBmZuNFNOEgPh/yZw8+e8sXv0INzOjj8beWnvYogIE7jcXipmudIzcMpR5f4G VgPSQHCiAAbgqHnoeiClnBI4/TqpW0Btr0GL/mCwlAWRCrGjjEYTUMQEUqqk3rUjwn Y5z9+FqFFrmusblAPcCWqx+H3sZHk0f/71gD+7dqX3kRAIanrz3nEMjzHlr9JRhMXF 8XmCD0o/Sru50A1hvlU2f+EiUStASqzDj/8vJha3k/PV5qN124Wm968kFhGOPDNHR1 Jh8x64CrOH2Nw== 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 01/13] PCI/P2PDMA: Safely terminate ACS redirect lists Date: Sun, 2 Aug 2026 18:09:39 +0300 Message-ID: <20260802-fix-p2p-acs-v1-1-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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") Signed-off-by: Leon Romanovsky Reviewed-by: Logan Gunthorpe --- 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 10:08:32 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 5D18A3B960F; Sun, 2 Aug 2026 15:10: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=1785683420; cv=none; b=ksuMWJZXM/afI/Ja977Nqx9Ge4Di3BdOavsowKlC4XzC+kB4cy+mCxMiLBH23Og0SdkDhnVELJZqn5D8puP0Q22rFEPFdeKsAqTU8sHNt5TS+tJutmGSHqXepYeDZNnFzpfdNkTRMWDRhgKeWU1Jfy0hdPju2K2WlEJU1D4pFxk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683420; c=relaxed/simple; bh=lPC11LfxiJlc/7y0U5OYMCkPeg80+7fdxRYVtayCHEY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lohj8mtpUUwtPQqe7Hz9MFdHGs4xhV34GS5zK+HlM88KIzsGVU7gcvhZcMVNDwl8vEj8610RaOwQa8hW3079laz76mVt0ENKt+dRinXc5SkWwPWqBhNPP+wxvymXdr/QA1/7VgTH7BC+ISgDDw/LqEFJ5EZkhEvHjaptvjdJ4zs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=lt21Kijl; 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="lt21Kijl" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1B9751F00A3D; Sun, 2 Aug 2026 15:10:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683419; bh=22HdukBMyVSh0f8dzFmZVgseWKLJtqGJOxKwgmITFtQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=lt21KijlP6UglhI744gnvVuLOrQ0LDBbvBvHPE5L5OChDxoM+vrEs9PsM6/Bu2h1Y Pn6T6nXjGcP0DuVNu23A8WD8EvPWWShtKQbghK5feKqjUxchkLkABkeRyrvnQLFPZQ SHMKTV4qbpuMIgx8A6MRjnIJLYteUYDZhOeEcqfGrEO6WWkLHLeyRtNWJMMhNCtZ8e PCpd5rhCKebRP6Szg82zW1+Gwl/J26vNO2aEqT1aK3+hSfCgcHDaLOpQadv11uOpK5 GZVXW7uVngwS4FYNYnJJm/24kK1vZXjfE7HJyXeQXK7ZOQG+w3q9lyVJl9EBgRRdg8 NcN9Q/HzuTMLA== 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 02/13] PCI/P2PDMA: Report ACS ports when the paths share no upstream bridge Date: Sun, 2 Aug 2026 18:09:40 +0300 Message-ID: <20260802-fix-p2p-acs-v1-2-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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. Signed-off-by: Leon Romanovsky Reviewed-by: Logan Gunthorpe --- 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 10:08:32 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 D1A093BE141; Sun, 2 Aug 2026 15:10: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=1785683414; cv=none; b=fV1EdRS/6f30X8jobZDeYbKJBlBEZ5CvVrI7PUk562dyTSgNVUyKfJrO+64pCmS45oOoj3IFJ9yYAvX/O+KJS9hqg2rsoUCydi4ohQGSVmNBkWBHCpZOU5Vw5tWUAZLQWMOl1CWyQPLbN4+Cp0ScbA21HJ3ukfVtfJPKA55gYN4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683414; c=relaxed/simple; bh=zD7Z3XRx/NnAXg9ZpYq2hi9Ww+d9uV3hgJk/VddNRCA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=rLX4LcCoayBnGHVOe475V0i2J8pBQrMe+/ddCP0A/WNhDmlskAGi9lGQPq2jDQWUb1ccNpqdnQYPlmcljV9Df+HY1Pqg3NG8m+40WI9xjehS6NIb7e1PPsJNr9YIX5CGzXIRPBYpyGpSai+V4Txo3KhceexFoZOor/xLBDsQwcg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FU5XJQlZ; 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="FU5XJQlZ" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 75E461F00A3A; Sun, 2 Aug 2026 15:10:08 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683409; bh=YfhVjG228lRhqeGWl3QV3O1en3dT8BOAamHtMgvZSlg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=FU5XJQlZSRogA14cxOb79WrnnN65Xqq9F8tqranThlTuu85pOZtqWUxxAn5f4wKns Inu/xU6smQ18MlaTwa4GxTLs+5CLo3MuK5nztgki+GMeDrWdI5wwSMmuhskoxKBO0A aKs7JtT6hR+vWYQoNVLKZvuWFqzjzoMeHbcWpT6mON6KxxuJfhrJaUOvFyDDILzLrT gPu2Rc5QeQASh/fgD/bCXUWvivf0KnuegbEm5x4PYMNryCfxzAy+ObZoxXgXPeFw4a LLwvmOPUqmHAZTzC3ZTNjR5euqDOTiqNhIIoxrakd4MisdvbsaZfIF7yBxZ5OhfrGg syIaNOhPZUNew== 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 03/13] PCI/P2PDMA: Document the Address Type assumption Date: Sun, 2 Aug 2026 18:09:41 +0300 Message-ID: <20260802-fix-p2p-acs-v1-3-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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. Signed-off-by: Leon Romanovsky Reviewed-by: Logan Gunthorpe --- 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 10:08:32 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 7E1573B8BA5; Sun, 2 Aug 2026 15:10:15 +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=1785683418; cv=none; b=UJAq6p5kF4cTiGahYLMf/bGGDhX86KK7sLxHQvq6P0nxDL+qWNng3feiMQJVRpiW0Hy5TkUED4N9KGhUrb9ivVGDituekBWPDsLph7efJxySfMDaB8RDmNkudN+Xyj5cZcN099Q8meIHrABHU0i9L8fJ6inXWesUT7Hdz94Cvbc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683418; c=relaxed/simple; bh=QIn51PBe4F3qbFqErtrhQVaNaMqR4v8oZ3NCV6UHk9Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=NFbVCcqXl9FhL0TN4auaeoiHU5ISaQ3afsWPYnsQ6vsqAJxJnwPCezAkdVGXcwohtO/5Wj/bwynGsP3hCXOhVnWjzospfZnag8yQx2eAMa9iKPwNUVtQ8gJyrzsZfWahCfPAsJ4mH8QlvqfwXYGWm4fR9/4hofgyh1fWUlwtzxg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=TOtlmOpc; 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="TOtlmOpc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 3645F1F000E9; Sun, 2 Aug 2026 15:10:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683414; bh=fRj143jgUPW88jFD2yowRZvlsut0lLiYHSAK2WguMUY=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=TOtlmOpcBlVphrd81vtnryZOaBNdAbbnoAxGCASntnO29xL3EDfuud7p/AC9IlRPC w+vjLz2+uZ2tJpuUJHv2WU/Wzmf/tJKf+RbFN1FAOomdRoax8wE6EPDwV3DOfgG8lT v+YYlfPnK4PKvLGI2fLKBpeEsZZVVb2s44Wq4vhoioly5jenlYARvvG3lMKoKRBZ2L 3552aE+izl8J8fmoCkAq2eyR8bUxChsHQV2e+W3fSWvbjnmgnU/TwmnReVThlATqJM JpiJC0s8wndNGrIhTktgy17gDrIjPzYlXgm7bcGrczXjXlWjhqskRPoPhRrZjO7P07 z3tZGVeUVvO9A== 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 04/13] PCI: Account for Direct Translated P2P in ACS isolation checks Date: Sun, 2 Aug 2026 18:09:42 +0300 Message-ID: <20260802-fix-p2p-acs-v1-4-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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") Signed-off-by: Leon Romanovsky Reviewed-by: Logan Gunthorpe --- 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 10:08:32 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 E72643C276B; Sun, 2 Aug 2026 15:10:43 +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=1785683446; cv=none; b=abcawK0Up4fDUc3RPR1AIqNcZljncOLoOyilUPTxtiDWPEpXXN728Un5zmBgPRV6A93VhTngopxtWtiloMtCmz+9V+xlK2rAoT/ANPwBfCUQGTf8tzBrwuPe+tNEWmjjVCkAZgFgz83KAwBNy9qFfB/6TKKxH2rGpxje9tCOl+4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683446; c=relaxed/simple; bh=0N653im5HJHG8P96HTSmH9N85ZRf9Or16D2Q4RHx3b4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VDfhJL3FBt2P8i3N+sZ6OhMndEJnPzQSWlAoEyuOwSgGGNGHqykqH0eEpfBbilf7U4CSLuvxKNNKU1exulsxhUIhAq+viwtW+jCtkQZNZYkUqUiaaE8wwS0WjAseymYTLHOpAyxJ4nPfqp63y+ttW4H88NPrSay2oJBcPi/E4Ek= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=LW7o1ArO; 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="LW7o1ArO" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 114221F000E9; Sun, 2 Aug 2026 15:10:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683442; bh=a1p9fj09WeFNNSCLnQjRDnzPphVEPHyxwGUjJ+VcZGg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=LW7o1ArOB7JtRdOUBaMt8u2A8mlJ8j/CXxXDuyAo4xGVAykwIhfoRgciHJW3XHUj8 hjL7W7LYUYGSfRSSAVFTL/Cmy7N8Pac8bR0rQoWyZZuyCK1ccRn46Iojga8XCpu0cn AtYvUIvzvb0rmstYb03Dg9Xwo8ijNNgSKFtFo0hpK0fv0crxzRF07C+yjmzoybHtKc VEQ13fLkfiyFhpcV5QT0fQS1gttFDnzNg286iwsbQDXFAFPjOTQ03IfE/q+WYhzbEX dLOukgOZQG/7hTErfQdjsNNH+xaqmzZ3oCgtGwpjOUYfqIAHngHHd4hys42r0K5V8Y ZQltMd4IZ5iIA== 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 05/13] PCI: Add ACS egress control vector accessor Date: Sun, 2 Aug 2026 18:09:43 +0300 Message-ID: <20260802-fix-p2p-acs-v1-5-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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. Signed-off-by: Leon Romanovsky Reviewed-by: Logan Gunthorpe --- 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 10:08:32 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 C15363C1D53; Sun, 2 Aug 2026 15:10:24 +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=1785683427; cv=none; b=CpXhnssObFe1gcxIL5LeEaDhwZAG+t+M87nHCV5rRK6WoNef5n+DUR1oFXrpB3A/SvOg8nkeh/59TnJKIJJkI8d2nSC80WB66K7BZIjuxhe9/SzS1EinQi10+qG5ntfS7Nfz5hi7LbS1rSHWKWpXZkWaWJ6B3TaOtxXBsd+c+a8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683427; c=relaxed/simple; bh=iwOqRKnK64gRe5K9JkBEiDSEQQsLwt8XUiJBFlR5vbE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=hPfQnuRyx5JfGHQfyfMR1j2zYhxfJfVxko6rQzCIV+eZfa5/Id9beAINeAjtzmHw63/U6ZYyQ8kR+fu1IUkGr5qalkwZM/htmYuZobWZxvbTKZ7P3AwyeXNYwayf66EysBzBV+aGto8joDosMPF0SiOLwhvNHBslkbuc3NB9UYA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=AEGdZbje; 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="AEGdZbje" Received: by smtp.kernel.org (Postfix) with ESMTPSA id E042B1F000E9; Sun, 2 Aug 2026 15:10:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683423; bh=nKBDZceAjALOvxgfRhjv6Ai/qKF7toNt+GOsnKRI+Sg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=AEGdZbjeNgJ7EGMUuPEKtmhcZDgNWCpCV+aHWdy5GE4sXp0/yt8FMqbm1/8jRZCLv D1uu3b1y7hGPRxBIAMIUFIkpdWQ5GCqmQ3Y69YBYPLuyV2t5BqzrNVRJOnY+Rx6vbX wXR/r2bvC77p0E27bnMXiKzxEDuyR3yaeK/9m8vBDrEPrv73XIoj6m5xhw2aKLFxzl 0hg24yMNikaNifSGGaNXTZBtiX49Zg282S4eK9sLI98V6XjMIk/wwDJKgV5VlXER4L I7VXMFcMH5H4EmXWCW1V35yTbH/WfU76aGAuRxXSjXgMqSqm0/Y3NuN9jSZiEH5Rs3 XmT3YOw6CkwAQ== 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 06/13] PCI: Account for ACS egress control in isolation checks Date: Sun, 2 Aug 2026 18:09:44 +0300 Message-ID: <20260802-fix-p2p-acs-v1-6-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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. Signed-off-by: Leon Romanovsky Reviewed-by: Logan Gunthorpe --- 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 10:08:32 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 ADC1A3C063F; Sun, 2 Aug 2026 15:10:28 +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=1785683431; cv=none; b=s02afwpYTa5qo/QZMC0MqQR7+1FWWRVx/ROEYlX88bhvqsvnlV5FNLnhOf/ooDuckYeQGNiAxeOk9/yCkgnMJLeXZK/vHV0gdwaSbHT18A0boy8jDhXvNfXb1Ut2bNvDXAX8MRjDFSnmNuqMLWeLN5vklUdzQLGEc6ZqlUQFO/0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683431; c=relaxed/simple; bh=3EjmAD6WZzBqrLlZgIqsHCi5B9pZSCUq4PIN3d+ZOEA=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=AHobmyRDQ/6+IbB7OZdbgVDIrtV0m0ShKIounWlaukTf3kYuvvlJxI2qz2nOl5PW9I7/k1MMf4YEDkM4Ze0NfMDlstHycSD8+e9S2reIafNacX3sQlJYyZA1PQmp4+sjZnppukCh2VBwJgZN5eoZgGaq3rA5+Og/f0pu/p8qpbY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=SXQe3SEh; 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="SXQe3SEh" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5AF771F00A3A; Sun, 2 Aug 2026 15:10:27 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683428; bh=/nM5S+RYDU9TqP+U5JEQl7dfKr6n6Rrk2qQVm2Z0OQ0=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=SXQe3SEh+wKKE273dHVypIqIBwzFRjEKRvGtUueoZ0cyAzGmfRReUKskoc5njtXeK cD0miJvq65svSkR1GRmzyn7+XOYjhutd9JiVyxX/5/3kFmMsobRMNZQRmYDVS0OZ6Y gKBauOh+ZGMh3Ntt2CdaVXuUErFZEBJ55HkhLKfCM8kD+Xz1saTIhyeSK4WBzE6M+p 9gnlSR/v3Nsu2BVKs+sKOkwPFokQIKhQV+v1MQZfhmYlIdZRXXCUFzpabtVDk7jnmp fJhy9PY5H/pn5nA3N8tAi5f17Y4tm/os8TbmKVayQP3LaVL+x6DB0RX+Kbn9Xq2caF ZcdhmFndc7X9A== 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 07/13] PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits Date: Sun, 2 Aug 2026 18:09:45 +0300 Message-ID: <20260802-fix-p2p-acs-v1-7-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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. Signed-off-by: Leon Romanovsky Reviewed-by: Logan Gunthorpe --- 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 10:08:32 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 BC6DD3C276E; Sun, 2 Aug 2026 15:10:34 +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=1785683437; cv=none; b=rP5LimIn+1uP+z15UW0J0yMmdh/95YXbI3V2BbZug1n1uK3SQcmld17IMb2BcSPjcUH+jhJvD60TXmq72lRpY/jyukTPwL/WCVuRosZ7RKjbHzLyFZ84TosILOKSLQDXnaPtHPRVVqlD9gIQTydQcijH+LxHZdXEKPmTZtkX4bA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683437; c=relaxed/simple; bh=QMhouJDYxT4X8TxHwwgn1dS95ogjeTWKr0UFTPcUi3I=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=BJOe9gJOx0VwHoAeT+rO6ELMzEy5xoC0nl8OtbkM0/cn1L4G7/AkIcuybOTJjLerIIg9zV8nXZ0ZUEsW3JSigK9HzRKmD/pmzadpUr6QesK8jcvAEDOfT3EV8h1pRGA44KylVQHqZoIHW9H6WR/kg2kUsOnuSoqGlZ01z+foszk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=RDerXNyR; 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="RDerXNyR" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 76AA41F000E9; Sun, 2 Aug 2026 15:10:31 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683433; bh=mHl+210Macd1VvJPGKNgNNvTuSw45yCNEzPtZ3Can/M=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=RDerXNyRMZ3XCSgMsxClF8lki4UvoNz0jHNHMuQ6mCZkY3RnrHDGOP9BIMA3f3vrA TrNlxnhiK4D8wGiGBpVVcPxxNi6rhnQC4ljS0mL+SZ0hKEK1rlKuyT39etywTOFJZC AcxcAe0ErcQWO28C1cNH1fbhTnUGXpLXvCfByIyuo/IgKosQl7P+/+araf1+K0NRni CaYUnCc574S2N5x/1v9DD3osjllUQb/DdkgoVUpmMzhb1CgnxnWTsGzwYKf9OdwJTp Qbd3EW8I1IHdIqGmtXl9aFwN2dx4VcZElqUazf63CG6Po/8rps49i5i+6jGB8vjhAo gMFuKbQtfESpA== 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 08/13] PCI/P2PDMA: Honor ACS egress control vectors Date: Sun, 2 Aug 2026 18:09:46 +0300 Message-ID: <20260802-fix-p2p-acs-v1-8-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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") Signed-off-by: Leon Romanovsky Reviewed-by: Logan Gunthorpe --- 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 10:08:32 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 A7DA73BB9F5; Sun, 2 Aug 2026 15:10: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=1785683443; cv=none; b=tz7SrF7HcrKWln1l8Wtj1/t0jsYqUGL70t7gs5ZT7Sm0F2TJaXYKcJIoKc5etb++vY3jI242yTUWznjE/VllgOyNdlRAYW/QO/pAdSUnc9WFqJ6JgAxh4gctiTySRocrrij2Eq0Z0wHmhrXE4G8krMVLRS3xSN233TTewUFs0hw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683443; c=relaxed/simple; bh=+TSRkZFgOzjHlqf1dhIxAq2ILzG8Xob7INfRZCiCw0s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=JG+ORq6Ie1k9p++kPI8K0yAvV0ty5JNxTRnU3ZcVYnDYRpKigLcHQKiTis0gyvCT7IMOYtFs7NspqEkVX2NwdTff9Pgkzn+LdWPqkYT3dkOrInr3Ern7u2eTVuzq21PGMSG7q05xMQVzwYJxkbEQm4ju2ugRk7MSlrh5IgqBotA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=N/WgBDtT; 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="N/WgBDtT" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 588101F00A3A; Sun, 2 Aug 2026 15:10:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683438; bh=0LbBCEMB73kHkfgnChHArhyM9WSvUEjBlEKOk1kAohQ=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=N/WgBDtTaG5ZMfmHVgiA9xQUgf6Ik+V8JrakjvuIt1DssboURTYdOx8xXuBuhst2z V2pdPynO8KKztJeN5fbKtbj49Wlv7DJb7AP8gfop4IY8QvumLIc28WmmSqq6GEC4gT XE8TzI7dDbVFAyBMhkpqdFLxkqOGBjbVfOx/cZTfTxfrpoBN4DDtHCcY6iOJdO3AY+ /UoPz8x0QGrbi1A9oYuj/nFSoEButbo+7o8P5AryQjssONbMGwT1u1XB2pCTfeXJqM 4jql/8mhpRIBmd7OlEA7BhXEa60bZuhFGuqvX/cEqjk7kzT2OLYeiTLMF9sBvxYeiS EN7PMUCfHW0qQ== 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 09/13] PCI/P2PDMA: Document ACS egress control handling Date: Sun, 2 Aug 2026 18:09:47 +0300 Message-ID: <20260802-fix-p2p-acs-v1-9-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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. Signed-off-by: Leon Romanovsky Reviewed-by: Logan Gunthorpe --- 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..85d4426847a1 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 10:08:32 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 927343C10B5; Sun, 2 Aug 2026 15:11: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=1785683463; cv=none; b=G9s/oazqStQ8rQZKH2GVILO96Kkrx3bSWAqJhg2HI7YRrdgNWTdvoGoa1QVJ8QgyNNcyafq0r+LlXNR01zhPBMspRuOuZS+SinYWfJvd8N497NcXOTL3T5Iw5GepyMJ3PhD0QFy8fn2gZrbvRCF5q6oWaqEQvHBtJiuINhOgRQQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683463; c=relaxed/simple; bh=qwh6jZxnUbzXh7mbgZahZxnI9dr4nWBqWI2OqB1Vod4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=gXWQPWVwqcOHHoN+OzHaxHEkIMC9nYwDhnKJNcar2glYt10+4NOeCiY0OB5Cx9QHBR+1kINBE2/QwDwpKyr1KXhaENHYKCjzOC1xY7x6FvbvwOTxjHtzmsEjfxWrOS0/Nw3AGkp7k1Q2eTl98IkjnZCrgoKHA9XByL9RSnqN6Os= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=d/FhfTgm; 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="d/FhfTgm" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 170801F000E9; Sun, 2 Aug 2026 15:10:58 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683459; bh=Ow/FxnGdF6cAti2yY7ccU7uxxcEhHFCFLIXiAdDVEmE=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=d/FhfTgmVoUwlvu+vRt5W4nD6YQlgI8I0nLI2qcePj0AEOFojjxKyqzVVt9bnissO vOvptpZsGb7iAIiBan3/0ZCx/EqIO3jut0hTSfPl8XOTg3T7qMEcWsyHtkAYFFEbz8 raZ3a6yGCMogU1wHpXx46YkoPjP0FMK1BtlT6CAE5hFOT7gvPf/vUQxALNiwDDomoN 9huyo7frywkKHZVfcc1cSanZbQr7JOsy/40MmbhYF2CO/+RFjpQXFQyeuqWUtZosGU lFAKgLrjTrR8qWyUE4nYAqyaWujb3ky7sSF6IFfFa0yKEKjmvLzwi05cGWkg7scY+j Wgzfr1Uxu0roA== 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 10/13] PCI/P2PDMA: Extract pure ACS routing decision helpers Date: Sun, 2 Aug 2026 18:09:48 +0300 Message-ID: <20260802-fix-p2p-acs-v1-10-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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. Signed-off-by: Leon Romanovsky Assisted-by: Claude Opus 4.8 Reviewed-by: Logan Gunthorpe --- drivers/pci/p2pdma.c | 65 +++++++++++++++++++++++++++++-------------------= ---- drivers/pci/pci.c | 26 +++++++++++++++++---- drivers/pci/pci.h | 17 ++++++++++++++ 3 files changed, 75 insertions(+), 33 deletions(-) diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index dbccc1d0c4e3..62e715a4ac84 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -491,46 +491,53 @@ 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 egress =3D 0; u16 ctrl; =20 - pos =3D pdev->acs_cap; - if (!pos) + if (!pdev->acs_cap) return PCI_ACS_P2PDMA_DIRECT; =20 - if (pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl)) + if (pci_read_config_word(pdev, pdev->acs_cap + 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 10:08:32 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 8A9023C76A0; Sun, 2 Aug 2026 15:10:48 +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=1785683452; cv=none; b=fcWcm0D6+42ISVpv1eRVvX+cOY2YeB15bvkkTXuX2Di9y8pgdjM8z3oWsd1Sp+ush6rgB5EqXb0vQyuCc+RO15NlZEgQ8yNuVHaj+1WWbaEeMUvLD/cI6UdZBNlS09Abn/hmCe3iKJKSvRbPnMNPo+zYnPovAZAlbavw/0gKpKA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683452; c=relaxed/simple; bh=WuPDjXSMvpqQrlGISIan50jAF8bSTcR4An1U+Nm1hrI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=UJxfmojSGJVOuzcNNym+Svnc9kSOBOt6Mnim8MS1qS/3RB4c//hEJmMi6elpotq0BU1Nw9tso2Y88zkux6dG/XtvC/S5bWweHWTBGP4mFSCyNgllk9KVz0V4ATZyY5DNDLHDEYHJkN4K/GHKMahZG/n8utt5wB/2B8QGnxqOgI0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=iDSD1D5n; 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="iDSD1D5n" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 53F3E1F00A3A; Sun, 2 Aug 2026 15:10:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683447; bh=xZY7w4rjuz251ejEkgvCw42iIbdDJRqXB6YpNxyjP4k=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=iDSD1D5nr/zIDCvaTi9Npth8EPJaijhvqtcIe/13O69EnpiQnHlOZNHyJLkWQGaxF 6QxYsaKqqkU5/OQuP/dBS/9QqeStpiGonXN7yvWyYca/oP4L2Qqa1J+KPJIi5/dZa6 4LHpmGWMgci/Ed5KniogmP1PRwIr9MdqtbeDyQXatnTCvTFJSx7WLFmOB+YSQ75+zR ARH1xJGjfVn7/iIs2YEAosfSQ92ke6ih7AiYb69JPyvfADpIUhpgZox030bekwz6Ip XU+EB8VfYuvsEIa6yLFOhmSktlMSC1NVrDC+MeIuqw6zUbBheBFEjXmpibNYV9OQdh 3el5b40pOsj+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, "Claude Opus 4.8" Subject: [PATCH 11/13] PCI/P2PDMA: Add KUnit tests for ACS routing decisions Date: Sun, 2 Aug 2026 18:09:49 +0300 Message-ID: <20260802-fix-p2p-acs-v1-11-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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 Signed-off-by: Leon Romanovsky Assisted-by: Claude Opus 4.8 --- 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 10:08:32 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 E9F593ACF18; Sun, 2 Aug 2026 15:10:52 +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=1785683458; cv=none; b=c7IfhD3xe0Tk1OcVM1eo0bio3sR9kSbxGI4RA7ofBwlA4dSRBKNL+Hlxe1Gk41MeYfuCyICevbthrDdJpjReMWm1RtmEm6CoYCJNqe4E7st/QjzBPlrkZ/s3Qizf5h+CfV03M1Yso5Y+knLda3RrIZIiFJdOdJXxtw0PBC43Rr8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683458; c=relaxed/simple; bh=LwZeyhOcxacfz/jw0EkaZgJGVM/F6qWIPvodZZUkrbE=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=lRWEbXx+8Gs/Ldzx4Tjh+D7FegTsCzYyaeC+1MzbcAkmibySH46tZXcJFYVqldZ8kh3W4yIxq26MHuA93qxl60FzQNUKdxzJwWKvxUeIgKwf7lNDKV98rJ7cwMpAikawsbBUv2Yjf+UzTTvLEi/kRVyMOr40X0Fp7HmAbKyrkws= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=gSfFh0V8; 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="gSfFh0V8" Received: by smtp.kernel.org (Postfix) with ESMTPSA id C2DF41F000E9; Sun, 2 Aug 2026 15:10:50 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683451; bh=ihqaYzxst/CZ/XBeCax7zwbU1QcsRWqRWCOdS9IOwAU=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=gSfFh0V8ldn8eIpQacBRQL3pcEgmMZiU9ahDePEH7818plvX+vY9qvjLViqHE6PsV 5NzHT/bMjJAPEr3gxMc5GElvN93+MOOQLisSQuitUZhrbqNLnWL/KDeGqmjKyKcREF x0+dwEXsq7ROjUEAgj3LaQUraotx9xHe3EJqtBZa3Sbzr7FjYiUXoxrUyS6mV5Sdme AYzdfN4UOEc64iJwglUYftUE+1IVw+TkHFbl0xFHt1Z9uNs2YutVL8yUcefrbl695V S6gheXNQA3zs3Ar+mtL8JgG5YWB67WAGBsOc++aUqdP7BX2dR0wU6hk9Z3VC8P1jld wExRpII0k4l+A== 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 12/13] PCI/P2PDMA: Add KUnit coverage for the ACS P2P routing walk Date: Sun, 2 Aug 2026 18:09:50 +0300 Message-ID: <20260802-fix-p2p-acs-v1-12-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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. Signed-off-by: Leon Romanovsky Assisted-by: Claude Opus 4.8 Reviewed-by: Logan Gunthorpe --- 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 62e715a4ac84..fad743032e4d 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -726,7 +726,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) { @@ -858,6 +858,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 10:08:32 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 B7FA83C279B; Sun, 2 Aug 2026 15:10:57 +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=1785683461; cv=none; b=HPLD7/F71tFB9yfWe2a54YM1DZlSXLnVIdkkIIwrTmFQqFb5A9ypfaO+H7xR1/ApYW23+Vd529UMlvpL+Nt5pPQofBYeOEMZv2w6vRjccyK0o5wypcvSSqnIXRYU4HCf4qsx8OyWCKDuad6GCIbfDrhextpae7HCv3+Ih9Oqj7s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1785683461; c=relaxed/simple; bh=v9YCQbh1WXcjgGWYaZw2f3xmV19av9VzdejVicB1I5A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=ERIRHVlZCSy7/gnUoQf5OIRUkEaWXXfnAokiUlCMB8yGVBJi3MsjglOl+gImgmrrzM1TkIm15/pgTSTFjx7vKi7Da3CAOO5z/2/Hydg6VKstQrQ2lCKq6G9lmC3ERoqk6hDS2eIBCyXUZrZsy+/bnTdBZ+Ew4JI3AyJ7uM2Bpqo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=FdqQAjVc; 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="FdqQAjVc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id F11991F00A3A; Sun, 2 Aug 2026 15:10:54 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1785683456; bh=qOm7ksSxmsxPIZ94t1yVXqXyDANLBmZmq0kcNCcHN4c=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=FdqQAjVch/AFsMiK3MXymeIi4Dy4ie/EQ+c/6vYB8vgTSfzK5q9/kDIaZPP+Bqmf/ aJ50Hry1vkrMpJrRW2+THw9ZHKQeQuNhH5c+okfxegTd8d4OYGl8kAQSTptXKFm8wH 8AAc4tf4PlmIwT8MLwcuWSLMxuTNJ1wRclABRTsFm2qSjiaR+HiKqPuBuJ+BnCaous TXRpdVV8SCm38Mpo6mtnvM5wP862gqD/jB01vZ7SnYd0zICH8nOIXPLizaoXrWjHSZ RNl3mtFCVWqWkgA3XmxleGKiYZxH59NL6JdyjNtTQ2gOBpvzAjuRB0uBsylTdhDNYp q+CwuYXgB+gfg== 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 13/13] PCI: Add KUnit coverage for ACS isolation checks Date: Sun, 2 Aug 2026 18:09:51 +0300 Message-ID: <20260802-fix-p2p-acs-v1-13-a7c5eb64fff6@nvidia.com> X-Mailer: git-send-email 2.55.0 In-Reply-To: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com> References: <20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@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. Signed-off-by: Leon Romanovsky Assisted-by: Claude Opus 5 Reviewed-by: Logan Gunthorpe --- 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