OSDN Git Service

driver core: Do not resume suppliers under device_links_write_lock()
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 1 Feb 2019 00:47:53 +0000 (01:47 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 1 Feb 2019 09:04:08 +0000 (10:04 +0100)
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>
drivers/base/core.c

index a4d6f9b..50610cd 100644 (file)
@@ -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);