From b35b1df5e6c213b0b0322e6c231b7111efe4a390 Mon Sep 17 00:00:00 2001 From: Yijing Wang Date: Mon, 17 Aug 2015 18:47:58 +0800 Subject: PCI: Tolerate hierarchies with no Root Port We should not assume any particular hardware topology. Commit d0751b98dfa3 ("PCI: Add dev->has_secondary_link to track downstream PCIe links") relied on the assumption that every PCIe hierarchy is rooted at a Root Port. But we can't rely on any assumption about what hardware we will find; we just have to deal with the world as it is. On some platforms, PCIe devices (endpoints, switch upstream ports, etc.) appear directly on the root bus, and there is no Root Port in the PCI bus hierarchy. For example, Meelis observed these top-level devices on a Sparc V245: 0000:02:00.0 PCI bridge to [bus 03-0d] Switch Upstream Port 0001:02:00.0 PCI bridge to [bus 03] PCIe to PCI/PCI-X Bridge These devices *look* like they have links going upstream, but there really are no upstream devices. In set_pcie_port_type(), we used the parent device to figure out which side of a switch port has a link, so if the parent device did not exist, we dereferenced a NULL parent pointer. Check whether the parent device exists before dereferencing it. Meelis observed this oops on Sparc V245 and T2000. Ben Herrenschmidt says this is also possible on IBM PowerVM guests on PowerPC. [bhelgaas: changelog, comment] Link: http://lkml.kernel.org/r/alpine.LRH.2.20.1508122118210.18637@math.ut.ee Reported-by: Meelis Roos Tested-by: Meelis Roos Signed-off-by: Yijing Wang Signed-off-by: Bjorn Helgaas Acked-by: David S. Miller --- drivers/pci/probe.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cefd636681b6..b978bbfe044c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -997,7 +997,12 @@ void set_pcie_port_type(struct pci_dev *pdev) else if (type == PCI_EXP_TYPE_UPSTREAM || type == PCI_EXP_TYPE_DOWNSTREAM) { parent = pci_upstream_bridge(pdev); - if (!parent->has_secondary_link) + + /* + * Usually there's an upstream device (Root Port or Switch + * Downstream Port), but we can't assume one exists. + */ + if (parent && !parent->has_secondary_link) pdev->has_secondary_link = 1; } } -- cgit v1.2.3