Hi Bjorn, On 2020/7/1 6:01, Bjorn Helgaas wrote: > 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); > } > We may have some problems here, as after pcie_find_root_port() called, *dev will be either root port or NULL but users may want it unchanged. One such usage is in acpi_pci_bridge_d3(), drivers/pci/pci-acpi.c, *dev is used as origin after called this. So we should use a temporary point to *dev rather than directly modify it. Thanks, Yicong > return NULL; > . >