sched discussion
[PATCH v3 0/7] sched: Flatten the pick
LLM 分析
sched: Flatten the pick - cgroup_mode +扁平 rq 分析
系列概况
- 标题:
sched: Flatten the pick - 作者:Peter Zijlstra (Intel) peterz@infradead.org
- 版本:v3,共 7 个 patch
- 修改文件:include/linux/cpuset.h、include/linux/sched.h、kernel/cgroup/cpuset.c、kernel/sched/{core,debug,fair,pelt}.c、kernel/sched/sched.h
- 代码统计:0/7 邮件给出 8 文件 +607/-488;fair.c 改动最重;patch 7/7 单条 803 增 / 475 删
- Message-ID 起点:
20260605105513.354837583@infradead.org - 完整性:完整 v3 系列 + 6 条讨论回复 + Chen Yu u32 溢出 fix + 7 条 tip-bot2 自动合入通知 + 后续 s390 / wake_affine_weight 追讨
补丁目的
解决 cgroup 层级权重按 1/N^d 衰减导致的跨层级公平性问题;并在此基础上把 CFS 的 EEVDF 从多层 cfs_rq 折叠为 rq->cfs 单层队列,去掉 pick_next_task 路径上的中间 entity,提升 P99 延迟。
旧流程的问题
calc_group_shares() 按 CPU 比例分配 tg->shares,effective weight = W_g * F_g_n * (W_t/ΣW_tn),其中 average(F_g_n) ~ 1/N。深度 d 的 cgroup 树进一步出现 1/(N^d) 量级的偏差,加上 fixed-point 量化噪声,远端 cgroup 任务实际拿到的 CPU 远低于相对权重暗示;并且 EEVDF 在多层 rq 上挑选时,中间 group se 会遮挡真实任务,损害延迟。
新流程
- 引入 debugfs
sched/debug/cgroup_mode,5 档可选:up / smp / max / concur / tasks calc_group_shares改为DEFINE_STATIC_CALL,__sched_cgroup_mode_update()运行时替换实现- 新默认
concur,按min(M, N) * shares(M=tasks、N=cpus)发放 - Patch 7/7 把 EEVDF rbtree 从 cgroup 内部挪到
rq->cfs,新增se->h_load表达层级有效 weight
Patch 概览
- 1/7 Add cgroup_mode switch:debugfs 切换 + static_call 钩子
- 2/7 Add cgroup_mode: up:F_g_n=1,全量下发
- 3/7 Add cgroup_mode: max:N*shares,clip 到 nice -20;新增
cpuset_num_cpus - 4/7 Add cgroup_mode: concur:
min(M,N)*shares;新增tg_tasks/tg->runnable_avg - 5/7 Add cgroup_mode: tasks:M*shares
- 6/7 Change default to concur:调整 switch 顺序让 case 2 默认
- 7/7 sched/eevdf 单 rq:引入
se->h_load、cfs_rq->h_load,删除find_matching_se
关键实现
struct sched_entity {
struct load_weight load;
struct load_weight h_load; /* 层级有效 weight */
...
};
Layered cfs_rq (old)
R
|- G1(se)
| |- G2(se) -- T1(se)
| \- T2(se)
|- G3(se)
\- T3(se)
Flat rq->cfs (after Patch 7/7)
rq->cfs (single EEVDF tree)
|--- T1
|--- T2
|--- T3
cgroup_mode switch (debugfs)
0: up -- F_g_n' = 1
1: smp -- current formula
2: concur -- min(M,N) * shares (new default)
3: max -- N * shares clip -20
4: tasks -- M * shares
__calc_smp_shares(cfs_rq, tg_shares, shares_max) 是各 mode 公用底座,结果夹在 [MIN_SHARES, shares_max]。concur 由 tg_tasks(tg) 取 tg->runnable_avg >> SCHED_CAPACITY_SHIFT,与 tg_cpus(tg) 取小后乘 shares。Patch 7/7 在 __enqueue_entity 增加 WARN_ON_ONCE(&rq_of(cfs_rq)->cfs != cfs_rq) 强制所有 EEVDF 操作集中在 rq->cfs;find_matching_se 整段删除,pick_next_task 不再跨层调度。
类比
把 cgroup 想成"集团-分公司-部门"三级预算分配:
- 老办法像按全集团人头数均分公司预算——部门越大越被摊薄,深处的部门几乎分不到钱
concur模式按"实际在岗项目数和一台机器能装下的最大人数"双封顶发钱,项目少时按项目数、人多时按机器数,永远保证每一级拿到公平的筹码- 扁平 rq 像把部门排班表直接挂到集团总册,集团总裁只看一份排班表就决定谁先上工,不用层层审批 pick_next_task 的中间节点
Highlight:风险与注意点
update_tg_cfs_runnable中u32 new_sum = se->avg.runnable_avg * divider在大 weight 下溢出,Chen Yu C 以u64 new_sum修复,commit4f166adb5cb0已合入 sched/corewake_affine_weight改为读task_h_load(p)后,与 this_cpu/prev_cpu 的cpu_load比较更"诚实",s390 +WA_WEIGHT跑 netperf 时 this_cpu 偏好下降、L2 miss% 上升、吞吐下滑(见 0/7后续回复链)cpuset_num_cpus仅覆盖 cgroup-v2 的effective_cpus;v1、arm64task_cpu_possible_mask()受限子集暂不支持,Waiman Long 建议加注释明确边界__sched_cgroup_mode_update()与sched_dynamic_update()同入口串联调用,运行中切换窗口须保证 static_call 写入顺序- Patch 7/7 遗留 TODO:拆分
struct cfs_rq/struct sched_entity、去掉h_curr - s390 hackbench + stress-ng 并跑在低 thread 数下退化;Prateek 建议重测
68e3748781"Fix flat hierarchy" 之后的 tip 才能下结论 - 0day 机器人曾抓出 patch 6/7 引起的"任务变重、ksoftirqd 调度减少"的网络吞吐回归,Chen Yu 提议降低 ksoftirqd slice 但尚未落地
版本变化
v2 -> v3(0/7 邮件 changelog):
- 合并 debug / prep patches
- Vincent Guittot:修复
update_entity_lag()on dequeue - Prateek:修复 throttle vs tick
- Prateek:修复
wakeup_preempt_fair() - rebase 到 tip/sched/core
- 重写 cgroup_mode changelog
- rework cgroup_mode concur
- 新增 cgroup_mode tasks
- 默认 cgroup_mode 改为 concur
一句话总结
Peter Zijlstra 用 5 档可切换的 cgroup_mode 把层级权重从 1/N^d 拉回 1,再用 sched/eevdf 单 rq 化去掉 pick 路径上的中间 entity,并修掉了 update_tg_cfs_runnable 的 u32 溢出与新权重下 wake_affine_weight 的回归。