mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-21 16:29:59 +00:00
PM-runtime: Fix __pm_runtime_set_status() race with runtime resume
Commit4080ab0830
("PM-runtime: Take suppliers into account in __pm_runtime_set_status()") introduced a race condition that may trigger if __pm_runtime_set_status() is used incorrectly (that is, if it is called when PM-runtime is enabled for the target device and working). In that case, if the original PM-runtime status of the device is RPM_SUSPENDED, a runtime resume of the device may occur after __pm_runtime_set_status() has dropped its power.lock spinlock and before deactivating its suppliers, so the suppliers may be deactivated while the device is PM-runtime-active which may lead to functional issues. To avoid that, modify __pm_runtime_set_status() to check whether or not PM-runtime is enabled for the device before activating its suppliers (if the new status is RPM_ACTIVE) and either return an error if that's the case or increment the device's disable_depth counter to prevent PM-runtime from being enabled for it while the remaining part of the function is running (disable_depth is then decremented on the way out). Fixes:4080ab0830
("PM-runtime: Take suppliers into account in __pm_runtime_set_status()") Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
daaef255dc
commit
c1567f813a
1 changed files with 18 additions and 6 deletions
|
@ -1106,6 +1106,22 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
|
|||
if (status != RPM_ACTIVE && status != RPM_SUSPENDED)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
|
||||
/*
|
||||
* Prevent PM-runtime from being enabled for the device or return an
|
||||
* error if it is enabled already and working.
|
||||
*/
|
||||
if (dev->power.runtime_error || dev->power.disable_depth)
|
||||
dev->power.disable_depth++;
|
||||
else
|
||||
error = -EAGAIN;
|
||||
|
||||
spin_unlock_irq(&dev->power.lock);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* If the new status is RPM_ACTIVE, the suppliers can be activated
|
||||
* upfront regardless of the current status, because next time
|
||||
|
@ -1124,12 +1140,6 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
|
|||
|
||||
spin_lock_irq(&dev->power.lock);
|
||||
|
||||
if (!dev->power.runtime_error && !dev->power.disable_depth) {
|
||||
status = dev->power.runtime_status;
|
||||
error = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev->power.runtime_status == status || !parent)
|
||||
goto out_set;
|
||||
|
||||
|
@ -1182,6 +1192,8 @@ int __pm_runtime_set_status(struct device *dev, unsigned int status)
|
|||
device_links_read_unlock(idx);
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return error;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__pm_runtime_set_status);
|
||||
|
|
Loading…
Reference in a new issue