summaryrefslogtreecommitdiff
path: root/drivers/base/core.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2019-02-01 01:47:53 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-02-01 10:04:08 +0100
commit5db25c9eb893df8f6b93c1d97b8006d768e1b6f5 (patch)
treeb4a98023165114a5539d9804089fb1f4493e4caa /drivers/base/core.c
parentf265df550a4350dce0a4d721a77c52e4b847ea40 (diff)
downloadlwn-5db25c9eb893df8f6b93c1d97b8006d768e1b6f5.tar.gz
lwn-5db25c9eb893df8f6b93c1d97b8006d768e1b6f5.zip
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: 21d5c57b3726 ("PM / runtime: Use device links") Fixes: baa8809f6097 ("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>
Diffstat (limited to 'drivers/base/core.c')
-rw-r--r--drivers/base/core.c20
1 files changed, 14 insertions, 6 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c
index a4d6f9b93088..50610cd87e71 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -201,12 +201,21 @@ struct device_link *device_link_add(struct device *consumer,
struct device *supplier, u32 flags)
{
struct device_link *link;
+ bool rpm_put_supplier = false;
if (!consumer || !supplier ||
(flags & DL_FLAG_STATELESS &&
flags & (DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER)))
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_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_RPM_ACTIVE) {
- if (pm_runtime_get_sync(supplier) < 0) {
- pm_runtime_put_noidle(supplier);
- kfree(link);
- link = NULL;
- goto out;
- }
link->rpm_active = true;
+ rpm_put_supplier = false;
}
pm_runtime_new_link(consumer);
/*
@@ -328,6 +332,10 @@ struct device_link *device_link_add(struct device *consumer,
out:
device_pm_unlock();
device_links_write_unlock();
+
+ if (rpm_put_supplier)
+ pm_runtime_put(supplier);
+
return link;
}
EXPORT_SYMBOL_GPL(device_link_add);