* [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling
@ 2026-08-02 15:09 Leon Romanovsky
2026-08-02 15:09 ` [PATCH 01/13] PCI/P2PDMA: Safely terminate ACS redirect lists Leon Romanovsky
` (12 more replies)
0 siblings, 13 replies; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc, Claude Opus 4.8
PCI P2PDMA treats any enabled ACS P2P Egress Control bit as an upstream
redirect. PCIe r7.0, sec 6.12.3, table 6-11 says the Egress Control
Vector bit for the target port decides instead: a clear bit routes a peer
request directly, regardless of P2P Request Redirect. Firmware can
therefore enable Egress Control with a permissive vector while Linux
incorrectly rejects a valid direct P2P path.
Table 6-11, where E is ACS P2P Egress Control Enable, R is ACS P2P
Request Redirect Enable and V the Egress Control Vector bit for the
target port:
E R V Required Handling for Peer-to-Peer Requests
- - - ------------------------------------------
0 0 x Route directly to peer-to-peer target
0 1 x Redirect Upstream
1 0 1 Handle as an ACS Violation
1 0 0 Route directly to peer-to-peer target
1 1 1 Redirect Upstream
1 1 0 Route directly to peer-to-peer target
P2P Completion Redirect lies outside this table and also forces
host-bridge routing.
The same interaction affects target-independent ACS isolation checks.
Request Redirect does not guarantee that peer requests are forwarded
upstream while Egress Control is enabled because a clear vector bit
overrides it. Such checks cannot identify every potential target, so
treat Request Redirect as ineffective while Egress Control is enabled,
which merges the affected devices into one IOMMU group.
ACS Direct Translated P2P routes a Request carrying a Translated address
to the peer regardless of Request Redirect and Egress Control, so it
voids the same guarantee unless Translation Blocking rejects the Request
first.
Two pre-existing gaps come first. A provider and a client below different
Root Ports share no upstream bridge, so the walk reached the host-bridge
route without recording a single ACS port and left the operator no
pci=disable_acs_redir= hint to act on. The routing analysis also covers
only Requests carrying an Untranslated address; ACS Direct Translated P2P
overrides those controls, so that scope is now written down rather than
implied.
It is nearly impossible to test all possible combinations due to limited
hardware availability, so I added KUnit coverage for ACS routing
decisions, isolation checks, Egress Control Vector lookups, and
provider-to-client path traversal over a fabricated PCIe fabric.
Disclaimer:
All patches were prepared with AI assistance, with a significant
difference between the code changes and the KUnit tests. The code
changes were thoroughly reviewed and rewritten.
In contrast, the KUnit patches were produced entirely by AI with
minimal human interaction, and multiple AI tools (Claude, Codex,
and Gemini) with frontier models were used to verify that the tests
comply with the PCI specification.
Thanks
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
Leon Romanovsky (13):
PCI/P2PDMA: Safely terminate ACS redirect lists
PCI/P2PDMA: Report ACS ports when the paths share no upstream bridge
PCI/P2PDMA: Document the Address Type assumption
PCI: Account for Direct Translated P2P in ACS isolation checks
PCI: Add ACS egress control vector accessor
PCI: Account for ACS egress control in isolation checks
PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits
PCI/P2PDMA: Honor ACS egress control vectors
PCI/P2PDMA: Document ACS egress control handling
PCI/P2PDMA: Extract pure ACS routing decision helpers
PCI/P2PDMA: Add KUnit tests for ACS routing decisions
PCI/P2PDMA: Add KUnit coverage for the ACS P2P routing walk
PCI: Add KUnit coverage for ACS isolation checks
Documentation/admin-guide/kernel-parameters.txt | 9 +-
Documentation/driver-api/pci/p2pdma.rst | 14 +
drivers/pci/Kconfig | 15 +
drivers/pci/Makefile | 1 +
drivers/pci/p2pdma.c | 144 +++--
drivers/pci/pci.c | 102 +++-
drivers/pci/pci.h | 23 +
drivers/pci/pci_acs_test.c | 674 ++++++++++++++++++++++++
drivers/pci/quirks.c | 15 +-
9 files changed, 951 insertions(+), 46 deletions(-)
---
base-commit: 43598807f71ac1c9164f26004acf2496d4038daf
change-id: 20260713-fix-p2p-acs-725f8dd7b0e8
Best regards,
--
Leon Romanovsky <leonro@nvidia.com>
^ permalink raw reply [flat|nested] 27+ messages in thread
* [PATCH 01/13] PCI/P2PDMA: Safely terminate ACS redirect lists
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:26 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 02/13] PCI/P2PDMA: Report ACS ports when the paths share no upstream bridge Leon Romanovsky
` (11 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
---
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, struct pci_dev *client,
}
if (verbose) {
- acs_list.buffer[acs_list.len-1] = 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] = '\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=disable_acs_redir=%s\n",
- acs_list.buffer);
+ seq_buf_str(&acs_list));
}
acs_redirects = true;
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 02/13] PCI/P2PDMA: Report ACS ports when the paths share no upstream bridge
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
2026-08-02 15:09 ` [PATCH 01/13] PCI/P2PDMA: Safely terminate ACS redirect lists Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:34 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 03/13] PCI/P2PDMA: Document the Address Type assumption Leon Romanovsky
` (10 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc
From: Leon Romanovsky <leonro@nvidia.com>
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=disable_acs_redir= hint. The mapping type is unchanged.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
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 = PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
struct pci_dev *a = provider, *b = client, *bb;
+ bool no_common_upstream = false;
bool acs_redirects = false;
struct pci_p2pdma *p2pdma;
struct seq_buf acs_list;
@@ -740,8 +741,12 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
dist_a++;
}
- *dist = 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 = true;
check_b_path_acs:
bb = b;
@@ -761,6 +766,9 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
*dist = dist_a + dist_b;
if (!acs_cnt) {
+ if (no_common_upstream)
+ goto map_through_host_bridge;
+
map_type = PCI_P2PDMA_MAP_BUS_ADDR;
goto done;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 03/13] PCI/P2PDMA: Document the Address Type assumption
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
2026-08-02 15:09 ` [PATCH 01/13] PCI/P2PDMA: Safely terminate ACS redirect lists Leon Romanovsky
2026-08-02 15:09 ` [PATCH 02/13] PCI/P2PDMA: Report ACS ports when the paths share no upstream bridge Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:26 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 04/13] PCI: Account for Direct Translated P2P in ACS isolation checks Leon Romanovsky
` (9 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
---
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.
+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 another
PCIe root port, or routed internally to the SOC.
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 04/13] PCI: Account for Direct Translated P2P in ACS isolation checks
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (2 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 03/13] PCI/P2PDMA: Document the Address Type assumption Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:45 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 05/13] PCI: Add ACS egress control vector accessor Leon Romanovsky
` (8 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
---
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)
static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
{
+ bool request_redirect = acs_flags & PCI_ACS_RR;
int pos;
u16 ctrl;
@@ -3562,6 +3563,15 @@ static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
acs_flags &= (pdev->acs_capabilities | PCI_ACS_EC);
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) == acs_flags;
}
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)
static int pci_quirk_intel_spt_pch_acs(struct pci_dev *dev, u16 acs_flags)
{
+ bool request_redirect = acs_flags & PCI_ACS_RR;
int pos;
u32 cap, ctrl;
@@ -4994,6 +4995,10 @@ static int pci_quirk_intel_spt_pch_acs(struct pci_dev *dev, u16 acs_flags)
pci_read_config_dword(dev, pos + INTEL_SPT_ACS_CTRL, &ctrl);
+ /* 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);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 05/13] PCI: Add ACS egress control vector accessor
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (3 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 04/13] PCI: Account for Direct Translated P2P in ACS isolation checks Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:39 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 06/13] PCI: Account for ACS egress control in isolation checks Leon Romanovsky
` (7 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
---
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)
}
}
+/*
+ * 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 = pci_pcie_type(dev);
+
+ return type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == 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 = pcie_capability_read_dword(target, PCI_EXP_LNKCAP, &lnkcap);
+ if (ret)
+ return pcibios_err_to_errno(ret);
+
+ target_port = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
+ vector_size = pdev->acs_capabilities >> 8;
+
+ /* An Egress Control Vector Size of 0 encodes 256 bits. */
+ if (vector_size && target_port >= vector_size)
+ return -ERANGE;
+
+ ret = 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 = 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_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);
#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);
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 06/13] PCI: Account for ACS egress control in isolation checks
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (4 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 05/13] PCI: Add ACS egress control vector accessor Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:50 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 07/13] PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits Leon Romanovsky
` (6 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
---
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 *pdev, u16 acs_flags)
*/
acs_flags &= (pdev->acs_capabilities | PCI_ACS_EC);
- pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl);
+ if (pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl))
+ return false;
/*
* Direct Translated P2P routes a Translated Request to the peer
@@ -3630,6 +3631,15 @@ static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
if (request_redirect && (ctrl & PCI_ACS_DT) && !(ctrl & PCI_ACS_TB))
return false;
+ /*
+ * 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) == acs_flags;
}
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_dev *dev, u16 acs_flags)
return -ENOTTY;
/* 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 &= (cap | PCI_ACS_EC);
- 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;
/* Direct Translated P2P may bypass Request Redirect. */
if (request_redirect && (ctrl & PCI_ACS_DT) && !(ctrl & PCI_ACS_TB))
return 0;
+ /* 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);
}
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 07/13] PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (5 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 06/13] PCI: Account for ACS egress control in isolation checks Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:50 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 08/13] PCI/P2PDMA: Honor ACS egress control vectors Leon Romanovsky
` (5 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
---
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 <linux/seq_buf.h>
#include <linux/xarray.h>
+#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 device *dev)
return NULL;
}
-/*
- * 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;
pos = pdev->acs_cap;
if (!pos)
- return 0;
+ return PCI_ACS_P2PDMA_DIRECT;
- 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;
- 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;
- 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;
}
static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev)
@@ -722,7 +743,8 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
while (a) {
dist_b = 0;
- if (pci_bridge_has_acs_redir(a)) {
+ if (pci_acs_p2pdma_state(a, NULL) ==
+ 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 == bb)
break;
- if (pci_bridge_has_acs_redir(bb)) {
+ if (pci_acs_p2pdma_state(bb, NULL) ==
+ PCI_ACS_P2PDMA_REDIRECT) {
seq_buf_print_bus_devfn(&acs_list, bb);
acs_cnt++;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 08/13] PCI/P2PDMA: Honor ACS egress control vectors
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (6 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 07/13] PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:57 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 09/13] PCI/P2PDMA: Document ACS egress control handling Leon Romanovsky
` (4 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
---
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 device *dev)
enum pci_acs_p2pdma_state {
PCI_ACS_P2PDMA_DIRECT,
PCI_ACS_P2PDMA_REDIRECT,
+ PCI_ACS_P2PDMA_NOT_SUPPORTED,
};
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;
pos = pdev->acs_cap;
@@ -507,26 +508,26 @@ pci_acs_p2pdma_state(struct pci_dev *pdev, struct pci_dev *target)
return PCI_ACS_P2PDMA_DIRECT;
if (pci_read_config_word(pdev, pos + PCI_ACS_CTRL, &ctrl))
- return PCI_ACS_P2PDMA_REDIRECT;
+ return PCI_ACS_P2PDMA_NOT_SUPPORTED;
- 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;
/*
- * 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 = 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;
@@ -707,9 +708,9 @@ static unsigned long map_types_idx(struct pci_dev *client)
* 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, struct pci_dev *client,
int *dist, bool verbose)
{
enum pci_p2pdma_map_type map_type = PCI_P2PDMA_MAP_THRU_HOST_BRIDGE;
- struct pci_dev *a = provider, *b = client, *bb;
+ struct pci_dev *a = provider, *b = client, *bb, *target;
+ struct pci_dev *a_child = NULL, *b_child = NULL;
+ struct pci_dev *acs_unsupported = NULL;
+ enum pci_acs_p2pdma_state state;
bool no_common_upstream = false;
bool acs_redirects = false;
struct pci_p2pdma *p2pdma;
struct seq_buf acs_list;
- int acs_cnt = 0;
+ int acs_redirect_cnt = 0;
int dist_a = 0;
int dist_b = 0;
char buf[128];
@@ -742,23 +746,19 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
*/
while (a) {
dist_b = 0;
-
- if (pci_acs_p2pdma_state(a, NULL) ==
- PCI_ACS_P2PDMA_REDIRECT) {
- seq_buf_print_bus_devfn(&acs_list, a);
- acs_cnt++;
- }
-
+ b_child = NULL;
bb = b;
while (bb) {
if (a == bb)
- goto check_b_path_acs;
+ goto check_paths_acs;
+ b_child = bb;
bb = pci_upstream_bridge(bb);
dist_b++;
}
+ a_child = a;
a = pci_upstream_bridge(a);
dist_a++;
}
@@ -770,25 +770,44 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
*/
no_common_upstream = true;
-check_b_path_acs:
- bb = b;
+check_paths_acs:
+ *dist = dist_a + dist_b;
+ bb = provider;
while (bb) {
+ target = bb == a_child ? b_child : NULL;
+ state = pci_acs_p2pdma_state(bb, target);
+ if (state != PCI_ACS_P2PDMA_DIRECT) {
+ seq_buf_print_bus_devfn(&acs_list, bb);
+ if (state == PCI_ACS_P2PDMA_REDIRECT)
+ acs_redirect_cnt++;
+ else if (!acs_unsupported)
+ acs_unsupported = bb;
+ }
+
if (a == bb)
break;
- if (pci_acs_p2pdma_state(bb, NULL) ==
- PCI_ACS_P2PDMA_REDIRECT) {
+ bb = pci_upstream_bridge(bb);
+ }
+
+ bb = client;
+
+ while (bb && a != bb) {
+ target = bb == b_child ? a_child : NULL;
+ state = pci_acs_p2pdma_state(bb, target);
+ if (state != PCI_ACS_P2PDMA_DIRECT) {
seq_buf_print_bus_devfn(&acs_list, bb);
- acs_cnt++;
+ if (state == PCI_ACS_P2PDMA_REDIRECT)
+ acs_redirect_cnt++;
+ else if (!acs_unsupported)
+ acs_unsupported = bb;
}
bb = pci_upstream_bridge(bb);
}
- *dist = dist_a + dist_b;
-
- if (!acs_cnt) {
+ if (!acs_unsupported && !acs_redirect_cnt) {
if (no_common_upstream)
goto map_through_host_bridge;
@@ -796,13 +815,21 @@ calc_map_type_and_dist(struct pci_dev *provider, struct pci_dev *client,
goto done;
}
+ /*
+ * 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] = '\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=disable_acs_redir=%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=disable_acs_redir=%s\n",
seq_buf_str(&acs_list));
}
acs_redirects = true;
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 09/13] PCI/P2PDMA: Document ACS egress control handling
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (7 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 08/13] PCI/P2PDMA: Honor ACS egress control vectors Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:51 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 10/13] PCI/P2PDMA: Extract pure ACS routing decision helpers Leon Romanovsky
` (3 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc
From: Leon Romanovsky <leonro@nvidia.com>
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=disable_acs_redir= clears P2P Request Redirect,
Completion Redirect, and Egress Control.
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
---
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/Documentation/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=<pci_dev>[; ...]
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=
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.
+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 enabled.
+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
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 10/13] PCI/P2PDMA: Extract pure ACS routing decision helpers
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (8 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 09/13] PCI/P2PDMA: Document ACS egress control handling Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 15:55 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 11/13] PCI/P2PDMA: Add KUnit tests for ACS routing decisions Leon Romanovsky
` (2 subsequent siblings)
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc, Claude Opus 4.8
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
Assisted-by: Claude Opus 4.8 <noreply@anthropic.com>
---
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 device *dev)
return NULL;
}
-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 @ctrl,
+ * 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 divergence).
+ * There, a set vector bit redirects the request only when Request Redirect is
+ * set; with Request Redirect clear it is an ACS Violation. A clear vector 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);
static enum pci_acs_p2pdma_state
pci_acs_p2pdma_state(struct pci_dev *pdev, struct pci_dev *target)
{
- int pos, ret;
+ int egress = 0;
u16 ctrl;
- pos = pdev->acs_cap;
- if (!pos)
+ if (!pdev->acs_cap)
return PCI_ACS_P2PDMA_DIRECT;
- 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;
- /* 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 = pci_acs_egress_ctrl_set(pdev, target);
- /*
- * 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 = 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);
}
static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev)
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 == PCI_EXP_TYPE_DOWNSTREAM;
}
+/**
+ * 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 = 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 pci_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, struct pci_dev *target)
return pcibios_err_to_errno(ret);
target_port = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
- vector_size = pdev->acs_capabilities >> 8;
- /* An Egress Control Vector Size of 0 encodes 256 bits. */
- if (vector_size && target_port >= vector_size)
+ if (!pci_acs_egress_port_valid(pdev->acs_capabilities, target_port))
return -ERANGE;
ret = pci_read_config_dword(pdev,
@@ -3602,6 +3619,7 @@ int pci_acs_egress_ctrl_set(struct pci_dev *pdev, struct pci_dev *target)
return !!(vector & BIT(target_port % 32));
}
+EXPORT_SYMBOL_IF_KUNIT(pci_acs_egress_ctrl_set);
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
+#include <kunit/visibility.h>
#include <linux/align.h>
#include <linux/bitfield.h>
#include <linux/pci.h>
@@ -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_target,
+ 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);
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 11/13] PCI/P2PDMA: Add KUnit tests for ACS routing decisions
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (9 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 10/13] PCI/P2PDMA: Extract pure ACS routing decision helpers Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 16:02 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 12/13] PCI/P2PDMA: Add KUnit coverage for the ACS P2P routing walk Leon Romanovsky
2026-08-02 15:09 ` [PATCH 13/13] PCI: Add KUnit coverage for ACS isolation checks Leon Romanovsky
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc, Claude Opus 4.8
From: Leon Romanovsky <leonro@nvidia.com>
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=y
CONFIG_PCI=y
CONFIG_ZONE_DEVICE=y
CONFIG_MEMORY_HOTPLUG=y
CONFIG_MEMORY_HOTREMOVE=y
CONFIG_SPARSEMEM_VMEMMAP=y
CONFIG_PCI_P2PDMA=y
CONFIG_PCI_ACS_KUNIT_TEST=y
EOF
./tools/testing/kunit/kunit.py run --arch=x86_64 \
--kunitconfig=/tmp/pci-acs.kunitconfig --jobs=$(nproc) pci_acs
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Assisted-by: Claude Opus 4.8 <noreply@anthropic.com>
---
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
If unsure, say N.
+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) += pci-stub.o
obj-$(CONFIG_PCI_PF_STUB) += pci-pf-stub.o
obj-$(CONFIG_PCI_ECAM) += ecam.o
obj-$(CONFIG_PCI_P2PDMA) += p2pdma.o
+obj-$(CONFIG_PCI_ACS_KUNIT_TEST) += pci_acs_test.o
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
obj-$(CONFIG_VGA_ARB) += vgaarb.o
obj-$(CONFIG_PCI_DOE) += 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 the
+ * peer-to-peer hardware commonly available for testing.
+ */
+#include <kunit/test.h>
+
+#include <linux/pci.h>
+#include <linux/pci_regs.h>
+
+#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[] = {
+ /* 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 *desc)
+{
+ 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 = 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[] = {
+ /* 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 *desc)
+{
+ 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 = 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 = bus->sysdata;
+
+ *val = 0;
+ if (size != 4)
+ return PCIBIOS_SUCCESSFUL;
+
+ if (devfn == cfg->target_devfn &&
+ where == cfg->target_pcie_cap + PCI_EXP_LNKCAP) {
+ *val = FIELD_PREP(PCI_EXP_LNKCAP_PN, cfg->target_port);
+ } else if (devfn == cfg->pdev_devfn) {
+ int base = cfg->pdev_acs_cap + PCI_ACS_EGRESS_CTL_V;
+
+ if (where >= base &&
+ where < base + (int)sizeof(cfg->egress_vector))
+ *val = 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 = {
+ .read = acs_fake_cfg_read,
+ .write = acs_fake_cfg_write,
+};
+
+static struct acs_fake_cfg acs_base_cfg(void)
+{
+ return (struct acs_fake_cfg){
+ .pdev_devfn = PCI_DEVFN(0, 0),
+ .target_devfn = PCI_DEVFN(1, 0),
+ .pdev_acs_cap = 0x100,
+ .target_pcie_cap = 0x40,
+ };
+}
+
+static int acs_egress_ctrl_set(struct kunit *test, struct acs_fake_cfg *cfg,
+ u16 pdev_acs_caps, u16 pdev_flags, u16 target_flags)
+{
+ struct pci_bus *bus = kunit_kzalloc(test, sizeof(*bus), GFP_KERNEL);
+ struct pci_dev *pdev = kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL);
+ struct pci_dev *target = kunit_kzalloc(test, sizeof(*target), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, bus);
+ KUNIT_ASSERT_NOT_NULL(test, pdev);
+ KUNIT_ASSERT_NOT_NULL(test, target);
+
+ bus->ops = &acs_fake_ops;
+ bus->sysdata = cfg;
+
+ pdev->bus = bus;
+ pdev->devfn = cfg->pdev_devfn;
+ pdev->acs_cap = cfg->pdev_acs_cap;
+ pdev->acs_capabilities = pdev_acs_caps;
+ pdev->pcie_cap = 0x40;
+ pdev->pcie_flags_reg = pdev_flags;
+
+ target->bus = bus;
+ target->devfn = cfg->target_devfn;
+ target->pcie_cap = cfg->target_pcie_cap;
+ target->pcie_flags_reg = 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 = acs_base_cfg();
+
+ cfg.target_port = 5;
+ cfg.egress_vector[0] = 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 = acs_base_cfg();
+
+ cfg.target_port = 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 = acs_base_cfg();
+
+ /* Port 40 lives in vector DWORD 1, bit 8: exercises target_port/32. */
+ cfg.target_port = 40;
+ cfg.egress_vector[1] = 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 = acs_base_cfg();
+
+ /* Vector Size 8, port 40 is beyond it. */
+ cfg.target_port = 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 = acs_base_cfg();
+
+ cfg.target_port = 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 = acs_base_cfg();
+
+ cfg.target_port = 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 = acs_base_cfg();
+
+ cfg.target_port = 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 = acs_base_cfg();
+
+ cfg.target_port = 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 = acs_base_cfg();
+
+ cfg.target_port = 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 = acs_base_cfg();
+
+ cfg.target_port = 5;
+ cfg.egress_vector[0] = 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[] = {
+ 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 = {
+ .name = "pci_acs",
+ .test_cases = 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");
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 12/13] PCI/P2PDMA: Add KUnit coverage for the ACS P2P routing walk
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (10 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 11/13] PCI/P2PDMA: Add KUnit tests for ACS routing decisions Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 16:07 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 13/13] PCI: Add KUnit coverage for ACS isolation checks Leon Romanovsky
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc, Claude Opus 4.8
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
Assisted-by: Claude Opus 4.8 <noreply@anthropic.com>
---
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 *client)
* 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);
/**
* 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 <linux/align.h>
#include <linux/bitfield.h>
#include <linux/pci.h>
+#include <linux/pci-p2pdma.h>
#include <trace/events/pci.h>
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_target,
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 <kunit/test.h>
#include <linux/pci.h>
+#include <linux/pci-p2pdma.h>
#include <linux/pci_regs.h>
#include "pci.h"
@@ -346,6 +347,205 @@ static void acs_egress_root_port_test(struct kunit *test)
1);
}
+/*
+ * calc_map_type_and_dist(): drive the full provider->client hierarchy walk
+ * over a fabricated PCIe fabric matching the canonical "two devices behind 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) decides
+ * 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 = dn->acs_cap + PCI_ACS_EGRESS_CTL_V;
+
+ if (size == 4 && where == dn->pcie_cap + PCI_EXP_LNKCAP)
+ *val = FIELD_PREP(PCI_EXP_LNKCAP_PN, c->port);
+ else if (dn->acs_cap && size == 2 && where == dn->acs_cap + PCI_ACS_CTRL)
+ *val = c->acs_ctrl;
+ else if (dn->acs_cap && size == 4 &&
+ where >= vec && where < vec + (int)sizeof(c->egress))
+ *val = 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 = bus->sysdata;
+
+ *val = 0;
+ if (bus == f->dn0->bus && devfn == f->dn0->devfn)
+ acs_dn_read(f->dn0, &f->dn0_cfg, where, size, val);
+ else if (bus == f->dn1->bus && devfn == 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 = {
+ .read = acs_fabric_read,
+ .write = acs_fabric_write,
+};
+
+static struct pci_bus *acs_add_bus(struct kunit *test, struct pci_bus *parent,
+ struct pci_dev *self, u8 nr, void *sysdata)
+{
+ struct pci_bus *bus = kunit_kzalloc(test, sizeof(*bus), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, bus);
+ bus->parent = parent;
+ bus->self = self;
+ bus->number = nr;
+ bus->ops = &acs_fabric_ops;
+ bus->sysdata = 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 = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, dev);
+ dev->bus = bus;
+ dev->devfn = devfn;
+ dev->pcie_cap = 0x40;
+ dev->pcie_flags_reg = 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 = kunit_kzalloc(test, sizeof(*host), GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, host);
+
+ bus0 = acs_add_bus(test, NULL, NULL, 0, f); /* root bus */
+ /* The Root Port doubles as the whitelisted host-bridge device. */
+ rootport = acs_add_dev(test, bus0, PCI_DEVFN(0, 0),
+ PCI_EXP_TYPE_ROOT_PORT);
+ rootport->vendor = PCI_VENDOR_ID_GOOGLE;
+ rootport->device = 0x1234;
+ host->bus = bus0;
+ bus0->bridge = &host->dev;
+
+ bus1 = acs_add_bus(test, bus0, rootport, 1, f);
+ swup = acs_add_dev(test, bus1, PCI_DEVFN(0, 0), PCI_EXP_TYPE_UPSTREAM);
+
+ bus2 = acs_add_bus(test, bus1, swup, 2, f);
+ f->dn0 = acs_add_dev(test, bus2, PCI_DEVFN(0, 0), PCI_EXP_TYPE_DOWNSTREAM);
+ f->dn1 = acs_add_dev(test, bus2, PCI_DEVFN(1, 0), PCI_EXP_TYPE_DOWNSTREAM);
+
+ bus3 = acs_add_bus(test, bus2, f->dn0, 3, f);
+ f->provider = acs_add_dev(test, bus3, PCI_DEVFN(0, 0),
+ PCI_EXP_TYPE_ENDPOINT);
+
+ bus4 = acs_add_bus(test, bus2, f->dn1, 4, f);
+ f->client = 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 = {};
+
+ 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 = {};
+
+ 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 = 0x100;
+ f.dn0->acs_capabilities = PCI_ACS_EC | (64 << 8);
+ f.dn0_cfg.acs_ctrl = PCI_ACS_EC;
+ f.dn1_cfg.port = 5;
+ f.dn0_cfg.egress[0] = 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 = {};
+
+ acs_build_fabric(test, &f);
+ /* Egress Control enabled but the vector bit for the peer is clear. */
+ f.dn0->acs_cap = 0x100;
+ f.dn0->acs_capabilities = PCI_ACS_EC | (64 << 8);
+ f.dn0_cfg.acs_ctrl = PCI_ACS_EC;
+ f.dn1_cfg.port = 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 = {};
+
+ acs_build_fabric(test, &f);
+ /* Request Redirect set: traffic is redirected up to the host bridge. */
+ f.dn0->acs_cap = 0x100;
+ f.dn0->acs_capabilities = PCI_ACS_RR;
+ f.dn0_cfg.acs_ctrl = 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[] = {
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[] = {
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),
{}
};
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* [PATCH 13/13] PCI: Add KUnit coverage for ACS isolation checks
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
` (11 preceding siblings ...)
2026-08-02 15:09 ` [PATCH 12/13] PCI/P2PDMA: Add KUnit coverage for the ACS P2P routing walk Leon Romanovsky
@ 2026-08-02 15:09 ` Leon Romanovsky
2026-08-02 16:07 ` sashiko-bot
12 siblings, 1 reply; 27+ messages in thread
From: Leon Romanovsky @ 2026-08-02 15:09 UTC (permalink / raw)
To: Bjorn Helgaas, Logan Gunthorpe, Jonathan Corbet, Shuah Khan,
Alex Williamson
Cc: linux-pci, linux-kernel, linux-doc, Claude Opus 5
From: Leon Romanovsky <leonro@nvidia.com>
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 <leonro@nvidia.com>
Assisted-by: Claude Opus 5 <noreply@anthropic.com>
---
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, struct pci_dev *target)
}
EXPORT_SYMBOL_IF_KUNIT(pci_acs_egress_ctrl_set);
-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 = acs_flags & PCI_ACS_RR;
int pos;
@@ -3660,6 +3661,7 @@ static bool pci_acs_flags_enabled(struct pci_dev *pdev, u16 acs_flags)
return (ctrl & acs_flags) == acs_flags;
}
+EXPORT_SYMBOL_IF_KUNIT(pci_acs_flags_enabled);
/**
* 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 {
};
#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_target,
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 kunit *test)
c->expect);
}
+/*
+ * pci_acs_flags_enabled(): Direct Translated P2P and Egress Control both let a
+ * peer request reach the peer without Request Redirect, so neither may report
+ * 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 = bus->sysdata;
+
+ *val = 0;
+ if (devfn == cfg->devfn && size == 2 &&
+ where == ACS_TEST_CAP + PCI_ACS_CTRL)
+ *val = 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 = {
+ .read = acs_ctrl_read,
+ .write = 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[] = {
+ { "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 *desc)
+{
+ 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 = test->param_value;
+ struct acs_ctrl_cfg cfg = { .devfn = PCI_DEVFN(0, 0), .ctrl = c->ctrl };
+ struct pci_bus *bus = kunit_kzalloc(test, sizeof(*bus), GFP_KERNEL);
+ struct pci_dev *pdev = kunit_kzalloc(test, sizeof(*pdev), GFP_KERNEL);
+
+ KUNIT_ASSERT_NOT_NULL(test, bus);
+ KUNIT_ASSERT_NOT_NULL(test, pdev);
+
+ bus->ops = &acs_ctrl_ops;
+ bus->sysdata = &cfg;
+
+ pdev->bus = bus;
+ pdev->devfn = cfg.devfn;
+ pdev->acs_cap = ACS_TEST_CAP;
+ pdev->acs_capabilities = 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[] = {
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),
--
2.55.0
^ permalink raw reply related [flat|nested] 27+ messages in thread
* Re: [PATCH 01/13] PCI/P2PDMA: Safely terminate ACS redirect lists
2026-08-02 15:09 ` [PATCH 01/13] PCI/P2PDMA: Safely terminate ACS redirect lists Leon Romanovsky
@ 2026-08-02 15:26 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:26 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=1
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 03/13] PCI/P2PDMA: Document the Address Type assumption
2026-08-02 15:09 ` [PATCH 03/13] PCI/P2PDMA: Document the Address Type assumption Leon Romanovsky
@ 2026-08-02 15:26 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:26 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=3
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 02/13] PCI/P2PDMA: Report ACS ports when the paths share no upstream bridge
2026-08-02 15:09 ` [PATCH 02/13] PCI/P2PDMA: Report ACS ports when the paths share no upstream bridge Leon Romanovsky
@ 2026-08-02 15:34 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:34 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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=disable_acs_redir= hint. The mapping type is unchanged.
>
> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=2
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 05/13] PCI: Add ACS egress control vector accessor
2026-08-02 15:09 ` [PATCH 05/13] PCI: Add ACS egress control vector accessor Leon Romanovsky
@ 2026-08-02 15:39 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:39 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=5
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 04/13] PCI: Account for Direct Translated P2P in ACS isolation checks
2026-08-02 15:09 ` [PATCH 04/13] PCI: Account for Direct Translated P2P in ACS isolation checks Leon Romanovsky
@ 2026-08-02 15:45 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:45 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=4
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 06/13] PCI: Account for ACS egress control in isolation checks
2026-08-02 15:09 ` [PATCH 06/13] PCI: Account for ACS egress control in isolation checks Leon Romanovsky
@ 2026-08-02 15:50 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:50 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=6
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 07/13] PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits
2026-08-02 15:09 ` [PATCH 07/13] PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits Leon Romanovsky
@ 2026-08-02 15:50 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:50 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=7
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 09/13] PCI/P2PDMA: Document ACS egress control handling
2026-08-02 15:09 ` [PATCH 09/13] PCI/P2PDMA: Document ACS egress control handling Leon Romanovsky
@ 2026-08-02 15:51 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:51 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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=disable_acs_redir= clears P2P Request Redirect,
> Completion Redirect, and Egress Control.
>
> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=9
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 10/13] PCI/P2PDMA: Extract pure ACS routing decision helpers
2026-08-02 15:09 ` [PATCH 10/13] PCI/P2PDMA: Extract pure ACS routing decision helpers Leon Romanovsky
@ 2026-08-02 15:55 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:55 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=10
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 08/13] PCI/P2PDMA: Honor ACS egress control vectors
2026-08-02 15:09 ` [PATCH 08/13] PCI/P2PDMA: Honor ACS egress control vectors Leon Romanovsky
@ 2026-08-02 15:57 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 15:57 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=8
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 11/13] PCI/P2PDMA: Add KUnit tests for ACS routing decisions
2026-08-02 15:09 ` [PATCH 11/13] PCI/P2PDMA: Add KUnit tests for ACS routing decisions Leon Romanovsky
@ 2026-08-02 16:02 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 16:02 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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
> [ ... ]
>
> ./tools/testing/kunit/kunit.py run --arch=x86_64 \
> --kunitconfig=/tmp/pci-acs.kunitconfig --jobs=$(nproc) pci_acs
>
> Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=11
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 13/13] PCI: Add KUnit coverage for ACS isolation checks
2026-08-02 15:09 ` [PATCH 13/13] PCI: Add KUnit coverage for ACS isolation checks Leon Romanovsky
@ 2026-08-02 16:07 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 16:07 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=13
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [PATCH 12/13] PCI/P2PDMA: Add KUnit coverage for the ACS P2P routing walk
2026-08-02 15:09 ` [PATCH 12/13] PCI/P2PDMA: Add KUnit coverage for the ACS P2P routing walk Leon Romanovsky
@ 2026-08-02 16:07 ` sashiko-bot
0 siblings, 0 replies; 27+ messages in thread
From: sashiko-bot @ 2026-08-02 16:07 UTC (permalink / raw)
To: Leon Romanovsky; +Cc: linux-pci
> From: Leon Romanovsky <leonro@nvidia.com>
>
> 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 <leonro@nvidia.com>
Sashiko has reviewed this patch and found no issues. It looks great!
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260802-fix-p2p-acs-v1-0-a7c5eb64fff6@nvidia.com?part=12
^ permalink raw reply [flat|nested] 27+ messages in thread
end of thread, other threads:[~2026-08-02 16:07 UTC | newest]
Thread overview: 27+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-02 15:09 [PATCH 00/13] PCI/P2PDMA: Fix ACS egress control handling Leon Romanovsky
2026-08-02 15:09 ` [PATCH 01/13] PCI/P2PDMA: Safely terminate ACS redirect lists Leon Romanovsky
2026-08-02 15:26 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 02/13] PCI/P2PDMA: Report ACS ports when the paths share no upstream bridge Leon Romanovsky
2026-08-02 15:34 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 03/13] PCI/P2PDMA: Document the Address Type assumption Leon Romanovsky
2026-08-02 15:26 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 04/13] PCI: Account for Direct Translated P2P in ACS isolation checks Leon Romanovsky
2026-08-02 15:45 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 05/13] PCI: Add ACS egress control vector accessor Leon Romanovsky
2026-08-02 15:39 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 06/13] PCI: Account for ACS egress control in isolation checks Leon Romanovsky
2026-08-02 15:50 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 07/13] PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits Leon Romanovsky
2026-08-02 15:50 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 08/13] PCI/P2PDMA: Honor ACS egress control vectors Leon Romanovsky
2026-08-02 15:57 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 09/13] PCI/P2PDMA: Document ACS egress control handling Leon Romanovsky
2026-08-02 15:51 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 10/13] PCI/P2PDMA: Extract pure ACS routing decision helpers Leon Romanovsky
2026-08-02 15:55 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 11/13] PCI/P2PDMA: Add KUnit tests for ACS routing decisions Leon Romanovsky
2026-08-02 16:02 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 12/13] PCI/P2PDMA: Add KUnit coverage for the ACS P2P routing walk Leon Romanovsky
2026-08-02 16:07 ` sashiko-bot
2026-08-02 15:09 ` [PATCH 13/13] PCI: Add KUnit coverage for ACS isolation checks Leon Romanovsky
2026-08-02 16:07 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox