summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSang-Hun Lee <sanlee@nvidia.com>2013-02-10 17:48:24 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 13:02:37 -0700
commit89f99a20306f418d161db2f09d76edcc995525d7 (patch)
treef5fd54274ee7a5e1ab06732eab1ffaaa717c5cff
parent68b998198056a5c734a319d11e451da4f04867c1 (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 (cherry picked from commit 91bdd64c3c80480195a5ccdb8cef969ca5a18afc) Reviewed-on: http://git-master/r/207640 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c40
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.h3
-rw-r--r--arch/arm/mach-tegra/cpu-tegra3.c11
-rw-r--r--arch/arm/mach-tegra/cpuquiet.c7
-rw-r--r--arch/arm/mach-tegra/tegra3_throttle.c5
5 files changed, 40 insertions, 26 deletions
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index d864e9b96ba4..854f34777b8f 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
@@ -66,7 +66,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;
@@ -99,7 +99,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)
@@ -212,10 +212,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
@@ -263,7 +264,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);
@@ -299,7 +300,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);
@@ -359,7 +360,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);
}
@@ -378,7 +379,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;
}
@@ -615,7 +616,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)
@@ -631,11 +632,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
@@ -656,6 +658,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;
@@ -689,7 +701,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);
@@ -712,7 +724,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);
}
@@ -844,7 +856,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);
diff --git a/arch/arm/mach-tegra/cpu-tegra.h b/arch/arm/mach-tegra/cpu-tegra.h
index 749ec6345cfa..84504bb0ccc1 100644
--- a/arch/arm/mach-tegra/cpu-tegra.h
+++ b/arch/arm/mach-tegra/cpu-tegra.h
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/cpu-tegra.h
*
- * Copyright (c) 2011-2012, NVIDIA Corporation.
+ * Copyright (c) 2011-2013, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
unsigned int tegra_getspeed(unsigned int cpu);
int tegra_update_cpu_speed(unsigned long rate);
int tegra_cpu_set_speed_cap(unsigned int *speed_cap);
+int tegra_cpu_set_speed_cap_locked(unsigned int *speed_cap);
void tegra_cpu_set_volt_cap(unsigned int cap);
unsigned int tegra_count_slow_cpus(unsigned long speed_limit);
unsigned int tegra_get_slowest_cpu_n(void);
diff --git a/arch/arm/mach-tegra/cpu-tegra3.c b/arch/arm/mach-tegra/cpu-tegra3.c
index f26f449b482b..d7d0250e424e 100644
--- a/arch/arm/mach-tegra/cpu-tegra3.c
+++ b/arch/arm/mach-tegra/cpu-tegra3.c
@@ -3,7 +3,7 @@
*
* CPU auto-hotplug for Tegra3 CPUs
*
- * Copyright (c) 2011-2012, NVIDIA Corporation. All rights reserved.
+ * Copyright (c) 2011-2013, NVIDIA Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,6 +43,7 @@
#define UP2Gn_DELAY_MS 100
#define DOWN_DELAY_MS 2000
+/* tegra3_cpu_lock is tegra_cpu_lock from cpu-tegra.c */
static struct mutex *tegra3_cpu_lock;
static struct workqueue_struct *hotplug_wq;
@@ -160,7 +161,7 @@ static int hp_state_set(const char *arg, const struct kernel_param *kp)
hp_init_stats();
}
/* catch-up with governor target speed */
- tegra_cpu_set_speed_cap(NULL);
+ tegra_cpu_set_speed_cap_locked(NULL);
}
} else
pr_warn("%s: unable to set tegra hotplug state %s\n",
@@ -295,7 +296,7 @@ static void __cpuinit tegra_auto_hotplug_work_func(struct work_struct *work)
hp_stats_update(CONFIG_NR_CPUS, true);
hp_stats_update(0, false);
/* catch-up with governor target speed */
- tegra_cpu_set_speed_cap(NULL);
+ tegra_cpu_set_speed_cap_locked(NULL);
break;
}
}
@@ -309,7 +310,7 @@ static void __cpuinit tegra_auto_hotplug_work_func(struct work_struct *work)
hp_stats_update(CONFIG_NR_CPUS, false);
hp_stats_update(0, true);
/* catch-up with governor target speed */
- tegra_cpu_set_speed_cap(NULL);
+ tegra_cpu_set_speed_cap_locked(NULL);
}
} else {
switch (tegra_cpu_speed_balance()) {
@@ -373,7 +374,7 @@ static int min_cpus_notify(struct notifier_block *nb, unsigned long n, void *p)
}
}
/* update governor state machine */
- tegra_cpu_set_speed_cap(NULL);
+ tegra_cpu_set_speed_cap_locked(NULL);
mutex_unlock(tegra3_cpu_lock);
return NOTIFY_OK;
}
diff --git a/arch/arm/mach-tegra/cpuquiet.c b/arch/arm/mach-tegra/cpuquiet.c
index a617fa262f46..a66cd52f435f 100644
--- a/arch/arm/mach-tegra/cpuquiet.c
+++ b/arch/arm/mach-tegra/cpuquiet.c
@@ -3,7 +3,7 @@
*
* Cpuquiet driver for Tegra CPUs
*
- * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2012-2013 NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -371,6 +371,7 @@ static void __cpuinit tegra_cpuquiet_work_func(struct work_struct *work)
current_cluster = __apply_cluster_config(current_cluster,
new_cluster);
+ tegra_cpu_set_speed_cap_locked(NULL);
mutex_unlock(tegra_cpu_lock);
if (current_cluster == TEGRA_CPQ_LP)
@@ -378,9 +379,9 @@ static void __cpuinit tegra_cpuquiet_work_func(struct work_struct *work)
else
cpuquiet_device_free();
- tegra_cpu_set_speed_cap(NULL);
- } else
+ } else {
mutex_unlock(tegra_cpu_lock);
+ }
if (current_cluster == TEGRA_CPQ_G)
__apply_core_config();
diff --git a/arch/arm/mach-tegra/tegra3_throttle.c b/arch/arm/mach-tegra/tegra3_throttle.c
index 62ba41b5b79d..90ab7c4b6dec 100644
--- a/arch/arm/mach-tegra/tegra3_throttle.c
+++ b/arch/arm/mach-tegra/tegra3_throttle.c
@@ -33,6 +33,7 @@
#include "clock.h"
#include "cpu-tegra.h"
+/* cpu_throttle_lock is tegra_cpu_lock from cpu-tegra.c */
static struct mutex *cpu_throttle_lock;
static DEFINE_MUTEX(bthrot_list_lock);
static LIST_HEAD(bthrot_list);
@@ -218,9 +219,7 @@ tegra_throttle_set_cur_state(struct thermal_cooling_device *cdev,
if (cur_state == 0) {
tegra_throttle_cap_freqs_update(NULL, -1, 1);/* uncap freqs */
- mutex_lock(cpu_throttle_lock);
tegra_cpu_set_speed_cap(NULL);
- mutex_unlock(cpu_throttle_lock);
} else {
if (cur_state == 1 && direction == 0)
bthrot->throttle_count++;
@@ -234,9 +233,7 @@ tegra_throttle_set_cur_state(struct thermal_cooling_device *cdev,
bthrot->cpu_cap_freq =
bthrot->throt_tab[index].cap_freqs[0];
- mutex_lock(cpu_throttle_lock);
tegra_cpu_set_speed_cap(NULL);
- mutex_unlock(cpu_throttle_lock);
}
}