summaryrefslogtreecommitdiffstats
path: root/drivers/thermal
diff options
context:
space:
mode:
authorJavi Merino <javi.merino@arm.com>2015-08-17 14:21:42 -0400
committerEduardo Valentin <edubezval@gmail.com>2015-09-13 23:33:17 -0400
commit459ac37506d195713b5e82271a2ac44a777e47df (patch)
tree7a3071c20fcaa2b5d11b452b1969359ac722b5e6 /drivers/thermal
parent0847e26a84faa0fcb4572d7e42d0a44cab69a83d (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.c47
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 */
267static int build_dyn_power_table(struct cpufreq_cooling_device *cpufreq_device, 269static 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
338unlock: 339 return 0;
339 rcu_read_unlock();
340 return ret;
341} 340}
342 341
343static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device, 342static u32 cpu_freq_to_power(struct cpufreq_cooling_device *cpufreq_device,