diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2019-05-13 18:34:33 -0500 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2019-05-13 18:34:33 -0500 |
commit | 292c9396541399b83a4f6d9141b919cb29275c28 (patch) | |
tree | b2ced4e9a473758296332fe0a69a74eaf7b1c1e1 /drivers/pci | |
parent | 09fdd75c1814faecf6623a33e9ad91c5aee08bdb (diff) | |
parent | 0f97da83102610db886e8b688b9029e080e6d511 (diff) | |
download | linux-292c9396541399b83a4f6d9141b919cb29275c28.tar.bz2 |
Merge branch 'pci/peer-to-peer'
- Add a whitelist of Root Complexes known to support peer-to-peer DMA
between Root Ports (Christian König)
* pci/peer-to-peer:
PCI/P2PDMA: Allow P2P DMA between any devices under AMD ZEN Root Complex
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/p2pdma.c | 38 |
1 files changed, 35 insertions, 3 deletions
diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index c52298d76e64..742928d0053e 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -275,6 +275,30 @@ static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev) } /* + * If we can't find a common upstream bridge take a look at the root + * complex and compare it to a whitelist of known good hardware. + */ +static bool root_complex_whitelist(struct pci_dev *dev) +{ + struct pci_host_bridge *host = pci_find_host_bridge(dev->bus); + struct pci_dev *root = pci_get_slot(host->bus, PCI_DEVFN(0, 0)); + unsigned short vendor, device; + + if (!root) + return false; + + vendor = root->vendor; + device = root->device; + pci_dev_put(root); + + /* AMD ZEN host bridges can do peer to peer */ + if (vendor == PCI_VENDOR_ID_AMD && device == 0x1450) + return true; + + return false; +} + +/* * Find the distance through the nearest common upstream bridge between * two PCI devices. * @@ -317,13 +341,13 @@ static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev) * In this case, a list of all infringing bridge addresses will be * populated in acs_list (assuming it's non-null) for printk purposes. */ -static int upstream_bridge_distance(struct pci_dev *a, - struct pci_dev *b, +static int upstream_bridge_distance(struct pci_dev *provider, + struct pci_dev *client, struct seq_buf *acs_list) { + struct pci_dev *a = provider, *b = client, *bb; int dist_a = 0; int dist_b = 0; - struct pci_dev *bb = NULL; int acs_cnt = 0; /* @@ -354,6 +378,14 @@ static int upstream_bridge_distance(struct pci_dev *a, dist_a++; } + /* + * Allow the connection if both devices are on a whitelisted root + * complex, but add an arbitary large value to the distance. + */ + if (root_complex_whitelist(provider) && + root_complex_whitelist(client)) + return 0x1000 + dist_a + dist_b; + return -1; check_b_path_acs: |