aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViresh Kumar <viresh.kumar@linaro.org>2013-06-19 00:46:55 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-06-27 15:49:55 -0400
commit7c30ed532cf798a8d924562f2f44d03d7652f7a7 (patch)
tree6198099d8f7fc69c3510908d539df878857684cd
parente11158c0c9ab59d46bb70953f6275643a7a01fa1 (diff)
cpufreq: make sure frequency transitions are serialized
Whenever we are changing frequency of a cpu, we are calling PRECHANGE and POSTCHANGE notifiers. They must be serialized. i.e. PRECHANGE or POSTCHANGE shouldn't be called twice contiguously. This can happen due to bugs in users of __cpufreq_driver_target() or actual cpufreq drivers who are sending these notifiers. This patch adds some protection against this. Now, we keep track of the last transaction and see if something went wrong. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/cpufreq/cpufreq.c14
-rw-r--r--include/linux/cpufreq.h1
2 files changed, 15 insertions, 0 deletions
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index d976e222f10f..03b3b69f64a7 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -312,6 +312,12 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
312 switch (state) { 312 switch (state) {
313 313
314 case CPUFREQ_PRECHANGE: 314 case CPUFREQ_PRECHANGE:
315 if (WARN(policy->transition_ongoing,
316 "In middle of another frequency transition\n"))
317 return;
318
319 policy->transition_ongoing = true;
320
315 /* detect if the driver reported a value as "old frequency" 321 /* detect if the driver reported a value as "old frequency"
316 * which is not equal to what the cpufreq core thinks is 322 * which is not equal to what the cpufreq core thinks is
317 * "old frequency". 323 * "old frequency".
@@ -331,6 +337,12 @@ static void __cpufreq_notify_transition(struct cpufreq_policy *policy,
331 break; 337 break;
332 338
333 case CPUFREQ_POSTCHANGE: 339 case CPUFREQ_POSTCHANGE:
340 if (WARN(!policy->transition_ongoing,
341 "No frequency transition in progress\n"))
342 return;
343
344 policy->transition_ongoing = false;
345
334 adjust_jiffies(CPUFREQ_POSTCHANGE, freqs); 346 adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);
335 pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new, 347 pr_debug("FREQ: %lu - CPU: %lu", (unsigned long)freqs->new,
336 (unsigned long)freqs->cpu); 348 (unsigned long)freqs->cpu);
@@ -1539,6 +1551,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
1539 1551
1540 if (cpufreq_disabled()) 1552 if (cpufreq_disabled())
1541 return -ENODEV; 1553 return -ENODEV;
1554 if (policy->transition_ongoing)
1555 return -EBUSY;
1542 1556
1543 /* Make sure that target_freq is within supported range */ 1557 /* Make sure that target_freq is within supported range */
1544 if (target_freq > policy->max) 1558 if (target_freq > policy->max)
diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h
index 3c7ee2f90370..c0bc7374445e 100644
--- a/include/linux/cpufreq.h
+++ b/include/linux/cpufreq.h
@@ -119,6 +119,7 @@ struct cpufreq_policy {
119 119
120 struct kobject kobj; 120 struct kobject kobj;
121 struct completion kobj_unregister; 121 struct completion kobj_unregister;
122 bool transition_ongoing; /* Tracks transition status */
122}; 123};
123 124
124#define CPUFREQ_ADJUST (0) 125#define CPUFREQ_ADJUST (0)