sched discussion
[PATCH] sched/core: Skip rq->avg_idle update without a valid idle_stamp
LLM 分析
sched/core:恢复 update_rq_avg_idle 对 idle_stamp 的有效性检查
系列概况
- 标题:[PATCH] sched/core: Skip rq->avg_idle update without a valid idle_stamp
- 作者:Shubhang Kaushik (Ampere) sh@gentwo.org
- 版本:v1(thread_key 中
master-v1-1表示首版) - 规模:单 patch,1 个文件,8 insertions(+) / 2 deletions(-)
- 修改文件:
kernel/sched/core.c - 代码统计:在
update_rq_avg_idle()内 cacherq->idle_stamp,加 validity 守卫 - Message-ID:
20260728-master-v1-1-f95d9b0147d2@gentwo.org - 完整性:包含 diff、
Fixes:tag、签名行、hackbench 备注、相关讨论说明与base-commit - 后续回复:
39ff2c76-d533-4b6c-9449-de19ed18e44a@amd.com(K Prateek Nayak, AMD),正文只回显前一封 commit message,没有独立评审意见
补丁目的
修复 commit 4b603f1551a73 把 rq->avg_idle 记账从 wakeup 路径搬到 put_prev_task_idle() 时丢失的 idle_stamp 合法性检查。原 wakeup 侧代码只在 rq->idle_stamp 非零时才更新 avg_idle;新 helper 失去该守卫后,当 idle_stamp == 0 时会无脑用 rq_clock(rq) 当作 delta,瞬时把 rq->avg_idle 推到 clamp 上限,污染 idle load balancing 的输入样本。
旧流程的问题
put_prev_task_idle()
|
v
update_rq_avg_idle(rq)
|
+-- delta = rq_clock(rq) - rq->idle_stamp
| (when idle_stamp == 0,
| delta == rq_clock(rq), a huge bogus value)
|
v
update_avg(&rq->avg_idle, delta) --> saturates to clamp on first call
旧 wakeup 路径只在 idle_stamp 非零时记账;搬到 put_prev_task_idle() 后这条合法性守卫被丢掉,记账系统被一次虚假"巨大"采样直接饱和。
新流程
put_prev_task_idle()
|
v
update_rq_avg_idle(rq)
|
+-- cache rq->idle_stamp into local var
+-- if (unlikely(!idle_stamp)) return;
|
v
delta = rq_clock(rq) - idle_stamp
update_avg(&rq->avg_idle, delta)
clamp to max
新流程在入口用 unlikely(!idle_stamp) return; 把没有真实 idle 区间的调用直接挡掉,其他行为保持不变。
Patch 概览
仅 v1,整条线索只包含一个 patch。核心改动集中在 update_rq_avg_idle():把 rq->idle_stamp 缓存到局部变量,并在计算 delta 前做 validity 检查。作者明确没有在 set_next_task_idle() 里额外打 idle stamp,刻意保留既有 newidle 记账模型以避开 forced/proxy idle accounting 争议。
关键实现
void update_rq_avg_idle(struct rq *rq)
{
- u64 delta = rq_clock(rq) - rq->idle_stamp;
- u64 max = 2*rq->max_idle_balance_cost;
+ u64 idle_stamp = rq->idle_stamp;
+ u64 delta, max;
+
+ if (unlikely(!idle_stamp))
+ return;
+
+ delta = rq_clock(rq) - idle_stamp;
+ max = 2 * rq->max_idle_balance_cost;
要点拆解:
- 把
rq->idle_stamp缓存到本地局部变量,避免重复读 rq 字段。 - 用
unlikely(!idle_stamp)把"非法采样"挡在记账逻辑之前。 - delta/max 计算公式与之前一致,正常路径行为不变。
- 函数结尾
rq->idle_stamp = 0的清零由调用方/别处处理,未在本补丁改动。
类比
把 rq->avg_idle 想象成"咖啡店收银台过去几次顾客排队的平均等待时长"。
- 上一个改动相当于把
start_time默认设成了"店面开业时间 0",即使收银员当天没开张,关账时也会算出 elapsed = now - 0,把"平均等待时长"瞬间变成天文数字,并被一键钳到 max。 - 本补丁相当于:关账前先确认"我今天上过班了 (idle_stamp != 0)",没上就直接关账、跳过统计,账本不会被污染。
另一层类比:idle_stamp == 0 类似"温度计还没被激活",直接当作"现在的温度"读数一记下来,会污染趋势曲线;补丁相当于"温度计未就绪就跳过本次采样"。
Highlight:风险与注意点
- 回归面:
4b603f1551a73的初衷是把记账放在 idle task 切出处;本补丁只堵住idle_stamp==0这一个漏点,未修复相邻的"被 fake idle 切出"等场景,理论上仍可能喂入不真实样本,需要后续 trace 验证。 - idle_stamp 状态机:补丁只 return、不清 stamp,留下的 0 状态对下游路径是否还有副作用需要确认,例如
rq->no_balance或 idle 重新进入路径是否依赖idle_stamp已被清零。 - bench 覆盖度:备注只在 hackbench 上没观察到回归;hackbench 对 CPU idle 区间敏感度有限,SPECjvm、latency-critical workload 或 idle-heavy 服务器负载建议补充验证。
- 与原方案分歧:作者明确写这是"earlier proposal 的更窄变体",刻意没有在
set_next_task_idle()中打 idle stamp;该折中能否被 reviewer 接受是上游合并的关键点。 - 回复信息缺失:第二封邮件正文只回显前一封 commit message,没有看到 K Prateek Nayak 的具体评审意见,建议后续轮次补充确认。
版本变化
仅 v1,没有可对比的 vN -> vN+1 迭代。
一句话总结
本补丁在 update_rq_avg_idle() 入口加 idle_stamp 有效性守卫,避免 idle_stamp == 0 时把 rq_clock(rq) 当成 idle duration,把 rq->avg_idle 瞬时冲到 clamp,是 commit 4b603f1551a73 迁移 avg_idle 记账后的一次最小修复。
wakeup task on idle CPU
|
v
put_prev_task_idle()
|
v
+----------------------------------------+
| update_rq_avg_idle(rq) |
+----------------------------------------+
|
read idle_stamp
|
+-------+--------+
| |
idle_stamp == 0 idle_stamp != 0
| |
return delta = rq_clock(rq) - idle_stamp
(skip update) |
v
update_avg(&rq->avg_idle, delta)
|
v
clamp to max