[PATCH v2 3/9] driver core: Do not resume suppliers under device_links_write_lock()

From: Rafael J. Wysocki
Date: Thu Jan 31 2019 - 20:04:35 EST


From: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>

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@xxxxxxxxx>
---
drivers/base/core.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)

Index: linux-pm/drivers/base/core.c
===================================================================
--- linux-pm.orig/drivers/base/core.c
+++ linux-pm/drivers/base/core.c
@@ -201,12 +201,21 @@ struct device_link *device_link_add(stru
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(stru

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(stru
out:
device_pm_unlock();
device_links_write_unlock();
+
+ if (rpm_put_supplier)
+ pm_runtime_put(supplier);
+
return link;
}
EXPORT_SYMBOL_GPL(device_link_add);