From: "Rafael J. Wysocki" rafael.j.wysocki@intel.com
mainline inclusion from mainline-5.14-rc7 commit f1c8e410cdac5df42a7806e49efde2859a10fecd category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I48J5M CVE: NA
-------------------------------------------------
bug: 73520
If the minimum interval taken into account in the average computation loop in get_typical_interval() is less than the expected idle duration determined so far, the resultant average cannot be greater than that value as well and the entire return result of the function is going to be discarded anyway going forward.
In that case, it is a waste of time to carry out the remaining computations in get_typical_interval(), so avoid that by returning early if the minimum interval is not below the expected idle duration.
[liuyun01@kylinos.cn: in theory, this patch did not do much work, but due to the addition of the previous patch eee4b6538e3e ("cpuidle: menu: Avoid overflows when computing variance"), the value of thresh was adjusted from UINT_MAX to INT_MAX, which led to the possibility of dividing by 0, which eventually led to panic.]
Fixes: eee4b6538e3e ("cpuidle: menu: Avoid overflows when computing variance") (cherry-picked from f1c8e410cdac5df42a7806e49efde2859a10fecd) Cc: stable Signed-off-by: Rafael J. Wysocki rafael.j.wysocki@intel.com Signed-off-by: Jackie Liu liuyun01@kylinos.cn # openEuler_contributor Change-Id: I1109dc2bf03bf5a4fde7185dcd765805eef0a928 Reviewed-on: http://gerrit.kylin.com/c/klinux-4.19/+/13212 Tested-by: kernelbot kernel-bot@kylinos.cn Signed-off-by: Cheng Jian cj.chengjian@huawei.com Reviewed-by: Xie XiuQi xiexiuqi@huawei.com --- drivers/cpuidle/governors/menu.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c index c447014d1d72..805435975a5b 100644 --- a/drivers/cpuidle/governors/menu.c +++ b/drivers/cpuidle/governors/menu.c @@ -196,10 +196,11 @@ static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev); * of points is below a threshold. If it is... then use the * average of these 8 points as the estimated value. */ -static unsigned int get_typical_interval(struct menu_device *data) +static unsigned int get_typical_interval(struct menu_device *data, + unsigned int predicted_us) { int i, divisor; - unsigned int max, thresh, avg; + unsigned int max, min, thresh, avg; uint64_t sum, variance;
thresh = INT_MAX; /* Discard outliers above this value */ @@ -207,6 +208,7 @@ static unsigned int get_typical_interval(struct menu_device *data) again:
/* First calculate the average of past intervals */ + min = UINT_MAX; max = 0; sum = 0; divisor = 0; @@ -217,8 +219,18 @@ static unsigned int get_typical_interval(struct menu_device *data) divisor++; if (value > max) max = value; + if (value < min) + min = value; } } + + /* + * If the result of the computation is going to be discarded anyway, + * avoid the computation altogether. + */ + if (min >= predicted_us) + return UINT_MAX; + if (divisor == INTERVALS) avg = sum >> INTERVAL_SHIFT; else @@ -318,7 +330,7 @@ static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev, data->correction_factor[data->bucket], RESOLUTION * DECAY);
- expected_interval = get_typical_interval(data); + expected_interval = get_typical_interval(data, data->predicted_us); expected_interval = min(expected_interval, data->next_timer_us);
first_idx = 0;