Skip to content

Commit 0f97da8

Browse files
Christian Königbjorn-helgaas
authored andcommitted
PCI/P2PDMA: Allow P2P DMA between any devices under AMD ZEN Root Complex
The PCI specs say that peer-to-peer DMA should be supported between any two devices that have a common upstream PCI-to-PCI bridge. But devices under different Root Ports don't share a common upstream bridge, and PCIe r4.0, sec 1.3.1, says routing peer-to-peer transactions between Root Ports in the same Root Complex is optional. Many Root Complexes, including AMD ZEN, *do* support peer-to-peer DMA even between Root Ports. Add a whitelist and allow peer-to-peer DMA if both participants are attached to a Root Complex known to support it. Link: https://lore.kernel.org/linux-pci/[email protected] Signed-off-by: Christian König <[email protected]> [bhelgaas: changelog] Signed-off-by: Bjorn Helgaas <[email protected]> Reviewed-by: Logan Gunthorpe <[email protected]>
1 parent 9e98c67 commit 0f97da8

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

drivers/pci/p2pdma.c

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,30 @@ static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev)
274274
seq_buf_printf(buf, "%s;", pci_name(pdev));
275275
}
276276

277+
/*
278+
* If we can't find a common upstream bridge take a look at the root
279+
* complex and compare it to a whitelist of known good hardware.
280+
*/
281+
static bool root_complex_whitelist(struct pci_dev *dev)
282+
{
283+
struct pci_host_bridge *host = pci_find_host_bridge(dev->bus);
284+
struct pci_dev *root = pci_get_slot(host->bus, PCI_DEVFN(0, 0));
285+
unsigned short vendor, device;
286+
287+
if (!root)
288+
return false;
289+
290+
vendor = root->vendor;
291+
device = root->device;
292+
pci_dev_put(root);
293+
294+
/* AMD ZEN host bridges can do peer to peer */
295+
if (vendor == PCI_VENDOR_ID_AMD && device == 0x1450)
296+
return true;
297+
298+
return false;
299+
}
300+
277301
/*
278302
* Find the distance through the nearest common upstream bridge between
279303
* two PCI devices.
@@ -317,13 +341,13 @@ static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev)
317341
* In this case, a list of all infringing bridge addresses will be
318342
* populated in acs_list (assuming it's non-null) for printk purposes.
319343
*/
320-
static int upstream_bridge_distance(struct pci_dev *a,
321-
struct pci_dev *b,
344+
static int upstream_bridge_distance(struct pci_dev *provider,
345+
struct pci_dev *client,
322346
struct seq_buf *acs_list)
323347
{
348+
struct pci_dev *a = provider, *b = client, *bb;
324349
int dist_a = 0;
325350
int dist_b = 0;
326-
struct pci_dev *bb = NULL;
327351
int acs_cnt = 0;
328352

329353
/*
@@ -354,6 +378,14 @@ static int upstream_bridge_distance(struct pci_dev *a,
354378
dist_a++;
355379
}
356380

381+
/*
382+
* Allow the connection if both devices are on a whitelisted root
383+
* complex, but add an arbitary large value to the distance.
384+
*/
385+
if (root_complex_whitelist(provider) &&
386+
root_complex_whitelist(client))
387+
return 0x1000 + dist_a + dist_b;
388+
357389
return -1;
358390

359391
check_b_path_acs:

0 commit comments

Comments
 (0)