mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
driver core: Do not resume suppliers under device_links_write_lock()
It is incorrect to call pm_runtime_get_sync() under device_links_write_lock(), because it may end up trying to take device_links_read_lock() while resuming the target device and that will deadlock in the non-SRCU case, so avoid that by resuming the supplier device in device_link_add() before calling device_links_write_lock(). Fixes:21d5c57b37
("PM / runtime: Use device links") Fixes:baa8809f60
("PM / runtime: Optimize the use of device links") Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
f265df550a
commit
5db25c9eb8
1 changed files with 14 additions and 6 deletions
|
@ -201,12 +201,21 @@ struct device_link *device_link_add(struct device *consumer,
|
||||||
struct device *supplier, u32 flags)
|
struct device *supplier, u32 flags)
|
||||||
{
|
{
|
||||||
struct device_link *link;
|
struct device_link *link;
|
||||||
|
bool rpm_put_supplier = false;
|
||||||
|
|
||||||
if (!consumer || !supplier ||
|
if (!consumer || !supplier ||
|
||||||
(flags & DL_FLAG_STATELESS &&
|
(flags & DL_FLAG_STATELESS &&
|
||||||
flags & (DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER)))
|
flags & (DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
if (flags & DL_FLAG_PM_RUNTIME && flags & DL_FLAG_RPM_ACTIVE) {
|
||||||
|
if (pm_runtime_get_sync(supplier) < 0) {
|
||||||
|
pm_runtime_put_noidle(supplier);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
rpm_put_supplier = true;
|
||||||
|
}
|
||||||
|
|
||||||
device_links_write_lock();
|
device_links_write_lock();
|
||||||
device_pm_lock();
|
device_pm_lock();
|
||||||
|
|
||||||
|
@ -250,13 +259,8 @@ struct device_link *device_link_add(struct device *consumer,
|
||||||
|
|
||||||
if (flags & DL_FLAG_PM_RUNTIME) {
|
if (flags & DL_FLAG_PM_RUNTIME) {
|
||||||
if (flags & DL_FLAG_RPM_ACTIVE) {
|
if (flags & DL_FLAG_RPM_ACTIVE) {
|
||||||
if (pm_runtime_get_sync(supplier) < 0) {
|
|
||||||
pm_runtime_put_noidle(supplier);
|
|
||||||
kfree(link);
|
|
||||||
link = NULL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
link->rpm_active = true;
|
link->rpm_active = true;
|
||||||
|
rpm_put_supplier = false;
|
||||||
}
|
}
|
||||||
pm_runtime_new_link(consumer);
|
pm_runtime_new_link(consumer);
|
||||||
/*
|
/*
|
||||||
|
@ -328,6 +332,10 @@ struct device_link *device_link_add(struct device *consumer,
|
||||||
out:
|
out:
|
||||||
device_pm_unlock();
|
device_pm_unlock();
|
||||||
device_links_write_unlock();
|
device_links_write_unlock();
|
||||||
|
|
||||||
|
if (rpm_put_supplier)
|
||||||
|
pm_runtime_put(supplier);
|
||||||
|
|
||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(device_link_add);
|
EXPORT_SYMBOL_GPL(device_link_add);
|
||||||
|
|
Loading…
Reference in a new issue