Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
Signed-off-by: default avatarZhang Rui <rui.zhang@intel.com>
parent 4495c08e
Loading
Loading
Loading
Loading
+11 −9
Original line number Diff line number Diff line
@@ -107,8 +107,6 @@ struct cpufreq_cooling_device {
};
static DEFINE_IDA(cpufreq_ida);

static unsigned int cpufreq_dev_count;

static DEFINE_MUTEX(cooling_list_lock);
static LIST_HEAD(cpufreq_dev_list);

@@ -771,6 +769,7 @@ __cpufreq_cooling_register(struct device_node *np,
	unsigned int freq, i, num_cpus;
	int ret;
	struct thermal_cooling_device_ops *cooling_ops;
	bool first;

	if (!alloc_cpumask_var(&temp_mask, GFP_KERNEL))
		return ERR_PTR(-ENOMEM);
@@ -874,13 +873,14 @@ __cpufreq_cooling_register(struct device_node *np,
	cpufreq_dev->cool_dev = cool_dev;

	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);
	mutex_unlock(&cooling_list_lock);

	/* Register the notifier for first cpufreq cooling device */
	if (!cpufreq_dev_count++)
	if (first)
		cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
					  CPUFREQ_POLICY_NOTIFIER);
	mutex_unlock(&cooling_list_lock);

	goto put_policy;

@@ -1021,6 +1021,7 @@ EXPORT_SYMBOL(of_cpufreq_power_cooling_register);
void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
{
	struct cpufreq_cooling_device *cpufreq_dev;
	bool last;

	if (!cdev)
		return;
@@ -1028,14 +1029,15 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev)
	cpufreq_dev = cdev->devdata;

	mutex_lock(&cooling_list_lock);
	list_del(&cpufreq_dev->node);
	/* 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_POLICY_NOTIFIER);

	list_del(&cpufreq_dev->node);
	mutex_unlock(&cooling_list_lock);

	thermal_cooling_device_unregister(cpufreq_dev->cool_dev);
	ida_simple_remove(&cpufreq_ida, cpufreq_dev->id);
	kfree(cpufreq_dev->dyn_power_table);