sched discussion
[PATCH v3] cpufreq: schedutil: Fix rate limit overflow
LLM 分析
cpufreq schedutil:修复 rate_limit_us 32 位溢出
系列概况
- 标题:[PATCH v3] cpufreq: schedutil: Fix rate limit overflow
- 作者:Hui Su sh_def@163.com
- 版本:v3(演进自 v1/v2)
- 规模:1 个文件,新增 13 行,删除 2 行
- 修改文件:kernel/sched/cpufreq_schedutil.c
- 代码统计:1 file changed, 13 insertions(+), 2 deletions(-)
- Message-ID:20260806142304.1761454-1-sh_def@163.com
- 完整性:含 commit message、Fixes/Reviewed-by/Signed-off-by、v3 changelog 与前置 v1/v2 链接
- Reviewer:Zhongqiu Han(已 Reviewed-by)
- Cc:stable@vger.kernel.org,需要回溯到 stable 分支
- 跟进邮件:Rafael J. Wysocki(cpufreq maintainer)的回复(正文基本只是引用 v3 patch)
补丁目的
schedutil governor 用 rate_limit_us * NSEC_PER_USEC 计算 freq_update_delay_ns。
rate_limit_us 是 unsigned int,NSEC_PER_USEC 是 1000L。
在 32 位平台上两操作数都按 32 位 unsigned long 做乘法,再把结果赋给 s64 freq_update_delay_ns。
写入 rate_limit_us = 4294968 时,期望 4294968000 ns,但 32 位乘法溢出为 704 ns,
schedutil 频率更新频率远高于配置。补丁抽出 helper,在乘法前显式把 rate_limit_us 扩到 s64,消除溢出。
旧流程的问题
sg_policy->freq_update_delay_ns = rate_limit_us * NSEC_PER_USEC;
sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
rate_limit_us (u32) NSEC_PER_USEC (long)
| |
+------- 32-bit mul -------+
|
v
freq_update_delay_ns (s64) <-- low 32 bits only
|
v
4294968000 ns --> 704 ns
schedutil freq updates run wild
新流程
新增 helper,并在 sysfs 写入与 governor 启动两条路径上都改用它:
rate_limit_us (u32) --(s64)--> s64 value
|
v
s64 * NSEC_PER_USEC (64-bit mul)
|
v
freq_update_delay_ns (s64)
|
v
real delay = 4294968000 ns (no wrap)
Patch 概览
- 新增静态函数
sugov_update_rate_limit_us(),含说明性注释 rate_limit_us_store()(sysfs 写入路径)改用 helpersugov_start()(governor 启动路径)改用 helperFixes指向引入该问题的 commit9bdcb44e391d- Cc
stable@vger.kernel.org
关键实现
static void sugov_update_rate_limit_us(struct sugov_policy *sg_policy)
{
/*
* Cast rate_limit_us before multiplication to force 64-bit arithmetic.
* Otherwise, on 32-bit platforms, both operands are converted to
* 32-bit unsigned long and the multiplication may overflow.
*/
sg_policy->freq_update_delay_ns =
(s64)sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
}
/* rate_limit_us_store() */
- sg_policy->freq_update_delay_ns = rate_limit_us * NSEC_PER_USEC;
+ sugov_update_rate_limit_us(sg_policy);
/* sugov_start() */
- sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC;
+ sugov_update_rate_limit_us(sg_policy);
类比
把 rate_limit_us 想象成一个最多装 4 L 的小水桶,NSEC_PER_USEC 是一杯 1000 mL 的水。
本想倒 4.29 L 进大盆,水桶(32 位)只能装 4 L,多出来的水被截断,
大盆里只剩 704 mL,倒水动作(频率更新)就变得又急又频繁。
补丁相当于先把水倒进能装 4.29 L 的大桶(s64),桶能装下再做乘法,水就不会再溢出。
Highlight:风险与注意点
- 触发面:32 位平台 +
rate_limit_us > ~4.29s(毫秒级大延迟配置)。64 位平台不受影响,
但补丁统一了 sysfs 与启动两条路径,风格更安全。 - 类型基础:
freq_update_delay_ns是s64,但tunables->rate_limit_us是unsigned int,
类型不匹配本身就是隐患;建议在 tunable 结构里同步检查类型。 - 验证点:在 32 位 ARM/i386 构建上,用 sysfs 写
rate_limit_us=4294968后应读到接近
4294968000 ns,而不是704 ns。 - stable 回溯:
Fixes指向原始 commit,Cc stable表示要 cherry-pick 到现有 stable 分支。 - 跟进缺口:Rafael 回复正文几乎是清一色引用块,没看到他实际给的 Acked-by/Reviewed-by
或合并决定,需关注后续正式回信。
版本变化
- v1 -> v2:澄清 32 位无符号算术的原因;将
rate_limit_us转为s64以匹配
freq_update_delay_ns;增加 Zhongqiu 的 Reviewed-by。 - v2 -> v3:仅为
sugov_update_rate_limit_us()添加说明性注释,解释为什么乘法前必须
做强制类型转换,没有逻辑变更。
一句话总结
schedutil 在 32 位平台上把 rate_limit_us * 1000 按 32 位算术算出后再赋给 s64,
导致大延迟溢出成几百纳秒;补丁抽出 sugov_update_rate_limit_us() 强制扩到 s64,
在 sysfs 与启动两条路径上一并修复,并抄送 stable。