summaryrefslogtreecommitdiff
path: root/drivers/base
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>2020-01-27 14:46:15 +0100
commitd7ee5bfb5541b2d8b652f1026c12a5a631d14b8e (patch)
treeefd086b30acf2bbc3398be4d4702aeb59fd86e88 /drivers/base
parentd7a7f04e10017bf95eea0cb294287b4ab26e541a (diff)
driver core: Do not resume suppliers under device_links_write_lock()
[ Upstream commit 5db25c9eb893df8f6b93c1d97b8006d768e1b6f5 ] 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> Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'drivers/base')
-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 2b0a1054535c..93c2fc58013e 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -180,11 +180,20 @@ 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)))
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();
@@ -209,13 +218,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);
/*
@@ -286,6 +290,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);