aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/cpuidle
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2013-08-23 15:45:12 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-08-29 16:15:34 -0400
commit9e19b73c30a5fa42a53583a1f7817dd857126156 (patch)
tree74447e7a435320f8f87837821ac50863dffcc30c /drivers/cpuidle
parentf983827bcb9d2c34c4d8935861a1e9128aec2baf (diff)
cpuidle: coupled: fix race condition between pokes and safe state
The coupled cpuidle waiting loop clears pending pokes before entering the safe state. If a poke arrives just before the pokes are cleared, but after the while loop condition checks, the poke will be lost and the cpu will stay in the safe state until another interrupt arrives. This may cause the cpu that sent the poke to spin in the ready loop with interrupts off until another cpu receives an interrupt, and if no other cpus have interrupts routed to them it can spin forever. Change the return value of cpuidle_coupled_clear_pokes to return if a poke was cleared, and move the need_resched() checks into the callers. In the waiting loop, if a poke was cleared restart the loop to repeat the while condition checks. Reported-by: Neil Zhang <zhangwm@marvell.com> Signed-off-by: Colin Cross <ccross@android.com> Cc: 3.6+ <stable@vger.kernel.org> # 3.6+ Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/cpuidle')
-rw-r--r--drivers/cpuidle/coupled.c20
1 files changed, 14 insertions, 6 deletions
diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c
index 5d3962730063..f8a86364c6b6 100644
--- a/drivers/cpuidle/coupled.c
+++ b/drivers/cpuidle/coupled.c
@@ -408,19 +408,22 @@ static void cpuidle_coupled_set_done(int cpu, struct cpuidle_coupled *coupled)
408 * been processed and the poke bit has been cleared. 408 * been processed and the poke bit has been cleared.
409 * 409 *
410 * Other interrupts may also be processed while interrupts are enabled, so 410 * Other interrupts may also be processed while interrupts are enabled, so
411 * need_resched() must be tested after turning interrupts off again to make sure 411 * need_resched() must be tested after this function returns to make sure
412 * the interrupt didn't schedule work that should take the cpu out of idle. 412 * the interrupt didn't schedule work that should take the cpu out of idle.
413 * 413 *
414 * Returns 0 if need_resched was false, -EINTR if need_resched was true. 414 * Returns 0 if no poke was pending, 1 if a poke was cleared.
415 */ 415 */
416static int cpuidle_coupled_clear_pokes(int cpu) 416static int cpuidle_coupled_clear_pokes(int cpu)
417{ 417{
418 if (!cpumask_test_cpu(cpu, &cpuidle_coupled_poke_pending))
419 return 0;
420
418 local_irq_enable(); 421 local_irq_enable();
419 while (cpumask_test_cpu(cpu, &cpuidle_coupled_poke_pending)) 422 while (cpumask_test_cpu(cpu, &cpuidle_coupled_poke_pending))
420 cpu_relax(); 423 cpu_relax();
421 local_irq_disable(); 424 local_irq_disable();
422 425
423 return need_resched() ? -EINTR : 0; 426 return 1;
424} 427}
425 428
426static bool cpuidle_coupled_any_pokes_pending(struct cpuidle_coupled *coupled) 429static bool cpuidle_coupled_any_pokes_pending(struct cpuidle_coupled *coupled)
@@ -464,7 +467,8 @@ int cpuidle_enter_state_coupled(struct cpuidle_device *dev,
464 return -EINVAL; 467 return -EINVAL;
465 468
466 while (coupled->prevent) { 469 while (coupled->prevent) {
467 if (cpuidle_coupled_clear_pokes(dev->cpu)) { 470 cpuidle_coupled_clear_pokes(dev->cpu);
471 if (need_resched()) {
468 local_irq_enable(); 472 local_irq_enable();
469 return entered_state; 473 return entered_state;
470 } 474 }
@@ -503,7 +507,10 @@ retry:
503 */ 507 */
504 while (!cpuidle_coupled_cpus_waiting(coupled) || 508 while (!cpuidle_coupled_cpus_waiting(coupled) ||
505 !cpumask_test_cpu(dev->cpu, &cpuidle_coupled_poked)) { 509 !cpumask_test_cpu(dev->cpu, &cpuidle_coupled_poked)) {
506 if (cpuidle_coupled_clear_pokes(dev->cpu)) { 510 if (cpuidle_coupled_clear_pokes(dev->cpu))
511 continue;
512
513 if (need_resched()) {
507 cpuidle_coupled_set_not_waiting(dev->cpu, coupled); 514 cpuidle_coupled_set_not_waiting(dev->cpu, coupled);
508 goto out; 515 goto out;
509 } 516 }
@@ -518,7 +525,8 @@ retry:
518 local_irq_disable(); 525 local_irq_disable();
519 } 526 }
520 527
521 if (cpuidle_coupled_clear_pokes(dev->cpu)) { 528 cpuidle_coupled_clear_pokes(dev->cpu);
529 if (need_resched()) {
522 cpuidle_coupled_set_not_waiting(dev->cpu, coupled); 530 cpuidle_coupled_set_not_waiting(dev->cpu, coupled);
523 goto out; 531 goto out;
524 } 532 }