Merge tag 'modules-for-v5.4' of git://git.kernel.org/pub/scm/linux/kernel/git/jeyu...
[linux-block.git] / drivers / cpuidle / governors / teo.c
index 7d05efdbd3c665871d23579d96d35a4d7a2fdd37..b5a0e498f7989791192f79e72e166eb5ed862a85 100644 (file)
@@ -96,7 +96,6 @@ struct teo_idle_state {
  * @time_span_ns: Time between idle state selection and post-wakeup update.
  * @sleep_length_ns: Time till the closest timer event (at the selection time).
  * @states: Idle states data corresponding to this CPU.
- * @last_state: Idle state entered by the CPU last time.
  * @interval_idx: Index of the most recent saved idle interval.
  * @intervals: Saved idle duration values.
  */
@@ -104,7 +103,6 @@ struct teo_cpu {
        u64 time_span_ns;
        u64 sleep_length_ns;
        struct teo_idle_state states[CPUIDLE_STATE_MAX];
-       int last_state;
        int interval_idx;
        unsigned int intervals[INTERVALS];
 };
@@ -125,12 +123,15 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
 
        if (cpu_data->time_span_ns >= cpu_data->sleep_length_ns) {
                /*
-                * One of the safety nets has triggered or this was a timer
-                * wakeup (or equivalent).
+                * One of the safety nets has triggered or the wakeup was close
+                * enough to the closest timer event expected at the idle state
+                * selection time to be discarded.
                 */
-               measured_us = sleep_length_us;
+               measured_us = UINT_MAX;
        } else {
-               unsigned int lat = drv->states[cpu_data->last_state].exit_latency;
+               unsigned int lat;
+
+               lat = drv->states[dev->last_state_idx].exit_latency;
 
                measured_us = ktime_to_us(cpu_data->time_span_ns);
                /*
@@ -188,15 +189,6 @@ static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
                cpu_data->states[idx_timer].hits = hits;
        }
 
-       /*
-        * If the total time span between idle state selection and the "reflect"
-        * callback is greater than or equal to the sleep length determined at
-        * the idle state selection time, the wakeup is likely to be due to a
-        * timer event.
-        */
-       if (cpu_data->time_span_ns >= cpu_data->sleep_length_ns)
-               measured_us = UINT_MAX;
-
        /*
         * Save idle duration values corresponding to non-timer wakeups for
         * pattern detection.
@@ -242,12 +234,12 @@ 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 (cpu_data->last_state >= 0) {
+       if (dev->last_state_idx >= 0) {
                teo_update(drv, dev);
-               cpu_data->last_state = -1;
+               dev->last_state_idx = -1;
        }
 
        cpu_data->time_span_ns = local_clock();
@@ -257,6 +249,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 +279,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 +306,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 +322,12 @@ refine:
 
                /*
                 * 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 +346,10 @@ refine:
                         * 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);
                        }
                }
        }
@@ -394,7 +386,7 @@ static void teo_reflect(struct cpuidle_device *dev, int state)
 {
        struct teo_cpu *cpu_data = per_cpu_ptr(&teo_cpus, dev->cpu);
 
-       cpu_data->last_state = state;
+       dev->last_state_idx = state;
        /*
         * If the wakeup was not "natural", but triggered by one of the safety
         * nets, assume that the CPU might have been idle for the entire sleep