Commit 9e19b73c30a5fa42a53583a1f7817dd857126156

Authored by Colin Cross
Committed by Rafael J. Wysocki
1 parent f983827bcb

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>

Showing 1 changed file with 14 additions and 6 deletions Side-by-side Diff

drivers/cpuidle/coupled.c
... ... @@ -408,19 +408,22 @@
408 408 * been processed and the poke bit has been cleared.
409 409 *
410 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 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 */
416 416 static int cpuidle_coupled_clear_pokes(int cpu)
417 417 {
  418 + if (!cpumask_test_cpu(cpu, &cpuidle_coupled_poke_pending))
  419 + return 0;
  420 +
418 421 local_irq_enable();
419 422 while (cpumask_test_cpu(cpu, &cpuidle_coupled_poke_pending))
420 423 cpu_relax();
421 424 local_irq_disable();
422 425  
423   - return need_resched() ? -EINTR : 0;
  426 + return 1;
424 427 }
425 428  
426 429 static bool cpuidle_coupled_any_pokes_pending(struct cpuidle_coupled *coupled)
... ... @@ -464,7 +467,8 @@
464 467 return -EINVAL;
465 468  
466 469 while (coupled->prevent) {
467   - if (cpuidle_coupled_clear_pokes(dev->cpu)) {
  470 + cpuidle_coupled_clear_pokes(dev->cpu);
  471 + if (need_resched()) {
468 472 local_irq_enable();
469 473 return entered_state;
470 474 }
... ... @@ -503,7 +507,10 @@
503 507 */
504 508 while (!cpuidle_coupled_cpus_waiting(coupled) ||
505 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 514 cpuidle_coupled_set_not_waiting(dev->cpu, coupled);
508 515 goto out;
509 516 }
... ... @@ -518,7 +525,8 @@
518 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 530 cpuidle_coupled_set_not_waiting(dev->cpu, coupled);
523 531 goto out;
524 532 }