diff options
author | Peter De Schrijver <pdeschrijver@nvidia.com> | 2012-11-21 17:39:58 +0200 |
---|---|---|
committer | Mrutyunjay Sawant <msawant@nvidia.com> | 2012-12-06 02:52:40 -0800 |
commit | 860cda3e17295324ddd865a51c6f666bd601fed4 (patch) | |
tree | 2d9b1c36712e0b07a174adb9843271cc53b4c5f6 /arch/arm/mach-tegra/cpuquiet.c | |
parent | e1946e679a93b23770157fd0d69b82df11147aab (diff) |
ARM: tegra: avoid race condition between cpufreq and clusterswitch
Avoid a race condition between cpufreq and clusterswitch and also make writes
to no_lp and enable synchronous operations.
bug 1178947
Change-Id: Ib9608f8a0a22be84d3c0916babb7e43c1f6df2e1
Signed-off-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Reviewed-on: http://git-master/r/165751
Reviewed-by: Diwakar Tundlam <dtundlam@nvidia.com>
Reviewed-by: Juha Tukkinen <jtukkinen@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/cpuquiet.c')
-rw-r--r-- | arch/arm/mach-tegra/cpuquiet.c | 57 |
1 files changed, 34 insertions, 23 deletions
diff --git a/arch/arm/mach-tegra/cpuquiet.c b/arch/arm/mach-tegra/cpuquiet.c index 7fe6b644e34b..cf83ff395d7d 100644 --- a/arch/arm/mach-tegra/cpuquiet.c +++ b/arch/arm/mach-tegra/cpuquiet.c @@ -51,6 +51,9 @@ static struct delayed_work cpuquiet_work; static struct kobject *tegra_auto_sysfs_kobject; +static wait_queue_head_t wait_no_lp; +static wait_queue_head_t wait_enable; + static bool no_lp; static bool enable; static unsigned long up_delay; @@ -67,11 +70,6 @@ static struct cpumask cr_online_requests; static struct cpumask cr_offline_requests; enum { - TEGRA_CPQ_DISABLE, - TEGRA_CPQ_ENABLE, -}; - -enum { TEGRA_CPQ_DISABLED = 0, TEGRA_CPQ_ENABLED, TEGRA_CPQ_IDLE, @@ -154,11 +152,11 @@ static void hp_stats_update(unsigned int cpu, bool up) static int update_core_config(unsigned int cpunumber, bool up) { - int ret = -EINVAL; + int ret = 0; unsigned int nr_cpus = num_online_cpus(); if (cpq_state == TEGRA_CPQ_DISABLED || cpunumber >= nr_cpu_ids) - return ret; + return -EINVAL; mutex_lock(tegra_cpu_lock); @@ -229,6 +227,8 @@ static int __apply_cluster_config(int state, int target_state) } } + wake_up_interruptible(&wait_no_lp); + return new_state; } @@ -292,30 +292,34 @@ static void __cpuinit __apply_core_config(void) static void __cpuinit tegra_cpuquiet_work_func(struct work_struct *work) { - int new_cluster, current_cluster = is_lp_cluster(), action; + int new_cluster, current_cluster, action; mutex_lock(tegra_cpu_lock); + current_cluster = is_lp_cluster(); action = cpq_target_state; new_cluster = cpq_target_cluster_state; - mutex_unlock(tegra_cpu_lock); - if (action == TEGRA_CPQ_ENABLED) { hp_init_stats(); cpuquiet_device_free(); pr_info("Tegra cpuquiet clusterswitch enabled\n"); cpq_state = TEGRA_CPQ_ENABLED; cpq_target_state = TEGRA_CPQ_IDLE; + wake_up_interruptible(&wait_enable); } - if (cpq_state == TEGRA_CPQ_DISABLED) + if (cpq_state == TEGRA_CPQ_DISABLED) { + mutex_unlock(tegra_cpu_lock); return; + } - if (action == TEGRA_CPQ_DISABLE) { + if (action == TEGRA_CPQ_DISABLED) { + mutex_unlock(tegra_cpu_lock); cpq_state = TEGRA_CPQ_DISABLED; cpuquiet_device_busy(); pr_info("Tegra cpuquiet clusterswitch disabled\n"); + wake_up_interruptible(&wait_enable); return; } @@ -323,13 +327,16 @@ static void __cpuinit tegra_cpuquiet_work_func(struct work_struct *work) current_cluster = __apply_cluster_config(current_cluster, new_cluster); + mutex_unlock(tegra_cpu_lock); + if (current_cluster == TEGRA_CPQ_LP) cpuquiet_device_busy(); else cpuquiet_device_free(); tegra_cpu_set_speed_cap(NULL); - } + } else + mutex_unlock(tegra_cpu_lock); if (current_cluster == TEGRA_CPQ_G) __apply_core_config(); @@ -386,15 +393,13 @@ void tegra_auto_hotplug_governor(unsigned int cpu_freq, bool suspend) queue_delayed_work( cpuquiet_wq, &cpuquiet_work, up_delay); - } else if (is_lp_cluster() && - (cpu_freq >= idle_top_freq || no_lp)) { + } else if (cpu_freq >= idle_top_freq || no_lp) { cpq_target_cluster_state = TEGRA_CPQ_G; queue_delayed_work(cpuquiet_wq, &cpuquiet_work, up_delay); - } else if (!is_lp_cluster() && !no_lp && - cpu_freq <= idle_bottom_freq) { + } else if (!no_lp && cpu_freq <= idle_bottom_freq) { cpq_target_cluster_state = TEGRA_CPQ_LP; queue_delayed_work(cpuquiet_wq, &cpuquiet_work, @@ -423,17 +428,18 @@ static void delay_callback(struct cpuquiet_attribute *attr) static void enable_callback(struct cpuquiet_attribute *attr) { + int target_state = enable ? TEGRA_CPQ_ENABLED : TEGRA_CPQ_DISABLED; + mutex_lock(tegra_cpu_lock); - if (!enable && cpq_state != TEGRA_CPQ_DISABLED) { - cpq_target_state = TEGRA_CPQ_DISABLED; - } else if (cpq_state == TEGRA_CPQ_DISABLED) { - cpq_target_state = TEGRA_CPQ_ENABLED; + if (cpq_state != target_state) { + cpq_target_state = target_state; + queue_delayed_work(cpuquiet_wq, &cpuquiet_work, 0); } - queue_delayed_work(cpuquiet_wq, &cpuquiet_work, 0); - mutex_unlock(tegra_cpu_lock); + + wait_event_interruptible(wait_enable, cpq_state == target_state); } static void no_lp_callback(struct cpuquiet_attribute *attr) @@ -448,6 +454,8 @@ static void no_lp_callback(struct cpuquiet_attribute *attr) } mutex_unlock(tegra_cpu_lock); + + wait_event_interruptible(wait_no_lp, no_lp ? !is_lp_cluster() : 1); } CPQ_BASIC_ATTRIBUTE(idle_top_freq, 0644, uint); @@ -595,6 +603,9 @@ int __cpuinit tegra_auto_hotplug_init(struct mutex *cpulock) tegra_cpu_lock = cpulock; + init_waitqueue_head(&wait_no_lp); + init_waitqueue_head(&wait_enable); + /* * Not bound to the issuer CPU (=> high-priority), has rescue worker * task, single-threaded, freezable. |