diff options
author | Javi Merino <javi.merino@arm.com> | 2015-08-17 14:21:42 -0400 |
---|---|---|
committer | Eduardo Valentin <edubezval@gmail.com> | 2015-09-13 23:33:17 -0400 |
commit | 459ac37506d195713b5e82271a2ac44a777e47df (patch) | |
tree | 7a3071c20fcaa2b5d11b452b1969359ac722b5e6 /drivers/thermal | |
parent | 0847e26a84faa0fcb4572d7e42d0a44cab69a83d (diff) |
thermal: cpu_cooling: don't call kcalloc() under rcu_read_lock
build_dyn_power_table() allocates the power table while holding
rcu_read_lock. kcalloc using GFP_KERNEL may sleep, so it can't be
called in an RCU read-side path.
Move the rcu protection to the part of the function that really needs
it: the part that handles the dev_pm_opp pointer received from
dev_pm_opp_find_freq_ceil(). In the unlikely case that there is an OPP
added to the cpu while this function is running, return -EAGAIN.
Fixes: c36cf0717631 ("thermal: cpu_cooling: implement the power cooling device API")
Cc: Zhang Rui <rui.zhang@intel.com>
Cc: Eduardo Valentin <edubezval@gmail.com>
Signed-off-by: Javi Merino <javi.merino@arm.com>
Signed-off-by: Eduardo Valentin <edubezval@gmail.com>
Diffstat (limited to 'drivers/thermal')
-rw-r--r-- | drivers/thermal/cpu_cooling.c | 47 |
1 files changed, 23 insertions, 24 deletions
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 620dcd405ff6..b32755679395 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c | |||
@@ -262,7 +262,9 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, | |||
262 | * efficiently. Power is stored in mW, frequency in KHz. The | 262 | * efficiently. Power is stored in mW, frequency in KHz. The |
263 | * resulting table is in ascending order. | 263 | * resulting table is in ascending order. |
264 | * | 264 | * |
265 | * Return: 0 on success, -E* on error. | 265 | * Return: 0 on success, -EINVAL if there are no OPPs for any CPUs, |
266 | * -ENOMEM if we run out of memory or -EAGAIN if an OPP was | ||
267 | * added/enabled while the function was executing. | ||
266 | */ | 268 | */ |
267 | static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, | 269 | static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, |
268 | u32 capacitance) | 270 | u32 capacitance) |
@@ -270,11 +272,9 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, | |||
270 | struct power_table *power_table; | 272 | struct power_table *power_table; |
271 | struct dev_pm_opp *opp; | 273 | struct dev_pm_opp *opp; |
272 | struct device *dev = NULL; | 274 | struct device *dev = NULL; |
273 | int num_opps = 0, cpu, i, ret = 0; | 275 | int num_opps = 0, cpu, i; |
274 | unsigned long freq; | 276 | unsigned long freq; |
275 | 277 | ||
276 | rcu_read_lock(); | ||
277 | |||
278 | for_each_cpu(cpu, &cpufreq_device->allowed_cpus) { | 278 | for_each_cpu(cpu, &cpufreq_device->allowed_cpus) { |
279 | dev = get_cpu_device(cpu); | 279 | dev = get_cpu_device(cpu); |
280 | if (!dev) { | 280 | if (!dev) { |
@@ -284,24 +284,20 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, | |||
284 | } | 284 | } |
285 | 285 | ||
286 | num_opps = dev_pm_opp_get_opp_count(dev); | 286 | num_opps = dev_pm_opp_get_opp_count(dev); |
287 | if (num_opps > 0) { | 287 | if (num_opps > 0) |
288 | break; | 288 | break; |
289 | } else if (num_opps < 0) { | 289 | else if (num_opps < 0) |
290 | ret = num_opps; | 290 | return num_opps; |
291 | goto unlock; | ||
292 | } | ||
293 | } | 291 | } |
294 | 292 | ||
295 | if (num_opps == 0) { | 293 | if (num_opps == 0) |
296 | ret = -EINVAL; | 294 | return -EINVAL; |
297 | goto unlock; | ||
298 | } | ||
299 | 295 | ||
300 | power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); | 296 | power_table = kcalloc(num_opps, sizeof(*power_table), GFP_KERNEL); |
301 | if (!power_table) { | 297 | if (!power_table) |
302 | ret = -ENOMEM; | 298 | return -ENOMEM; |
303 | goto unlock; | 299 | |
304 | } | 300 | rcu_read_lock(); |
305 | 301 | ||
306 | for (freq = 0, i = 0; | 302 | for (freq = 0, i = 0; |
307 | opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp); | 303 | opp = dev_pm_opp_find_freq_ceil(dev, &freq), !IS_ERR(opp); |
@@ -309,6 +305,11 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, | |||
309 | u32 freq_mhz, voltage_mv; | 305 | u32 freq_mhz, voltage_mv; |
310 | u64 power; | 306 | u64 power; |
311 | 307 | ||
308 | if (i >= num_opps) { | ||
309 | rcu_read_unlock(); | ||
310 | return -EAGAIN; | ||
311 | } | ||
312 | |||
312 | freq_mhz = freq / 1000000; | 313 | freq_mhz = freq / 1000000; |
313 | voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; | 314 | voltage_mv = dev_pm_opp_get_voltage(opp) / 1000; |
314 | 315 | ||
@@ -326,18 +327,16 @@ static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, | |||
326 | power_table[i].power = power; | 327 | power_table[i].power = power; |
327 | } | 328 | } |
328 | 329 | ||
329 | if (i == 0) { | 330 | rcu_read_unlock(); |
330 | ret = PTR_ERR(opp); | 331 | |
331 | goto unlock; | 332 | if (i != num_opps) |
332 | } | 333 | return PTR_ERR(opp); |
333 | 334 | ||
334 | cpufreq_device->cpu_dev = dev; | 335 | cpufreq_device->cpu_dev = dev; |
335 | cpufreq_device->dyn_power_table = power_table; | 336 | cpufreq_device->dyn_power_table = power_table; |
336 | cpufreq_device->dyn_power_table_entries = i; | 337 | cpufreq_device->dyn_power_table_entries = i; |
337 | 338 | ||
338 | unlock: | 339 | return 0; |
339 | rcu_read_unlock(); | ||
340 | return ret; | ||
341 | } | 340 | } |
342 | 341 | ||
343 | static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device, | 342 | static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device, |