summaryrefslogtreecommitdiff
path: root/drivers/cpuidle/cpuidle.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/cpuidle/cpuidle.c')
-rw-r--r--drivers/cpuidle/cpuidle.c41
1 files changed, 37 insertions, 4 deletions
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 080bd2dbde4b..5e6c6bec97de 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -158,21 +158,54 @@ int cpuidle_enter_state(struct cpuidle_device *dev, struct cpuidle_driver *drv,
int entered_state;
struct cpuidle_state *target_state = &drv->states[index];
- ktime_t time_start, time_end;
s64 diff;
trace_cpu_idle_rcuidle(index, dev->cpu);
- time_start = ktime_get();
+ /*
+ * Store the idle start time for this cpu, this information
+ * will be used by cpuidle to measure how long the cpu has
+ * been idle and by the scheduler to prevent to wake it up too
+ * early
+ */
+ target_state->idle_stamp = ktime_to_us(ktime_get());
+
+ /*
+ * The enter to the low level idle routine. This call will block
+ * until an interrupt occurs meaning it is the end of the idle
+ * period
+ */
entered_state = target_state->enter(dev, drv, index);
- time_end = ktime_get();
+ /*
+ * Measure as soon as possible the duration of the idle
+ * period. It MUST be done before re-enabling the interrupt in
+ * order to prevent to add in the idle time measurement the
+ * interrupt handling duration
+ */
+ diff = ktime_to_us(ktime_sub_us(ktime_get(), target_state->idle_stamp));
+
+ /*
+ * Reset the idle time stamp as the scheduler may think the cpu is idle
+ * while it is in the process of waking up
+ */
+ target_state->idle_stamp = 0;
+
trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, dev->cpu);
+ /*
+ * The cpuidle_enter_coupled uses the cpuidle_enter function.
+ * Don't re-enable the interrupts and let the enter_coupled
+ * function to wait for all cpus to sync and to enable the
+ * interrupts again from there
+ */
if (!cpuidle_state_is_coupled(dev, drv, entered_state))
local_irq_enable();
- diff = ktime_to_us(ktime_sub(time_end, time_start));
+ /*
+ * The idle duration will be casted to an integer, prevent to
+ * overflow by setting a boundary to INT_MAX
+ */
if (diff > INT_MAX)
diff = INT_MAX;