[PATCH v4 11/18] PCI/P2PDMA: Derive peer-to-peer routing from ACS control bits
From: Leon Romanovsky
Date: Fri Aug 21 2026 - 15:44:31 EST
From: Leon Romanovsky <leonro@xxxxxxxxxx>
pci_bridge_has_acs_redir() treats Request Redirect and Completion Redirect
as interchangeable for a P2PDMA transaction. On asymmetric fabrics this
sends an otherwise direct mapping through the host bridge.
The client issues Requests to provider memory, while the provider emits
read Completions. Evaluate Request Redirect for the former and Completion
Redirect for the latter. Leave Egress Control conservative until the target
vector is available.
Tested-by: Tushar Dave <tdave@xxxxxxxxxx>
Fixes: 52916982af48 ("PCI/P2PDMA: Support peer-to-peer memory")
Signed-off-by: Leon Romanovsky <leonro@xxxxxxxxxx>
---
drivers/pci/p2pdma.c | 49 ++++++++++++++++++++++++++++++++++---------------
1 file changed, 34 insertions(+), 15 deletions(-)
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c
index a364008bbf50..1eda7b2135d7 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"
+
/*
* Lifetime and RCU usage
*
@@ -536,26 +538,35 @@ 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,
+};
+
+enum pci_acs_p2pdma_tlp {
+ PCI_ACS_P2PDMA_TLP_REQUEST,
+ PCI_ACS_P2PDMA_TLP_COMPLETION,
+};
+
+static enum pci_acs_p2pdma_state
+pci_acs_p2pdma_state(struct pci_dev *pdev, enum pci_acs_p2pdma_tlp tlp)
{
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 (tlp == PCI_ACS_P2PDMA_TLP_COMPLETION)
+ return ctrl & PCI_ACS_CR ? PCI_ACS_P2PDMA_REDIRECT :
+ PCI_ACS_P2PDMA_DIRECT;
- return 0;
+ return ctrl & (PCI_ACS_RR | PCI_ACS_EC) ?
+ PCI_ACS_P2PDMA_REDIRECT : PCI_ACS_P2PDMA_DIRECT;
}
static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev)
@@ -733,6 +744,10 @@ 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.
*
+ * The client initiates Requests to provider memory. Check Request Redirect
+ * on the client path and Completion Redirect for read Completions on the
+ * provider 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
@@ -767,7 +782,9 @@ 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,
+ PCI_ACS_P2PDMA_TLP_COMPLETION) ==
+ PCI_ACS_P2PDMA_REDIRECT) {
seq_buf_print_bus_devfn(&acs_list, a);
acs_cnt++;
}
@@ -796,7 +813,9 @@ 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,
+ PCI_ACS_P2PDMA_TLP_REQUEST) ==
+ PCI_ACS_P2PDMA_REDIRECT) {
seq_buf_print_bus_devfn(&acs_list, bb);
acs_cnt++;
}
@@ -1150,10 +1169,10 @@ EXPORT_SYMBOL_GPL(pci_p2pmem_publish);
/**
* pci_p2pdma_map_type - Determine the mapping type for P2PDMA transfers
* @provider: P2PDMA provider structure
- * @dev: Target device for the transfer
+ * @dev: Client device that initiates the transfer
*
* Determines how peer-to-peer DMA transfers should be mapped between
- * the provider and the target device. The mapping type indicates whether
+ * the provider and the client device. The mapping type indicates whether
* the transfer can be done directly through PCI switches or must go
* through the host bridge.
*/
--
2.55.0