summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/pci/pci.c28
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;