summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/cpu-tegra.c
diff options
context:
space:
mode:
authorSang-Hun Lee <sanlee@nvidia.com>2013-02-10 17:48:24 -0800
committerMandar Padmawar <mpadmawar@nvidia.com>2013-02-18 06:57:29 -0800
commit91bdd64c3c80480195a5ccdb8cef969ca5a18afc (patch)
tree53df067d3ce7e4296353473372b6c5b12b8823d2 /arch/arm/mach-tegra/cpu-tegra.c
parentd68e3878da280659e126db7ad0345137d05ebf3e (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.c40
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);