diff options
author | Matthew Wilcox <mawilcox@microsoft.com> | 2017-03-10 13:33:28 -0500 |
---|---|---|
committer | Zhang Rui <rui.zhang@intel.com> | 2017-03-12 22:06:04 -0400 |
commit | 088db931e065bd3b159fa2e588eab859ef0d9d23 (patch) | |
tree | 7211f7bc220cfc430e37dc4fa37dec978efbe0f7 | |
parent | 4495c08e84729385774601b5146d51d9e5849f81 (diff) |
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: ae606089621e ("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>
-rw-r--r-- | drivers/thermal/cpu_cooling.c | 20 |
1 files changed, 11 insertions, 9 deletions
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 91048eeca28b..11b5ea685518 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c | |||
@@ -107,8 +107,6 @@ struct cpufreq_cooling_device { | |||
107 | }; | 107 | }; |
108 | static DEFINE_IDA(cpufreq_ida); | 108 | static DEFINE_IDA(cpufreq_ida); |
109 | 109 | ||
110 | static unsigned int cpufreq_dev_count; | ||
111 | |||
112 | static DEFINE_MUTEX(cooling_list_lock); | 110 | static DEFINE_MUTEX(cooling_list_lock); |
113 | static LIST_HEAD(cpufreq_dev_list); | 111 | static LIST_HEAD(cpufreq_dev_list); |
114 | 112 | ||
@@ -771,6 +769,7 @@ __cpufreq_cooling_register(struct device_node *np, | |||
771 | unsigned int freq, i, num_cpus; | 769 | unsigned int freq, i, num_cpus; |
772 | int ret; | 770 | int ret; |
773 | struct thermal_cooling_device_ops *cooling_ops; | 771 | struct thermal_cooling_device_ops *cooling_ops; |
772 | bool first; | ||
774 | 773 | ||
775 | if (!alloc_cpumask_var(&temp_mask, GFP_KERNEL)) | 774 | if (!alloc_cpumask_var(&temp_mask, GFP_KERNEL)) |
776 | return ERR_PTR(-ENOMEM); | 775 | return ERR_PTR(-ENOMEM); |
@@ -874,13 +873,14 @@ __cpufreq_cooling_register(struct device_node *np, | |||
874 | cpufreq_dev->cool_dev = cool_dev; | 873 | cpufreq_dev->cool_dev = cool_dev; |
875 | 874 | ||
876 | mutex_lock(&cooling_list_lock); | 875 | mutex_lock(&cooling_list_lock); |
876 | /* Register the notifier for first cpufreq cooling device */ | ||
877 | first = list_empty(&cpufreq_dev_list); | ||
877 | list_add(&cpufreq_dev->node, &cpufreq_dev_list); | 878 | list_add(&cpufreq_dev->node, &cpufreq_dev_list); |
879 | mutex_unlock(&cooling_list_lock); | ||
878 | 880 | ||
879 | /* Register the notifier for first cpufreq cooling device */ | 881 | if (first) |
880 | if (!cpufreq_dev_count++) | ||
881 | cpufreq_register_notifier(&thermal_cpufreq_notifier_block, | 882 | cpufreq_register_notifier(&thermal_cpufreq_notifier_block, |
882 | CPUFREQ_POLICY_NOTIFIER); | 883 | CPUFREQ_POLICY_NOTIFIER); |
883 | mutex_unlock(&cooling_list_lock); | ||
884 | 884 | ||
885 | goto put_policy; | 885 | goto put_policy; |
886 | 886 | ||
@@ -1021,6 +1021,7 @@ EXPORT_SYMBOL(of_cpufreq_power_cooling_register); | |||
1021 | void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) | 1021 | void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) |
1022 | { | 1022 | { |
1023 | struct cpufreq_cooling_device *cpufreq_dev; | 1023 | struct cpufreq_cooling_device *cpufreq_dev; |
1024 | bool last; | ||
1024 | 1025 | ||
1025 | if (!cdev) | 1026 | if (!cdev) |
1026 | return; | 1027 | return; |
@@ -1028,14 +1029,15 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) | |||
1028 | cpufreq_dev = cdev->devdata; | 1029 | cpufreq_dev = cdev->devdata; |
1029 | 1030 | ||
1030 | mutex_lock(&cooling_list_lock); | 1031 | mutex_lock(&cooling_list_lock); |
1032 | list_del(&cpufreq_dev->node); | ||
1031 | /* Unregister the notifier for the last cpufreq cooling device */ | 1033 | /* Unregister the notifier for the last cpufreq cooling device */ |
1032 | if (!--cpufreq_dev_count) | 1034 | last = list_empty(&cpufreq_dev_list); |
1035 | mutex_unlock(&cooling_list_lock); | ||
1036 | |||
1037 | if (last) | ||
1033 | cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, | 1038 | cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, |
1034 | CPUFREQ_POLICY_NOTIFIER); | 1039 | CPUFREQ_POLICY_NOTIFIER); |
1035 | 1040 | ||
1036 | list_del(&cpufreq_dev->node); | ||
1037 | mutex_unlock(&cooling_list_lock); | ||
1038 | |||
1039 | thermal_cooling_device_unregister(cpufreq_dev->cool_dev); | 1041 | thermal_cooling_device_unregister(cpufreq_dev->cool_dev); |
1040 | ida_simple_remove(&cpufreq_ida, cpufreq_dev->id); | 1042 | ida_simple_remove(&cpufreq_ida, cpufreq_dev->id); |
1041 | kfree(cpufreq_dev->dyn_power_table); | 1043 | kfree(cpufreq_dev->dyn_power_table); |