Skip to content

Commit

Permalink
cpuidle: teo: Fix alternative idle state lookup
Browse files Browse the repository at this point in the history
There are three mistakes in the loop in teo_select() that is looking
for an alternative candidate idle state.  First, it should walk all
of the idle states shallower than the current candidate one,
including all of the disabled ones, but it terminates after the first
enabled idle state.  Second, it should not terminate its last step
if idle state 0 is disabled (which is related to the first issue).
Finally, it may return the current alternative candidate idle state
prematurely if the time span criterion is not met by the idle state
under consideration at the moment.

To address the issues mentioned above, make the loop in question walk
all of the idle states shallower than the current candidate idle state
all the way down to idle state 0 and rearrange the checks in it.

Fixes: 7757755 ("cpuidle: teo: Rework most recent idle duration values treatment")
Reported-by: Doug Smythies <dsmythies@telus.net>
Tested-by: Doug Smythies <dsmythies@telus.net>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
  • Loading branch information
Rafael J. Wysocki committed Aug 3, 2021
1 parent c500bee commit c2ec772
Showing 1 changed file with 27 additions and 13 deletions.
40 changes: 27 additions & 13 deletions drivers/cpuidle/governors/teo.c
Original file line number Diff line number Diff line change
Expand Up @@ -397,32 +397,46 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev,
intercept_sum = 0;
recent_sum = 0;

for (i = idx - 1; i >= idx0; i--) {
for (i = idx - 1; i >= 0; i--) {
struct teo_bin *bin = &cpu_data->state_bins[i];
s64 span_ns;

intercept_sum += bin->intercepts;
recent_sum += bin->recent;

span_ns = teo_middle_of_bin(i, drv);

if ((!alt_recent || 2 * recent_sum > idx_recent_sum) &&
(!alt_intercepts ||
2 * intercept_sum > idx_intercept_sum)) {
if (teo_time_ok(span_ns) &&
!dev->states_usage[i].disable) {
idx = i;
duration_ns = span_ns;
} else {
/*
* The current state is too shallow or
* disabled, so take the first enabled
* deeper state with suitable time span.
*/
idx = last_enabled_idx;
duration_ns = last_enabled_span_ns;
}
break;
}

if (dev->states_usage[i].disable)
continue;

span_ns = teo_middle_of_bin(i, drv);
if (!teo_time_ok(span_ns)) {
/*
* The current state is too shallow, so select
* the first enabled deeper state.
* The current state is too shallow, but if an
* alternative candidate state has been found,
* it may still turn out to be a better choice.
*/
duration_ns = last_enabled_span_ns;
idx = last_enabled_idx;
break;
}
if (last_enabled_idx != idx)
continue;

if ((!alt_recent || 2 * recent_sum > idx_recent_sum) &&
(!alt_intercepts ||
2 * intercept_sum > idx_intercept_sum)) {
idx = i;
duration_ns = span_ns;
break;
}

Expand Down

0 comments on commit c2ec772

Please sign in to comment.