cpuidle: teo: Consider hits and misses metrics of disabled states
[linux-block.git] / drivers / cpuidle / governors / teo.c
index b5a0e498f7989791192f79e72e166eb5ed862a85..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, count;
+       unsigned int duration_us, hits, misses, early_hits;
        int max_early_idx, constraint_idx, idx, i;
        ktime_t delta_tick;
 
@@ -247,7 +247,9 @@ 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);
 
-       count = 0;
+       hits = 0;
+       misses = 0;
+       early_hits = 0;
        max_early_idx = -1;
        constraint_idx = drv->state_count;
        idx = -1;
@@ -257,24 +259,45 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
                struct cpuidle_state_usage *su = &dev->states_usage[i];
 
                if (s->disabled || su->disable) {
+                       /*
+                        * Ignore disabled states with target residencies beyond
+                        * the anticipated idle duration.
+                        */
+                       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
                         * into account, because it would be a mistake to select
                         * a deeper state with lower "early hits" metric.  The
                         * index cannot be changed to point to it, however, so
-                        * just increase the max count alone and let the index
-                        * still point to a shallower idle state.
+                        * just increase the "early hits" count alone and let
+                        * the index still point to a shallower idle state.
                         */
                        if (max_early_idx >= 0 &&
-                           count < cpu_data->states[i].early_hits)
-                               count = cpu_data->states[i].early_hits;
+                           early_hits < cpu_data->states[i].early_hits)
+                               early_hits = cpu_data->states[i].early_hits;
 
                        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;
@@ -283,11 +306,13 @@ 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 (count < cpu_data->states[i].early_hits &&
+               if (early_hits < cpu_data->states[i].early_hits &&
                    !(tick_nohz_tick_stopped() &&
                      drv->states[i].target_residency < TICK_USEC)) {
-                       count = cpu_data->states[i].early_hits;
+                       early_hits = cpu_data->states[i].early_hits;
                        max_early_idx = i;
                }
        }
@@ -300,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;
        }
@@ -316,10 +340,9 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
        if (idx < 0) {
                idx = 0; /* No states enabled. Must use 0. */
        } else if (idx > 0) {
+               unsigned int count = 0;
                u64 sum = 0;
 
-               count = 0;
-
                /*
                 * Count and sum the most recent idle duration values less than
                 * the current expected idle duration value.