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: ae60608962
("thermal: convert cpu_cooling to use an IDA")
Reported-and-Tested-by: Russell King <linux@armlinux.org.uk>
Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com
Signed-off-by: Zhang Rui <rui.zhang@intel.com>
This commit is contained in:
parent
4495c08e84
commit
088db931e0
|
@ -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,
|
||||||
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,
|
||||||
cpufreq_dev->cool_dev = cool_dev;
|
cpufreq_dev->cool_dev = cool_dev;
|
||||||
|
|
||||||
mutex_lock(&cooling_list_lock);
|
mutex_lock(&cooling_list_lock);
|
||||||
list_add(&cpufreq_dev->node, &cpufreq_dev_list);
|
|
||||||
|
|
||||||
/* Register the notifier for first cpufreq cooling device */
|
/* Register the notifier for first cpufreq cooling device */
|
||||||
if (!cpufreq_dev_count++)
|
first = list_empty(&cpufreq_dev_list);
|
||||||
|
list_add(&cpufreq_dev->node, &cpufreq_dev_list);
|
||||||
|
mutex_unlock(&cooling_list_lock);
|
||||||
|
|
||||||
|
if (first)
|
||||||
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);
|
||||||
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)
|
||||||
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);
|
||||||
|
|
Loading…
Reference in New Issue