[PATCH v1] cpuidle: governors: menu: Predict longer idle time when in doubt

Rafael J. Wysocki
2025-10-16T16:25:39Z

From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>

It is reported that commit 85975daeaa4d ("cpuidle: menu: Avoid discarding
useful information") led to a performance regression on Intel Jasper Lake
systems because it reduced the time spent by CPUs in idle state C7 which
is correlated to the maximum frequency the CPUs can get to because of an
average running power limit [1].

Before that commit, get_typical_interval() would have returned UINT_MAX
whenever it had been unable to make a high-confidence prediction which
had led to selecting the deepest available idle state too often and
both power and performance had been inadequate as a result of that in
some cases. This was not a problem on systems with relatively
aggressive average running power limits, like the Jasper Lake systems
in question, because on those systems it was compensated by the ability
to run CPUs at relatively higher frequencies.

Commit 85975daeaa4d addressed that by causing get_typical_interval() to
return a number based on the recent idle duration information available
to it in those cases, but that number is usually smaller than the
maximum idle duration observed recently which may be regarded as an
overly optimistic choice.

Namely, it may be argued that when the samples considered by
get_typical_interval() are spread too much for a high-confidence
prediction to be made, the function should fall back to returning a
number that is likely to be an upper bound for the duration of the
upcoming idle interval and that number needs to be at least equal to
the maximum recently observed idle time. Otherwise, the governor may
miss an oportunity to reduce power without hurting performance in a
noticeable way. Of course, it may also be argued the other way around,
but the available data indicate that get_typical_interval() should
rather tend to return larger numbers as that causes the governor to
behave more closely to its past behavior from before the problematic
commit.

Accordingly, modify get_typical_interval() to return the maximum
recently observed idle time when it is unable to make a high-
confidence prediction.

Fixes: 85975daeaa4d ("cpuidle: menu: Avoid discarding useful information")
Closes: https://lore.kernel.org/linux-pm/36iykr223vmcfsoysexug6s274nq2oimcu55ybn6ww4il3g3cv@cohflgdbpnq7/ [1]
Reported-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Tested-by: Sergey Senozhatsky <senozhatsky@chromium.org>
Cc: All applicable <stable@vger.kernel.org>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
---
drivers/cpuidle/governors/menu.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)

--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -116,6 +116,7 @@ static void menu_update(struct cpuidle_d
static unsigned int get_typical_interval(struct menu_device *data)
{
s64 value, min_thresh = -1, max_thresh = UINT_MAX;
+ unsigned int max_overall = 0;
unsigned int max, min, divisor;
u64 avg, variance, avg_sq;
int i;
@@ -151,6 +152,9 @@ again:
if (!max)
return UINT_MAX;

+ if (max_overall < max)
+ max_overall = max;
+
if (divisor == INTERVALS) {
avg >>= INTERVAL_SHIFT;
variance >>= INTERVAL_SHIFT;
@@ -198,7 +202,7 @@ again:
* maximum, so return the latter in that case.
*/
if (divisor >= INTERVALS / 2)
- return max;
+ return max_overall;

return UINT_MAX;
}