Commit 088db931 authored by Matthew Wilcox's avatar Matthew Wilcox Committed by Zhang Rui

thermal: Fix potential deadlock in cpu_cooling

cooling_list_lock is covering not just cpufreq_dev_count, but also the
calls to cpufreq_register_notifier() and cpufreq_unregister_notifier().

Since cooling_list_lock is also used within cpufreq_thermal_notifier(),
lockdep reports a potential deadlock. Fix it by testing the condition
under cooling_list_lock and dropping the lock before calling
cpufreq_register_notifier(). And variable cpufreq_dev_count is removed
at the same time, because it's no longer needed after the fix.

Fixes: ae606089 ("thermal: convert cpu_cooling to use an IDA")
Reported-and-Tested-by: default avatarRussell King <linux@armlinux.org.uk>
Signed-off-by: default avatarMatthew Wilcox <mawilcox@microsoft.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com
Signed-off-by: default avatarZhang Rui <rui.zhang@intel.com>
parent 4495c08e
...@@ -107,8 +107,6 @@ struct cpufreq_cooling_device { ...@@ -107,8 +107,6 @@ struct cpufreq_cooling_device {
}; };
static DEFINE_IDA(cpufreq_ida); static DEFINE_IDA(cpufreq_ida);
static unsigned int cpufreq_dev_count;
static DEFINE_MUTEX(cooling_list_lock); static DEFINE_MUTEX(cooling_list_lock);
static LIST_HEAD(cpufreq_dev_list); static LIST_HEAD(cpufreq_dev_list);
...@@ -771,6 +769,7 @@ __cpufreq_cooling_register(struct device_node *np, ...@@ -771,6 +769,7 @@ __cpufreq_cooling_register(struct device_node *np,
unsigned int freq, i, num_cpus; unsigned int freq, i, num_cpus;
int ret; int ret;
struct thermal_cooling_device_ops *cooling_ops; struct thermal_cooling_device_ops *cooling_ops;
bool first;
if (!alloc_cpumask_var(&temp_mask, GFP_KERNEL)) if (!alloc_cpumask_var(&temp_mask, GFP_KERNEL))
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -874,13 +873,14 @@ __cpufreq_cooling_register(struct device_node *np, ...@@ -874,13 +873,14 @@ __cpufreq_cooling_register(struct device_node *np,
cpufreq_dev->cool_dev = cool_dev; cpufreq_dev->cool_dev = cool_dev;
mutex_lock(&cooling_list_lock); mutex_lock(&cooling_list_lock);
/* Register the notifier for first cpufreq cooling device */
first = list_empty(&cpufreq_dev_list);
list_add(&cpufreq_dev->node, &cpufreq_dev_list); list_add(&cpufreq_dev->node, &cpufreq_dev_list);
mutex_unlock(&cooling_list_lock);
/* Register the notifier for first cpufreq cooling device */ if (first)
if (!cpufreq_dev_count++)
cpufreq_register_notifier(&thermal_cpufreq_notifier_block, cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER); CPUFREQ_POLICY_NOTIFIER);
mutex_unlock(&cooling_list_lock);
goto put_policy; goto put_policy;
...@@ -1021,6 +1021,7 @@ EXPORT_SYMBOL(of_cpufreq_power_cooling_register); ...@@ -1021,6 +1021,7 @@ EXPORT_SYMBOL(of_cpufreq_power_cooling_register);
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
{ {
struct cpufreq_cooling_device *cpufreq_dev; struct cpufreq_cooling_device *cpufreq_dev;
bool last;
if (!cdev) if (!cdev)
return; return;
...@@ -1028,14 +1029,15 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) ...@@ -1028,14 +1029,15 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
cpufreq_dev = cdev->devdata; cpufreq_dev = cdev->devdata;
mutex_lock(&cooling_list_lock); mutex_lock(&cooling_list_lock);
list_del(&cpufreq_dev->node);
/* Unregister the notifier for the last cpufreq cooling device */ /* Unregister the notifier for the last cpufreq cooling device */
if (!--cpufreq_dev_count) last = list_empty(&cpufreq_dev_list);
mutex_unlock(&cooling_list_lock);
if (last)
cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
CPUFREQ_POLICY_NOTIFIER); CPUFREQ_POLICY_NOTIFIER);
list_del(&cpufreq_dev->node);
mutex_unlock(&cooling_list_lock);
thermal_cooling_device_unregister(cpufreq_dev->cool_dev); thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
ida_simple_remove(&cpufreq_ida, cpufreq_dev->id); ida_simple_remove(&cpufreq_ida, cpufreq_dev->id);
kfree(cpufreq_dev->dyn_power_table); kfree(cpufreq_dev->dyn_power_table);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment