sched discussion
[PATCH] sched/fair: Use update_curr_eevdf() for the remaining root cfs_rq callers
LLM 分析
sched/fair:让 root cfs_rq 的剩余调用者也走 update_curr_eevdf()
系列概况
| 字段 | 内容 |
|---|---|
| 标题 | [PATCH] sched/fair: Use update_curr_eevdf() for the remaining root cfs_rq callers |
| 作者 | Zhan Xusheng zhanxusheng@xiaomi.com |
| 版本 | 单封 PATCH(v1),最终被 tip: sched/urgent 接收 |
| 规模 | 1 个文件,2 处修改,+2/-2 |
| 修改文件 | kernel/sched/fair.c |
| 代码统计 | 2 insertions(+), 2 deletions(-) |
| Message-ID | 20260822105930.2352761-1-zhanxusheng1024@gmail.com |
| 完整性 | 含 commit message(含 Fixes/Signed-off-by)、patch diff、tip-bot merge 通知;Vincent Guittot 仅引用正文做背书 |
补丁目的
修复一个回归:在带 cgroup 的 root cfs_rq 上,pick_task_fair() 与 yield_task_fair() 调用的 update_curr(cfs_rq) 实际上不会更新 task 的 vruntime / deadline。
根因是 update_curr()走的是 cfs_rq->h_curr(group entity),在 !entity_is_task() 处直接 return;但调用方后面读的是 cfs_rq->curr(真正的 task entity),二者身份不一致,导致 EEVDF 的 entity_eligible() 等判断读到陈旧的 vruntime。把这两个调用点换成直接作用于 task 的 update_curr_eevdf(cfs_rq),让 cgroup 场景也能在 pick/yield 之前把 curr 刷新到最新。
旧流程的问题
pick_task_fair() / yield_task_fair()
|
v
update_curr(&rq->cfs) [OLD]
|
+-- read cfs_rq->h_curr (top-level group entity)
+-- if (!entity_is_task(curr)) -> return early [BUG]
|
v
read cfs_rq->curr (task entity) <-- NOT the same object |
v
entity_eligible() / pick_next_entity() use STALE vruntime
新流程
pick_task_fair() / yield_task_fair()
|
v
update_curr_eevdf(&rq->cfs) [NEW]
|
+-- locate cfs_rq->curr (task entity) directly
+-- __update_curr_eevdf(): vruntime += delta / weight
+-- update_deadline() / dl_server_update()
+-- resched_curr_lazy()
|
v
entity_eligible() / pick_next_entity() use FRESH vruntime
Patch 概览
仅两行替换,无新增函数、无结构性改动:
pick_task_fair()内update_curr(cfs_rq)->update_curr_eevdf(cfs_rq)yield_task_fair()内update_curr(cfs_rq)->update_curr_eevdf(cfs_rq)
关键实现
修改位置(kernel/sched/fair.c):
@@ -10052,7 +10052,7 @@ struct task_struct *pick_task_fair(struct rq *rq, struct rq_flags *rf)
- update_curr(cfs_rq);
+ update_curr_eevdf(cfs_rq);
@@ -10155,7 +10155,7 @@ static void yield_task_fair(struct rq *rq)
- update_curr(cfs_rq);
+ update_curr_eevdf(cfs_rq);
两个函数的语义差别:
update_curr():面向 hierarchy,取cfs_rq->h_curr;若为 group entity 则直接 return。update_curr_eevdf():直接作用在cfs_rq->curr(task),强制走__update_curr_eevdf(),刷新 vruntime / deadline / dl_server 等。
作者在 commit message 中给出定量证据(10s、三 busy 任务 + 一个 200us 周期任务,单核 pick_task_fair() 统计):
+-----------------------------+-----------+----------------+
| Scenario | calls | h_curr==curr |
+-----------------------------+-----------+----------------+
| all tasks in root cgroup | 43321 | 0 |
| busy in G0, periodic in G1 | 45211 | 45193 (noop) |
+-----------------------------+-----------+----------------+
也即在 cgroup 场景下,绝大多数 pick 实际上都错过了 update_curr() 的执行。
为什么平时看不出来?自 68e37487810a("Fix flat hierarchy")起,tick 与 enqueue / dequeue 已经能正确更新 curr,所以正常 reschedule 路径只缺 pick 前的那几微秒,作者测不到延迟差异。但仍有三条路径前面"没人帮我更新过":
- core scheduling 下兄弟核的
pick_task()(kernel/sched/core.c,那里显式先 update clock 正是为此) fair_server_pick_task()yield_task_fair():陈旧值直接喂给entity_eligible(),用于判断是否交还剩余 vruntime
这三条路径上 curr 可能落后整整一个 tick,相当于回退到 68e37487810a 之前的行为。
类比
把 update_curr() 想象成公司前台,她手里拿的是部门考勤汇总表(h_curr)。前台只更新这份汇总;如果部门本身就是 leaf,她就不再去更新每位员工的真实打卡时间。update_curr_eevdf() 则是直接走到员工本人(cfs_rq->curr)面前核对工时,把每分钟该计入 vruntime 的活儿都补上。原来的 pick_task_fair() 把这份工作交给了前台,所以一开 cgroup,员工考勤就漏更;换成 update_curr_eevdf() 后,无论员工挂在哪个部门,考勤都按他本人补齐,部门经理才能正确判断"这位员工手上的活儿干完没有"。
Highlight:风险与注意点
- cgroup 开启后,root cfs_rq 上
update_curr()长期是 no-op,但调用方都没察觉,因为它们读cfs_rq->curr而h_curr默默与之不一致——典型的"对的对象、对的字段读错"型 bug。 - 影响最大的是
yield_task_fair():stale vruntime 会直接喂给entity_eligible(),可能让 yield 提前放弃剩余时间片,恢复到 68e37487810a 之前的旧行为。 - core scheduling 兄弟核的
pick_task()、fair_server_pick_task()都属于"前面没人帮我更新"的路径,是重点回归对象。 - 改动标了
Fixes: 85570f10a4c6("sched/eevdf: Move to a single runqueue"),属于行为修复而非性能优化。常规 scheduler benchmark 可能看不到差异,建议专门压测 yield、core-scheduling、DL server 这几条路径。 - 不带 cgroup 时行为完全等价:
h_curr就是 task,原调用点本来就走完整update_curr()。本次只是补齐 cgroup 场景下的"漏更新",不会引入新行为。
版本变化
单封 PATCH,无 v2/v3。Vincent Guittot 仅引用正文做背书,未提出修改。最终被 tip: sched/urgent 接收(commit 1719d035a6fa90b7467b6daf45a573f5180013b2,committer Peter Zijlstra,2026-09-02)。
一句话总结
把 pick_task_fair() / yield_task_fair() 中错误引用 h_curr 而提前 return 的 update_curr() 替换为直接作用于 task entity 的 update_curr_eevdf(),让 cgroup 场景下 root cfs_rq 的 curr 在 pick / yield 之前被正确刷新,修复85570f10a4c6(EEVDF 单 runqueue 化)引入的回归。