| From e7387da52028b072489c45efeb7a916c0205ebd2 Mon Sep 17 00:00:00 2001 |
| From: Daniel Lezcano <daniel.lezcano@linaro.org> |
| Date: Tue, 17 May 2016 16:54:00 +0200 |
| Subject: cpuidle: Fix cpuidle_state_is_coupled() argument in cpuidle_enter() |
| |
| From: Daniel Lezcano <daniel.lezcano@linaro.org> |
| |
| commit e7387da52028b072489c45efeb7a916c0205ebd2 upstream. |
| |
| Commit 0b89e9aa2856 (cpuidle: delay enabling interrupts until all |
| coupled CPUs leave idle) rightfully fixed a regression by letting |
| the coupled idle state framework to handle local interrupt enabling |
| when the CPU is exiting an idle state. |
| |
| The current code checks if the idle state is coupled and, if so, it |
| will let the coupled code to enable interrupts. This way, it can |
| decrement the ready-count before handling the interrupt. This |
| mechanism prevents the other CPUs from waiting for a CPU which is |
| handling interrupts. |
| |
| But the check is done against the state index returned by the back |
| end driver's ->enter functions which could be different from the |
| initial index passed as parameter to the cpuidle_enter_state() |
| function. |
| |
| entered_state = target_state->enter(dev, drv, index); |
| |
| [ ... ] |
| |
| if (!cpuidle_state_is_coupled(drv, entered_state)) |
| local_irq_enable(); |
| |
| [ ... ] |
| |
| If the 'index' is referring to a coupled idle state but the |
| 'entered_state' is *not* coupled, then the interrupts are enabled |
| again. All CPUs blocked on the sync barrier may busy loop longer |
| if the CPU has interrupts to handle before decrementing the |
| ready-count. That's consuming more energy than saving. |
| |
| Fixes: 0b89e9aa2856 (cpuidle: delay enabling interrupts until all coupled CPUs leave idle) |
| Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> |
| [ rjw: Subject & changelog ] |
| Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/cpuidle/cpuidle.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/drivers/cpuidle/cpuidle.c |
| +++ b/drivers/cpuidle/cpuidle.c |
| @@ -214,7 +214,7 @@ int cpuidle_enter_state(struct cpuidle_d |
| tick_broadcast_exit(); |
| } |
| |
| - if (!cpuidle_state_is_coupled(drv, entered_state)) |
| + if (!cpuidle_state_is_coupled(drv, index)) |
| local_irq_enable(); |
| |
| diff = ktime_to_us(ktime_sub(time_end, time_start)); |