aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTodd Poynor2012-12-18 19:50:44 -0600
committerArve Hjønnevåg2013-02-19 19:56:09 -0600
commita04276770ecffcec07178c74df30687a7dedf7dc (patch)
treef79645deae346e4609a26a45f09e59587c3d4259
parent1e18b4ba037b89caa3a3e5653e50e7f88e5b8127 (diff)
downloadkernel-common-a04276770ecffcec07178c74df30687a7dedf7dc.tar.gz
kernel-common-a04276770ecffcec07178c74df30687a7dedf7dc.tar.xz
kernel-common-a04276770ecffcec07178c74df30687a7dedf7dc.zip
cpufreq: interactive: fix racy timer stopping
When stopping the governor, del_timer_sync() can race against an invocation of the idle notifier callback, which has the potential to reactivate the timer. To fix this issue, a read-write semaphore is used. Multiple readers are allowed as long as pcpu->governor_enabled is true. However it can be moved to false only after taking a write semaphore which would wait for any on-going asynchronous activities to complete and prevent any more of those activities to be initiated. [toddpoynor@google.com: cosmetic and commit text changes] Change-Id: Ib51165a735d73dcf964a06754c48bdc1913e13d0 Signed-off-by: Nicolas Pitre <nicolas.pitre@linaro.org>
-rw-r--r--drivers/cpufreq/cpufreq_interactive.c38
1 files changed, 28 insertions, 10 deletions
diff --git a/drivers/cpufreq/cpufreq_interactive.c b/drivers/cpufreq/cpufreq_interactive.c
index 0c00b6db260..f3d187604d9 100644
--- a/drivers/cpufreq/cpufreq_interactive.c
+++ b/drivers/cpufreq/cpufreq_interactive.c
@@ -21,14 +21,13 @@
21#include <linux/cpufreq.h> 21#include <linux/cpufreq.h>
22#include <linux/module.h> 22#include <linux/module.h>
23#include <linux/moduleparam.h> 23#include <linux/moduleparam.h>
24#include <linux/mutex.h> 24#include <linux/rwsem.h>
25#include <linux/sched.h> 25#include <linux/sched.h>
26#include <linux/tick.h> 26#include <linux/tick.h>
27#include <linux/time.h> 27#include <linux/time.h>
28#include <linux/timer.h> 28#include <linux/timer.h>
29#include <linux/workqueue.h> 29#include <linux/workqueue.h>
30#include <linux/kthread.h> 30#include <linux/kthread.h>
31#include <linux/mutex.h>
32#include <linux/slab.h> 31#include <linux/slab.h>
33#include <asm/cputime.h> 32#include <asm/cputime.h>
34 33
@@ -51,6 +50,7 @@ struct cpufreq_interactive_cpuinfo {
51 unsigned int floor_freq; 50 unsigned int floor_freq;
52 u64 floor_validate_time; 51 u64 floor_validate_time;
53 u64 hispeed_validate_time; 52 u64 hispeed_validate_time;
53 struct rw_semaphore enable_sem;
54 int governor_enabled; 54 int governor_enabled;
55}; 55};
56 56
@@ -278,8 +278,8 @@ static void cpufreq_interactive_timer(unsigned long data)
278 unsigned long flags; 278 unsigned long flags;
279 bool boosted; 279 bool boosted;
280 280
281 smp_rmb(); 281 if (!down_read_trylock(&pcpu->enable_sem))
282 282 return;
283 if (!pcpu->governor_enabled) 283 if (!pcpu->governor_enabled)
284 goto exit; 284 goto exit;
285 285
@@ -386,6 +386,7 @@ rearm:
386 cpufreq_interactive_timer_resched(pcpu); 386 cpufreq_interactive_timer_resched(pcpu);
387 387
388exit: 388exit:
389 up_read(&pcpu->enable_sem);
389 return; 390 return;
390} 391}
391 392
@@ -395,8 +396,12 @@ static void cpufreq_interactive_idle_start(void)
395 &per_cpu(cpuinfo, smp_processor_id()); 396 &per_cpu(cpuinfo, smp_processor_id());
396 int pending; 397 int pending;
397 398
398 if (!pcpu->governor_enabled) 399 if (!down_read_trylock(&pcpu->enable_sem))
400 return;
401 if (!pcpu->governor_enabled) {
402 up_read(&pcpu->enable_sem);
399 return; 403 return;
404 }
400 405
401 pending = timer_pending(&pcpu->cpu_timer); 406 pending = timer_pending(&pcpu->cpu_timer);
402 407
@@ -413,6 +418,7 @@ static void cpufreq_interactive_idle_start(void)
413 cpufreq_interactive_timer_resched(pcpu); 418 cpufreq_interactive_timer_resched(pcpu);
414 } 419 }
415 420
421 up_read(&pcpu->enable_sem);
416} 422}
417 423
418static void cpufreq_interactive_idle_end(void) 424static void cpufreq_interactive_idle_end(void)
@@ -420,8 +426,12 @@ static void cpufreq_interactive_idle_end(void)
420 struct cpufreq_interactive_cpuinfo *pcpu = 426 struct cpufreq_interactive_cpuinfo *pcpu =
421 &per_cpu(cpuinfo, smp_processor_id()); 427 &per_cpu(cpuinfo, smp_processor_id());
422 428
423 if (!pcpu->governor_enabled) 429 if (!down_read_trylock(&pcpu->enable_sem))
430 return;
431 if (!pcpu->governor_enabled) {
432 up_read(&pcpu->enable_sem);
424 return; 433 return;
434 }
425 435
426 /* Arm the timer for 1-2 ticks later if not already. */ 436 /* Arm the timer for 1-2 ticks later if not already. */
427 if (!timer_pending(&pcpu->cpu_timer)) { 437 if (!timer_pending(&pcpu->cpu_timer)) {
@@ -431,6 +441,8 @@ static void cpufreq_interactive_idle_end(void)
431 del_timer(&pcpu->cpu_slack_timer); 441 del_timer(&pcpu->cpu_slack_timer);
432 cpufreq_interactive_timer(smp_processor_id()); 442 cpufreq_interactive_timer(smp_processor_id());
433 } 443 }
444
445 up_read(&pcpu->enable_sem);
434} 446}
435 447
436static int cpufreq_interactive_speedchange_task(void *data) 448static int cpufreq_interactive_speedchange_task(void *data)
@@ -465,10 +477,12 @@ static int cpufreq_interactive_speedchange_task(void *data)
465 unsigned int max_freq = 0; 477 unsigned int max_freq = 0;
466 478
467 pcpu = &per_cpu(cpuinfo, cpu); 479 pcpu = &per_cpu(cpuinfo, cpu);
468 smp_rmb(); 480 if (!down_read_trylock(&pcpu->enable_sem))
469 481 continue;
470 if (!pcpu->governor_enabled) 482 if (!pcpu->governor_enabled) {
483 up_read(&pcpu->enable_sem);
471 continue; 484 continue;
485 }
472 486
473 for_each_cpu(j, pcpu->policy->cpus) { 487 for_each_cpu(j, pcpu->policy->cpus) {
474 struct cpufreq_interactive_cpuinfo *pjcpu = 488 struct cpufreq_interactive_cpuinfo *pjcpu =
@@ -485,6 +499,8 @@ static int cpufreq_interactive_speedchange_task(void *data)
485 trace_cpufreq_interactive_setspeed(cpu, 499 trace_cpufreq_interactive_setspeed(cpu,
486 pcpu->target_freq, 500 pcpu->target_freq,
487 pcpu->policy->cur); 501 pcpu->policy->cur);
502
503 up_read(&pcpu->enable_sem);
488 } 504 }
489 } 505 }
490 506
@@ -935,10 +951,11 @@ static int cpufreq_governor_interactive(struct cpufreq_policy *policy,
935 case CPUFREQ_GOV_STOP: 951 case CPUFREQ_GOV_STOP:
936 for_each_cpu(j, policy->cpus) { 952 for_each_cpu(j, policy->cpus) {
937 pcpu = &per_cpu(cpuinfo, j); 953 pcpu = &per_cpu(cpuinfo, j);
954 down_write(&pcpu->enable_sem);
938 pcpu->governor_enabled = 0; 955 pcpu->governor_enabled = 0;
939 smp_wmb();
940 del_timer_sync(&pcpu->cpu_timer); 956 del_timer_sync(&pcpu->cpu_timer);
941 del_timer_sync(&pcpu->cpu_slack_timer); 957 del_timer_sync(&pcpu->cpu_slack_timer);
958 up_write(&pcpu->enable_sem);
942 } 959 }
943 960
944 if (atomic_dec_return(&active_count) > 0) 961 if (atomic_dec_return(&active_count) > 0)
@@ -988,6 +1005,7 @@ static int __init cpufreq_interactive_init(void)
988 init_timer(&pcpu->cpu_slack_timer); 1005 init_timer(&pcpu->cpu_slack_timer);
989 pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer; 1006 pcpu->cpu_slack_timer.function = cpufreq_interactive_nop_timer;
990 spin_lock_init(&pcpu->load_lock); 1007 spin_lock_init(&pcpu->load_lock);
1008 init_rwsem(&pcpu->enable_sem);
991 } 1009 }
992 1010
993 spin_lock_init(&target_loads_lock); 1011 spin_lock_init(&target_loads_lock);