aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2012-12-18 12:01:00 +0530
committerAndrey Konovalov <andrey.konovalov@linaro.org>2013-05-25 13:27:48 +0400
commit742c11a54c736b38211a93406dd8d5ca6cf758f6 (patch)
treec003602d36afa4ec7874d6542fe91dadde25fe79
parent9c2010c231ad92a97b66be2258cf2871a63304f7 (diff)
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 <viresh.kumar@linaro.org>
-rw-r--r--drivers/cpufreq/arm_big_little.c20
1 files 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 <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/export.h>
+#include <linux/mutex.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -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",