From: Rafael J. Wysocki <rafael.j.wyso...@intel.com>

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.wyso...@intel.com>
---
 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);

Reply via email to