On Mon, Jun 22, 2020 at 07:12:48PM +0300, Mika Westerberg wrote: > Commit 6ae72bfa656e ("PCI: Unify pcie_find_root_port() and > pci_find_pcie_root_port()") unified the root port finding functionality > into a single function but missed the fact that the passed in device may > already be a root port. This causes the kernel to block power management > of PCIe hierarchies in recent systems because ->bridge_d3 started to > return false for such ports after the commit in question. > > Fixes: 6ae72bfa656e ("PCI: Unify pcie_find_root_port() and pci_find_pcie_root_port()") > Signed-off-by: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx> > Cc: stable@xxxxxxxxxxxxxxx > --- > include/linux/pci.h | 7 ++++++- > 1 file changed, 6 insertions(+), 1 deletion(-) > > diff --git a/include/linux/pci.h b/include/linux/pci.h > index c79d83304e52..c17c24f5eeed 100644 > --- a/include/linux/pci.h > +++ b/include/linux/pci.h > @@ -2169,8 +2169,13 @@ static inline int pci_pcie_type(const struct pci_dev *dev) > */ > static inline struct pci_dev *pcie_find_root_port(struct pci_dev *dev) > { > - struct pci_dev *bridge = pci_upstream_bridge(dev); > + struct pci_dev *bridge; > > + /* If dev is already root port */ > + if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) > + return dev; > + > + bridge = pci_upstream_bridge(dev); > while (bridge) { > if (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT) > return bridge; I applied the patch below, which is slightly simplified but I think still equivalent, to for-linus for v5.8. Let me know if it's not. I dropped the stable tag because 6ae72bfa656e was merged for v5.8-rc1, and I assume v5.7 works correctly so it doesn't need any change. commit 5396956cc7c6 ("PCI: Make pcie_find_root_port() work for Root Ports") Author: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx> Date: Mon Jun 22 19:12:48 2020 +0300 PCI: Make pcie_find_root_port() work for Root Ports Commit 6ae72bfa656e ("PCI: Unify pcie_find_root_port() and pci_find_pcie_root_port()") broke acpi_pci_bridge_d3() because calling pcie_find_root_port() on a Root Port returned NULL when it should return the Root Port, which in turn broke power management of PCIe hierarchies. Rework pcie_find_root_port() so it returns its argument when it is already a Root Port. [bhelgaas: test device only once, test for PCIe] Fixes: 6ae72bfa656e ("PCI: Unify pcie_find_root_port() and pci_find_pcie_root_port()") Link: https://lore.kernel.org/r/20200622161248.51099-1-mika.westerberg@xxxxxxxxxxxxxxx Signed-off-by: Mika Westerberg <mika.westerberg@xxxxxxxxxxxxxxx> Signed-off-by: Bjorn Helgaas <bhelgaas@xxxxxxxxxx> diff --git a/include/linux/pci.h b/include/linux/pci.h index c79d83304e52..34c1c4f45288 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -2169,12 +2169,11 @@ static inline int pci_pcie_type(const struct pci_dev *dev) */ static inline struct pci_dev *pcie_find_root_port(struct pci_dev *dev) { - struct pci_dev *bridge = pci_upstream_bridge(dev); - - while (bridge) { - if (pci_pcie_type(bridge) == PCI_EXP_TYPE_ROOT_PORT) - return bridge; - bridge = pci_upstream_bridge(bridge); + while (dev) { + if (pci_is_pcie(dev) && + pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) + return dev; + dev = pci_upstream_bridge(dev); } return NULL;