aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Williamson <alex.williamson@redhat.com>2014-05-22 19:07:30 -0400
committerBjorn Helgaas <bhelgaas@google.com>2014-05-28 13:46:24 -0400
commitc25dc82899e67a32fdcfb20dd72a37fc236fde2e (patch)
tree5db0bd880c1b2fbfb3435e68b991216e2a770cf2
parentc9eaa447e77efe77b7fa4c953bd62de8297fd6c5 (diff)
PCI: Add DMA alias iterator
In a mixed PCI/PCI-X/PCIe topology, bridges can take ownership of transactions, replacing the original requester ID with their own. Sometimes we just want to know the resulting device or resulting alias; other times we want each step in the chain. This iterator allows either usage. When an endpoint is connected via an unbroken chain of PCIe switches and root ports, it has no alias and its requester ID is visible to the root bus. When PCI/X get in the way, we pick up aliases for bridges. The reason why we potentially care about each step in the path is because of PCI-X. PCI-X has the concept of a requester ID, but bridges may or may not take ownership of various types of transactions. We therefore leave it to the consumer of this function to prune out what they don't care about rather than attempt to flatten the alias ourselves. Tested-by: George Spelvin <linux@horizon.com> Tested-by: Pat Erley <pat-lkml@erley.org> Signed-off-by: Alex Williamson <alex.williamson@redhat.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r--drivers/pci/search.c70
-rw-r--r--include/linux/pci.h4
2 files changed, 74 insertions, 0 deletions
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index 4a1b972efe7f..5601cdb8bbb3 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -18,6 +18,76 @@ DECLARE_RWSEM(pci_bus_sem);
18EXPORT_SYMBOL_GPL(pci_bus_sem); 18EXPORT_SYMBOL_GPL(pci_bus_sem);
19 19
20/* 20/*
21 * pci_for_each_dma_alias - Iterate over DMA aliases for a device
22 * @pdev: starting downstream device
23 * @fn: function to call for each alias
24 * @data: opaque data to pass to @fn
25 *
26 * Starting @pdev, walk up the bus calling @fn for each possible alias
27 * of @pdev at the root bus.
28 */
29int pci_for_each_dma_alias(struct pci_dev *pdev,
30 int (*fn)(struct pci_dev *pdev,
31 u16 alias, void *data), void *data)
32{
33 struct pci_bus *bus;
34 int ret;
35
36 ret = fn(pdev, PCI_DEVID(pdev->bus->number, pdev->devfn), data);
37 if (ret)
38 return ret;
39
40 for (bus = pdev->bus; !pci_is_root_bus(bus); bus = bus->parent) {
41 struct pci_dev *tmp;
42
43 /* Skip virtual buses */
44 if (!bus->self)
45 continue;
46
47 tmp = bus->self;
48
49 /*
50 * PCIe-to-PCI/X bridges alias transactions from downstream
51 * devices using the subordinate bus number (PCI Express to
52 * PCI/PCI-X Bridge Spec, rev 1.0, sec 2.3). For all cases
53 * where the upstream bus is PCI/X we alias to the bridge
54 * (there are various conditions in the previous reference
55 * where the bridge may take ownership of transactions, even
56 * when the secondary interface is PCI-X).
57 */
58 if (pci_is_pcie(tmp)) {
59 switch (pci_pcie_type(tmp)) {
60 case PCI_EXP_TYPE_ROOT_PORT:
61 case PCI_EXP_TYPE_UPSTREAM:
62 case PCI_EXP_TYPE_DOWNSTREAM:
63 continue;
64 case PCI_EXP_TYPE_PCI_BRIDGE:
65 ret = fn(tmp,
66 PCI_DEVID(tmp->subordinate->number,
67 PCI_DEVFN(0, 0)), data);
68 if (ret)
69 return ret;
70 continue;
71 case PCI_EXP_TYPE_PCIE_BRIDGE:
72 ret = fn(tmp,
73 PCI_DEVID(tmp->bus->number,
74 tmp->devfn), data);
75 if (ret)
76 return ret;
77 continue;
78 }
79 } else {
80 ret = fn(tmp, PCI_DEVID(tmp->bus->number, tmp->devfn),
81 data);
82 if (ret)
83 return ret;
84 }
85 }
86
87 return ret;
88}
89
90/*
21 * find the upstream PCIe-to-PCI bridge of a PCI device 91 * find the upstream PCIe-to-PCI bridge of a PCI device
22 * if the device is PCIE, return NULL 92 * if the device is PCIE, return NULL
23 * if the device isn't connected to a PCIe bridge (that is its parent is a 93 * if the device isn't connected to a PCIe bridge (that is its parent is a
diff --git a/include/linux/pci.h b/include/linux/pci.h
index aab57b4abe7f..14b074bbc841 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1795,6 +1795,10 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev)
1795} 1795}
1796#endif 1796#endif
1797 1797
1798int pci_for_each_dma_alias(struct pci_dev *pdev,
1799 int (*fn)(struct pci_dev *pdev,
1800 u16 alias, void *data), void *data);
1801
1798/** 1802/**
1799 * pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device 1803 * pci_find_upstream_pcie_bridge - find upstream PCIe-to-PCI bridge of a device
1800 * @pdev: the PCI device 1804 * @pdev: the PCI device