c75cb4
From patchwork Mon Oct  2 14:08:40 2017
c75cb4
Content-Type: text/plain; charset="utf-8"
c75cb4
MIME-Version: 1.0
c75cb4
Content-Transfer-Encoding: 7bit
c75cb4
Subject: PCI: aspm: deal with missing root ports in link state handling
c75cb4
From: Ard Biesheuvel <ard.biesheuvel@linaro.org>
c75cb4
X-Patchwork-Id: 9980861
c75cb4
Message-Id: <20171002140840.7767-1-ard.biesheuvel@linaro.org>
c75cb4
To: linux-pci@vger.kernel.org, bhelgaas@google.com
c75cb4
Cc: graeme.gregory@linaro.org, leif.lindholm@linaro.org,
c75cb4
 daniel.thompson@Linaro.org, Ard Biesheuvel <ard.biesheuvel@linaro.org>
c75cb4
Date: Mon,  2 Oct 2017 15:08:40 +0100
c75cb4
c75cb4
Even though it is unconventional, some PCIe host implementations omit
c75cb4
the root ports entirely, and simply consist of a host bridge (which
c75cb4
is not modeled as a device in the PCI hierarchy) and a link.
c75cb4
c75cb4
When the downstream device is an endpoint, our current code does not
c75cb4
seem to mind this unusual configuration. However, when PCIe switches
c75cb4
are involved, the ASPM code assumes that any downstream switch port
c75cb4
has a parent, and blindly derefences the bus->parent->self field of
c75cb4
the pci_dev struct to chain the downstream link state to the link
c75cb4
state of the root port. Given that the root port is missing, the link
c75cb4
is not modeled at all, and nor is the link state, and attempting to
c75cb4
access it results in a NULL pointer dereference and a crash.
c75cb4
c75cb4
So let's avoid this by allowing the link state chain to terminate at
c75cb4
the downstream port if no root port exists.
c75cb4
c75cb4
Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
c75cb4
---
c75cb4
 drivers/pci/pcie/aspm.c | 8 ++++++--
c75cb4
 1 file changed, 6 insertions(+), 2 deletions(-)
c75cb4
c75cb4
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
c75cb4
index 1dfa10cc566b..0bea8498b5a5 100644
c75cb4
--- a/drivers/pci/pcie/aspm.c
c75cb4
+++ b/drivers/pci/pcie/aspm.c
c75cb4
@@ -802,10 +802,14 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
c75cb4
 
c75cb4
 	/*
c75cb4
 	 * Root Ports and PCI/PCI-X to PCIe Bridges are roots of PCIe
c75cb4
-	 * hierarchies.
c75cb4
+	 * hierarchies.  Note that some PCIe host implementations omit
c75cb4
+	 * the root ports entirely, in which case a downstream port on
c75cb4
+	 * a switch may become the root of the link state chain for all
c75cb4
+	 * its subordinate endpoints.
c75cb4
 	 */
c75cb4
 	if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
c75cb4
-	    pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE) {
c75cb4
+	    pci_pcie_type(pdev) == PCI_EXP_TYPE_PCIE_BRIDGE ||
c75cb4
+	    !pdev->bus->parent->self) {
c75cb4
 		link->root = link;
c75cb4
 	} else {
c75cb4
 		struct pcie_link_state *parent;