From 5db25c9eb893df8f6b93c1d97b8006d768e1b6f5 Mon Sep 17 00:00:00 2001
From: "Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
Date: Fri, 1 Feb 2019 01:47:53 +0100
Subject: [PATCH] driver core: Do not resume suppliers under device_links_write_lock()
Git-commit: 5db25c9eb893df8f6b93c1d97b8006d768e1b6f5
Patch-mainline: v5.1-rc1
References: bsc#1051510
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>
Acked-by: Takashi Iwai <tiwai@suse.de>
---
drivers/base/core.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -179,11 +179,20 @@ 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)))
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();
@@ -208,13 +217,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);
/*
@@ -285,6 +289,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);