From c2ec772b87408259cb01209a22fb4e1ae7d346de Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 30 Jul 2021 16:38:07 +0200 Subject: [PATCH 1/2] cpuidle: teo: Fix alternative idle state lookup 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: 77577558f25d ("cpuidle: teo: Rework most recent idle duration values treatment") Reported-by: Doug Smythies Tested-by: Doug Smythies Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/governors/teo.c | 44 ++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c index 7b91060e82f6..8c59050ba419 100644 --- a/drivers/cpuidle/governors/teo.c +++ b/drivers/cpuidle/governors/teo.c @@ -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; - 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. - */ - duration_ns = last_enabled_span_ns; - idx = last_enabled_idx; - break; - } if ((!alt_recent || 2 * recent_sum > idx_recent_sum) && (!alt_intercepts || 2 * intercept_sum > idx_intercept_sum)) { - idx = i; - duration_ns = span_ns; + 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; + + if (!teo_time_ok(span_ns)) { + /* + * 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. + */ + if (last_enabled_idx != idx) + continue; + break; } From 4adae7dd10db10f20f51833dc11b3cf7a342ad38 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 30 Jul 2021 16:38:52 +0200 Subject: [PATCH 2/2] cpuidle: teo: Rename two local variables in teo_select() Rename two local variables in teo_select() so that their names better reflect their purpose. No functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/cpuidle/governors/teo.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/cpuidle/governors/teo.c b/drivers/cpuidle/governors/teo.c index 8c59050ba419..d9262db79cae 100644 --- a/drivers/cpuidle/governors/teo.c +++ b/drivers/cpuidle/governors/teo.c @@ -382,8 +382,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, alt_intercepts = 2 * idx_intercept_sum > cpu_data->total - idx_hit_sum; alt_recent = idx_recent_sum > NR_RECENT / 2; if (alt_recent || alt_intercepts) { - s64 last_enabled_span_ns = duration_ns; - int last_enabled_idx = idx; + s64 first_suitable_span_ns = duration_ns; + int first_suitable_idx = idx; /* * Look for the deepest idle state whose target residency had @@ -419,8 +419,8 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, * disabled, so take the first enabled * deeper state with suitable time span. */ - idx = last_enabled_idx; - duration_ns = last_enabled_span_ns; + idx = first_suitable_idx; + duration_ns = first_suitable_span_ns; } break; } @@ -434,14 +434,14 @@ static int teo_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, * alternative candidate state has been found, * it may still turn out to be a better choice. */ - if (last_enabled_idx != idx) + if (first_suitable_idx != idx) continue; break; } - last_enabled_span_ns = span_ns; - last_enabled_idx = i; + first_suitable_span_ns = span_ns; + first_suitable_idx = i; } }