diff options
author | Sang-Hun Lee <sanlee@nvidia.com> | 2013-02-10 17:48:24 -0800 |
---|---|---|
committer | Mandar Padmawar <mpadmawar@nvidia.com> | 2013-02-18 06:57:29 -0800 |
commit | 91bdd64c3c80480195a5ccdb8cef969ca5a18afc (patch) | |
tree | 53df067d3ce7e4296353473372b6c5b12b8823d2 /arch/arm/mach-tegra/cpu-tegra.c | |
parent | d68e3878da280659e126db7ad0345137d05ebf3e (diff) |
arm: mach-tegra: resolve tegra_cpu_lock race condition
Problem description:
- lock used in cpuquiet, cpu-tegra3.c, tegra2-throttle.c, and tegra3_throttle
originate from cpu-tegra.c, tegra_cpu_lock
- edp_update_limit and tegra_auto_hotplug_governor need a protection from
tegra_cpu_lock. These are also called by tegra_cpu_set_speed_cap
- Some callers of tegra_cpu_set_speed_cap do not acquire tegra_cpu_lock,
but some do
Fix description:
- Create a locked variant and unlocked variant of tegra_cpu_set_speed_cap
to make it explicit that tegra_cpu_lock is needed for tegra_cpu_set_speed_cap
- Replace existing calls with new variants of tegra_cpu_set_speed_cap appropriately
Bug 1225764
Change-Id: I8aa6356df278375e3a9105023f66c8286e3fdbef
Signed-off-by: Sang-Hun Lee <sanlee@nvidia.com>
Reviewed-on: http://git-master/r/199238
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/cpu-tegra.c')
-rw-r--r-- | arch/arm/mach-tegra/cpu-tegra.c | 40 |
1 files changed, 27 insertions, 13 deletions
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c index ca04e8462e07..ede69d7b4c8d 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -7,7 +7,7 @@ * Colin Cross <ccross@google.com> * Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation * - * Copyright (C) 2010-2012 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2010-2013 NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -68,7 +68,7 @@ static int force_policy_max_set(const char *arg, const struct kernel_param *kp) ret = param_set_bool(arg, kp); if ((ret == 0) && (old_policy != force_policy_max)) - tegra_cpu_set_speed_cap(NULL); + tegra_cpu_set_speed_cap_locked(NULL); mutex_unlock(&tegra_cpu_lock); return ret; @@ -101,7 +101,7 @@ static inline void _cpu_user_cap_set_locked(void) cpu_user_cap = freq_table[i].frequency; } #endif - tegra_cpu_set_speed_cap(NULL); + tegra_cpu_set_speed_cap_locked(NULL); } void tegra_cpu_user_cap_set(unsigned int speed_khz) @@ -214,10 +214,11 @@ static unsigned int edp_predict_limit(unsigned int cpus) return limit; } +/* Must be called while holding cpu_tegra_lock */ static void edp_update_limit(void) { unsigned int limit = edp_predict_limit(cpumask_weight(&edp_cpumask)); - + BUG_ON(!mutex_is_locked(&tegra_cpu_lock)); #ifdef CONFIG_TEGRA_EDP_EXACT_FREQ edp_limit = limit; #else @@ -265,7 +266,7 @@ int tegra_edp_set_cur_state(struct thermal_cooling_device *cdev, tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, true, 0); if (target_cpu_speed[0]) { edp_update_limit(); - tegra_cpu_set_speed_cap(NULL); + tegra_cpu_set_speed_cap_locked(NULL); } tegra_cpu_dvfs_alter(edp_thermal_index, &edp_cpumask, false, 0); mutex_unlock(&tegra_cpu_lock); @@ -301,7 +302,7 @@ int tegra_system_edp_alarm(bool alarm) or alarm is canceled */ if (target_cpu_speed[0]) { edp_update_limit(); - ret = tegra_cpu_set_speed_cap(NULL); + ret = tegra_cpu_set_speed_cap_locked(NULL); } if (!ret || !alarm) tegra_edp_throttle_cpu_now(0); @@ -361,7 +362,7 @@ static int tegra_cpu_edp_notify( cpu_speed = tegra_getspeed(0); new_speed = edp_governor_speed(cpu_speed); if (new_speed < cpu_speed) { - ret = tegra_cpu_set_speed_cap(NULL); + ret = tegra_cpu_set_speed_cap_locked(NULL); printk(KERN_DEBUG "cpu-tegra:%sforce EDP limit %u kHz" "\n", ret ? " failed to " : " ", new_speed); } @@ -380,7 +381,7 @@ static int tegra_cpu_edp_notify( tegra_cpu_dvfs_alter( edp_thermal_index, &edp_cpumask, true, event); edp_update_limit(); - tegra_cpu_set_speed_cap(NULL); + tegra_cpu_set_speed_cap_locked(NULL); mutex_unlock(&tegra_cpu_lock); break; } @@ -617,7 +618,7 @@ void tegra_cpu_set_volt_cap(unsigned int cap) mutex_lock(&tegra_cpu_lock); if (cap != volt_capped_speed) { volt_capped_speed = cap; - tegra_cpu_set_speed_cap(NULL); + tegra_cpu_set_speed_cap_locked(NULL); } mutex_unlock(&tegra_cpu_lock); if (cap) @@ -633,11 +634,12 @@ static unsigned int volt_cap_speed(unsigned int requested_speed) return requested_speed; } -int tegra_cpu_set_speed_cap(unsigned int *speed_cap) +/* Must be called with tegra_cpu_lock held */ +int tegra_cpu_set_speed_cap_locked(unsigned int *speed_cap) { int ret = 0; unsigned int new_speed = tegra_cpu_highest_speed(); - + BUG_ON(!mutex_is_locked(&tegra_cpu_lock)); #ifdef CONFIG_TEGRA_EDP_LIMITS edp_update_limit(); #endif @@ -658,6 +660,16 @@ int tegra_cpu_set_speed_cap(unsigned int *speed_cap) return ret; } +int tegra_cpu_set_speed_cap(unsigned int *speed_cap) +{ + int ret; + mutex_lock(&tegra_cpu_lock); + ret = tegra_cpu_set_speed_cap_locked(speed_cap); + mutex_unlock(&tegra_cpu_lock); + return ret; +} + + int tegra_suspended_target(unsigned int target_freq) { unsigned int new_speed = target_freq; @@ -691,7 +703,7 @@ static int tegra_target(struct cpufreq_policy *policy, freq = freq_table[idx].frequency; target_cpu_speed[policy->cpu] = freq; - ret = tegra_cpu_set_speed_cap(&new_speed); + ret = tegra_cpu_set_speed_cap_locked(&new_speed); _out: mutex_unlock(&tegra_cpu_lock); @@ -714,7 +726,7 @@ static int tegra_pm_notify(struct notifier_block *nb, unsigned long event, unsigned int freq; is_suspended = false; tegra_cpu_edp_init(true); - tegra_cpu_set_speed_cap(&freq); + tegra_cpu_set_speed_cap_locked(&freq); pr_info("Tegra cpufreq resume: restoring frequency to %d kHz\n", freq); } @@ -847,7 +859,9 @@ static int __init tegra_cpufreq_init(void) return ret; freq_table = table_data->freq_table; + mutex_lock(&tegra_cpu_lock); tegra_cpu_edp_init(false); + mutex_unlock(&tegra_cpu_lock); ret = register_pm_notifier(&tegra_cpu_pm_notifier); |