cpuidle: teo: Consider hits and misses metrics of disabled states
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>
Thu, 10 Oct 2019 21:36:15 +0000 (23:36 +0200)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Mon, 14 Oct 2019 08:40:33 +0000 (10:40 +0200)
The TEO governor uses idle duration "bins" defined in accordance with
the CPU idle states table provided by the driver, so that each "bin"
covers the idle duration range between the target residency of the
idle state corresponding to it and the target residency of the closest
deeper idle state.  The governor collects statistics for each bin
regardless of whether or not the idle state corresponding to it is
currently enabled.

In particular, the "hits" and "misses" metrics measure the likelihood
of a situation in which both the time till the next timer (sleep
length) and the idle duration measured after wakeup fall into the
given bin.  Namely, if the "hits" value is greater than the "misses"
one, that situation is more likely than the one in which the sleep
length falls into the given bin, but the idle duration measured after
wakeup falls into a bin corresponding to one of the shallower idle
states.

If the idle state corresponding to the given bin is disabled, it
cannot be selected and if it turns out to be the one that should be
selected, a shallower idle state needs to be used instead of it.
Nevertheless, the metrics collected for the bin corresponding to it
are still valid and need to be taken into account as though that
state had not been disabled.

For this reason, make teo_select() always use the "hits" and "misses"
values of the idle duration range that the sleep length falls into
even if the specific idle state corresponding to it is disabled and
if the "hits" values is greater than the "misses" one, select the
closest enabled shallower idle state in that case.

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

index de3139b17a504c4186eaf76989b33a5e6c4d0a86..5a0f60ea4ab9e04a961d60f9eece0d0de46410b2 100644 (file)
@@ -233,7 +233,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, early_hits;
+       unsigned int duration_us, hits, misses, early_hits;
        int max_early_idx, constraint_idx, idx, i;
        ktime_t delta_tick;
 
@@ -247,6 +247,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
        cpu_data->sleep_length_ns = tick_nohz_get_sleep_length(&delta_tick);
        duration_us = ktime_to_us(cpu_data->sleep_length_ns);
 
+       hits = 0;
+       misses = 0;
        early_hits = 0;
        max_early_idx = -1;
        constraint_idx = drv->state_count;
@@ -264,6 +266,17 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
                        if (s->target_residency > duration_us)
                                continue;
 
+                       /*
+                        * This state is disabled, so the range of idle duration
+                        * values corresponding to it is covered by the current
+                        * candidate state, but still the "hits" and "misses"
+                        * metrics of the disabled state need to be used to
+                        * decide whether or not the state covering the range in
+                        * question is good enough.
+                        */
+                       hits = cpu_data->states[i].hits;
+                       misses = cpu_data->states[i].misses;
+
                        /*
                         * If the "early hits" metric of a disabled state is
                         * greater than the current maximum, it should be taken
@@ -280,8 +293,11 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
                        continue;
                }
 
-               if (idx < 0)
+               if (idx < 0) {
                        idx = i; /* first enabled state */
+                       hits = cpu_data->states[i].hits;
+                       misses = cpu_data->states[i].misses;
+               }
 
                if (s->target_residency > duration_us)
                        break;
@@ -290,6 +306,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
                        constraint_idx = i;
 
                idx = i;
+               hits = cpu_data->states[i].hits;
+               misses = cpu_data->states[i].misses;
 
                if (early_hits < cpu_data->states[i].early_hits &&
                    !(tick_nohz_tick_stopped() &&
@@ -307,8 +325,7 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
         * "early hits" metric, but if that cannot be determined, just use the
         * state selected so far.
         */
-       if (cpu_data->states[idx].hits <= cpu_data->states[idx].misses &&
-           max_early_idx >= 0) {
+       if (hits <= misses && max_early_idx >= 0) {
                idx = max_early_idx;
                duration_us = drv->states[idx].target_residency;
        }