]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
cpuidle: teo: Allow tick to be stopped if PM QoS is used
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Fri, 19 Jul 2019 10:12:42 +0000 (12:12 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 5 Aug 2019 09:02:44 +0000 (11:02 +0200)
The TEO goveror prevents the scheduler tick from being stopped (unless
stopped already) if there is a PM QoS latency constraint for the given
CPU and the target residency of the deepest idle state matching that
constraint is below the tick boundary.

However, that is problematic if CPUs with PM QoS latency constraints
are idle for long times, because it effectively causes the tick to
run on them all the time which is wasteful.  [It is also confusing
and questionable if they are full dynticks CPUs.]

To address that issue, modify the TEO governor to carry out the
entire search for the most suitable idle state (from the target
residency perspective) even if a latency constraint is present,
to allow it to determine the expected idle duration in all cases.

Also, when using the last several measured idle duration values
to refine the idle state selection, make it compare those values
with the current expected idle duration value (instead of
comparing them with the target residency of the idle state
selected so far) which should prevent the tick from being
retained when it makes sense to stop it sometimes (especially
in the presence of PM QoS latency constraints).

Fixes: b26bf6ab716f ("cpuidle: New timer events oriented governor for tickless systems")
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/cpuidle/governors/teo.c

index a2fd81067a13b4510bcca1cbbec703e5df7e2a1f..4d7a60c5b24af25f176598d5f329bc79e065412e 100644 (file)
@@ -242,7 +242,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
        struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
        int latency_req = cpuidle_governor_latency_req(dev->cpu);
        unsigned int duration_us, count;
-       int max_early_idx, idx, i;
+       int max_early_idx, constraint_idx, idx, i;
        ktime_t delta_tick;
 
        if (dev->last_state_idx >= 0) {
@@ -257,6 +257,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
 
        count = 0;
        max_early_idx = -1;
+       constraint_idx = drv->state_count;
        idx = -1;
 
        for (i = 0; i < drv->state_count; i++) {
@@ -286,16 +287,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
                if (s->target_residency > duration_us)
                        break;
 
-               if (s->exit_latency > latency_req) {
-                       /*
-                        * If we break out of the loop for latency reasons, use
-                        * the target residency of the selected state as the
-                        * expected idle duration to avoid stopping the tick
-                        * as long as that target residency is low enough.
-                        */
-                       duration_us = drv->states[idx].target_residency;
-                       goto refine;
-               }
+               if (s->exit_latency > latency_req && constraint_idx > i)
+                       constraint_idx = i;
 
                idx = i;
 
@@ -321,7 +314,13 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
                duration_us = drv->states[idx].target_residency;
        }
 
-refine:
+       /*
+        * If there is a latency constraint, it may be necessary to use a
+        * shallower idle state than the one selected so far.
+        */
+       if (constraint_idx < idx)
+               idx = constraint_idx;
+
        if (idx < 0) {
                idx = 0; /* No states enabled. Must use 0. */
        } else if (idx > 0) {
@@ -331,13 +330,12 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
 
                /*
                 * Count and sum the most recent idle duration values less than
-                * the target residency of the state selected so far, find the
-                * max.
+                * the current expected idle duration value.
                 */
                for (i = 0; i < INTERVALS; i++) {
                        unsigned int val = cpu_data->intervals[i];
 
-                       if (val >= drv->states[idx].target_residency)
+                       if (val >= duration_us)
                                continue;
 
                        count++;
@@ -356,8 +354,10 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
                         * would be too shallow.
                         */
                        if (!(tick_nohz_tick_stopped() && avg_us < TICK_USEC)) {
-                               idx = teo_find_shallower_state(drv, dev, idx, avg_us);
                                duration_us = avg_us;
+                               if (drv->states[idx].target_residency > avg_us)
+                                       idx = teo_find_shallower_state(drv, dev,
+                                                                      idx, avg_us);
                        }
                }
        }