diff options
-rw-r--r-- | drivers/pci/pci.c | 28 |
1 files changed, 20 insertions, 8 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8200874ef5fc..4a7f6f54d669 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2297,20 +2297,32 @@ void pci_bridge_d3_update(struct pci_dev *dev) return; /* - * If the device is removed we do not care about its D3cold - * capabilities. + * If D3 is currently allowed for the bridge, removing one of its + * children won't change that. + */ + if (remove && bridge->bridge_d3) + return; + + /* + * If D3 is currently allowed for the bridge and a child is added or + * changed, disallowance of D3 can only be caused by that child, so + * we only need to check that single device, not any of its siblings. + * + * If D3 is currently not allowed for the bridge, checking the device + * first may allow us to skip checking its siblings. */ if (!remove) pci_dev_check_d3cold(dev, &d3cold_ok); - if (d3cold_ok) { - /* - * We need to go through all children to find out if all of - * them can still go to D3cold. - */ + /* + * If D3 is currently not allowed for the bridge, this may be caused + * either by the device being changed/removed or any of its siblings, + * so we need to go through all children to find out if one of them + * continues to block D3. + */ + if (d3cold_ok && !bridge->bridge_d3) pci_walk_bus(bridge->subordinate, pci_dev_check_d3cold, &d3cold_ok); - } if (bridge->bridge_d3 != d3cold_ok) { bridge->bridge_d3 = d3cold_ok; |