Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PM / runtime: Drop children check from __pm_runtime_set_status()

The check for "active" children in __pm_runtime_set_status(), when
trying to set the parent device status to "suspended", doesn't
really make sense, because in fact it is not invalid to set the
status of a device with runtime PM disabled to "suspended" in any
case. It is invalid to enable runtime PM for a device with its
status set to "suspended" while its child_count reference counter
is nonzero, but the check in __pm_runtime_set_status() doesn't
really cover that situation.

For this reason, drop the children check from __pm_runtime_set_status()
and add a check against child_count reference counters of "suspended"
devices to pm_runtime_enable().

Fixes: a8636c89648a (PM / Runtime: Don't allow to suspend a device with an active child)
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
Reviewed-by: Johan Hovold <johan@kernel.org>

+12 -22
+1 -2
Documentation/power/runtime_pm.txt
··· 435 435 PM status to 'suspended' and update its parent's counter of 'active' 436 436 children as appropriate (it is only valid to use this function if 437 437 'power.runtime_error' is set or 'power.disable_depth' is greater than 438 - zero); it will fail and return an error code if the device has a child 439 - which is active and the 'power.ignore_children' flag is unset 438 + zero) 440 439 441 440 bool pm_runtime_active(struct device *dev); 442 441 - return true if the device's runtime PM status is 'active' or its
+11 -20
drivers/base/power/runtime.c
··· 1101 1101 goto out; 1102 1102 } 1103 1103 1104 - if (dev->power.runtime_status == status) 1104 + if (dev->power.runtime_status == status || !parent) 1105 1105 goto out_set; 1106 1106 1107 1107 if (status == RPM_SUSPENDED) { 1108 - /* 1109 - * It is invalid to suspend a device with an active child, 1110 - * unless it has been set to ignore its children. 1111 - */ 1112 - if (!dev->power.ignore_children && 1113 - atomic_read(&dev->power.child_count)) { 1114 - dev_err(dev, "runtime PM trying to suspend device but active child\n"); 1115 - error = -EBUSY; 1116 - goto out; 1117 - } 1118 - 1119 - if (parent) { 1120 - atomic_add_unless(&parent->power.child_count, -1, 0); 1121 - notify_parent = !parent->power.ignore_children; 1122 - } 1123 - goto out_set; 1124 - } 1125 - 1126 - if (parent) { 1108 + atomic_add_unless(&parent->power.child_count, -1, 0); 1109 + notify_parent = !parent->power.ignore_children; 1110 + } else { 1127 1111 spin_lock_nested(&parent->power.lock, SINGLE_DEPTH_NESTING); 1128 1112 1129 1113 /* ··· 1290 1306 dev->power.disable_depth--; 1291 1307 else 1292 1308 dev_warn(dev, "Unbalanced %s!\n", __func__); 1309 + 1310 + WARN(!dev->power.disable_depth && 1311 + dev->power.runtime_status == RPM_SUSPENDED && 1312 + !dev->power.ignore_children && 1313 + atomic_read(&dev->power.child_count) > 0, 1314 + "Enabling runtime PM for inactive device (%s) with active children\n", 1315 + dev_name(dev)); 1293 1316 1294 1317 spin_unlock_irqrestore(&dev->power.lock, flags); 1295 1318 }