diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2019-05-13 19:34:33 -0400 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2019-05-13 19:34:33 -0400 |
| commit | 292c9396541399b83a4f6d9141b919cb29275c28 (patch) | |
| tree | b2ced4e9a473758296332fe0a69a74eaf7b1c1e1 | |
| parent | 09fdd75c1814faecf6623a33e9ad91c5aee08bdb (diff) | |
| parent | 0f97da83102610db886e8b688b9029e080e6d511 (diff) | |
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
| -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) | |||
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | /* | 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 | |||
| 301 | /* | ||
| 278 | * Find the distance through the nearest common upstream bridge between | 302 | * Find the distance through the nearest common upstream bridge between |
| 279 | * two PCI devices. | 303 | * two PCI devices. |
| 280 | * | 304 | * |
| @@ -317,13 +341,13 @@ static void seq_buf_print_bus_devfn(struct seq_buf *buf, struct pci_dev *pdev) | |||
| 317 | * In this case, a list of all infringing bridge addresses will be | 341 | * In this case, a list of all infringing bridge addresses will be |
| 318 | * populated in acs_list (assuming it's non-null) for printk purposes. | 342 | * populated in acs_list (assuming it's non-null) for printk purposes. |
| 319 | */ | 343 | */ |
| 320 | static int upstream_bridge_distance(struct pci_dev *a, | 344 | static int upstream_bridge_distance(struct pci_dev *provider, |
| 321 | struct pci_dev *b, | 345 | struct pci_dev *client, |
| 322 | struct seq_buf *acs_list) | 346 | struct seq_buf *acs_list) |
| 323 | { | 347 | { |
| 348 | struct pci_dev *a = provider, *b = client, *bb; | ||
| 324 | int dist_a = 0; | 349 | int dist_a = 0; |
| 325 | int dist_b = 0; | 350 | int dist_b = 0; |
| 326 | struct pci_dev *bb = NULL; | ||
| 327 | int acs_cnt = 0; | 351 | int acs_cnt = 0; |
| 328 | 352 | ||
| 329 | /* | 353 | /* |
| @@ -354,6 +378,14 @@ static int upstream_bridge_distance(struct pci_dev *a, | |||
| 354 | dist_a++; | 378 | dist_a++; |
| 355 | } | 379 | } |
| 356 | 380 | ||
| 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 | |||
| 357 | return -1; | 389 | return -1; |
| 358 | 390 | ||
| 359 | check_b_path_acs: | 391 | check_b_path_acs: |
