From 742c11a54c736b38211a93406dd8d5ca6cf758f6 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 18 Dec 2012 12:01:00 +0530 Subject: cpufreq: arm_big_little: Protect bl_cpufreq_set_rate() routine again cpu races It is possible that different cpus may race against setting frequency of the same cluster. We need per-cluster locks to prevent that. Signed-off-by: Viresh Kumar --- drivers/cpufreq/arm_big_little.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index a3c9fbc9ba7..25fbf68ce05 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -58,6 +59,8 @@ static unsigned int clk_little_max; /* Maximum clock frequency (Little) */ static DEFINE_PER_CPU(unsigned int, physical_cluster); static DEFINE_PER_CPU(unsigned int, cpu_last_req_freq); +static struct mutex cluster_lock[MAX_CLUSTERS]; + /* * Functions to get the current status. * @@ -116,6 +119,8 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate) u32 new_rate, prev_rate; int ret; + mutex_lock(&cluster_lock[new_cluster]); + prev_rate = per_cpu(cpu_last_req_freq, cpu); per_cpu(cpu_last_req_freq, cpu) = rate; per_cpu(physical_cluster, cpu) = new_cluster; @@ -131,14 +136,19 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate) __func__, cpu, old_cluster, new_cluster, new_rate); ret = clk_set_rate(clk[new_cluster], new_rate * 1000); - if (ret) { + if (WARN_ON(ret)) { pr_err("clk_set_rate failed: %d, new cluster: %d\n", ret, new_cluster); per_cpu(cpu_last_req_freq, cpu) = prev_rate; per_cpu(physical_cluster, cpu) = old_cluster; + + mutex_unlock(&cluster_lock[new_cluster]); + return ret; } + mutex_unlock(&cluster_lock[new_cluster]); + /* Recalc freq for old cluster when switching clusters */ if (old_cluster != new_cluster) { pr_debug("%s: cpu: %d, old cluster: %d, new cluster: %d\n", @@ -147,6 +157,8 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate) /* Switch cluster */ bL_switch_request(cpu, new_cluster); + mutex_lock(&cluster_lock[old_cluster]); + /* Set freq of old cluster if there are cpus left on it */ new_rate = find_cluster_maxfreq(old_cluster); new_rate = ACTUAL_FREQ(old_cluster, new_rate); @@ -159,6 +171,7 @@ bL_cpufreq_set_rate(u32 cpu, u32 old_cluster, u32 new_cluster, u32 rate) pr_err("%s: clk_set_rate failed: %d, old cluster: %d\n", __func__, ret, old_cluster); } + mutex_unlock(&cluster_lock[old_cluster]); } return 0; @@ -506,7 +519,7 @@ static struct notifier_block bL_switcher_notifier = { int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) { - int ret; + int ret, i; if (arm_bL_ops) { pr_debug("%s: Already registered: %s, exiting\n", __func__, @@ -524,6 +537,9 @@ int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops) ret = bL_switcher_get_enabled(); set_switching_enabled(ret); + for (i = 0; i < MAX_CLUSTERS; i++) + mutex_init(&cluster_lock[i]); + ret = cpufreq_register_driver(&bL_cpufreq_driver); if (ret) { pr_info("%s: Failed registering platform driver: %s, err: %d\n", -- cgit v1.2.3