sched discussion
[PATCH] sched/cputime: Account cgroup fields to the scheduling context
LLM 分析
sched/cputime:把 cgroup 字段记账到调度上下文
系列概况
- 标题:
[PATCH] sched/cputime: Account cgroup fields to the scheduling context - 作者: Hui Su
<sh_def@163.com> - 版本: 单封 patch,v1(无 series 编号、无
[PATCH v2]之类重发标识) - 规模: 单 patch,diff 涉及
kernel/sched/core.c、kernel/sched/cputime.c、kernel/sched/sched.h三个文件;cputime.c 净增约 130 行,core.c/sched.h 各净增约 5–10 行(lore body 内 diff 末尾被截断,精确 stat 需b4重放) - Message-ID:
5733b51108eda90c1bda98a68d58b5e6ccbc24ec.1788433334.git.sh_def@163.com - 完整性: commit message 文本在 lore body 中被截断(
...this can ch处断开),但 diff 主体完整;下游翻译/评审需从 diff 反推 commit message
补丁目的
CONFIG_SCHED_PROXY_EXEC 把"调度上下文"(donor)与"执行上下文"(execution task)解耦后,cgroup_account_cputime()(来自 aa4f74dfd42b)把 cpu.stat.usage_usec 计到 donor cgroup;同一条 tick/vtime 路径上的 cpu.stat.user / cpu.stat.system 字段却仍按执行任务所属 cgroup 写入。当 donor 与 execution 不在同一 cgroup 时,单一 cgroup 视角下 usage_usec 跑到 donor,user/sys 字段跑到 execution,cgroup 自洽性被打破。补丁在 rq 上新增 cputime_donor/cputime_donor_hold,并把 cgroup 字段(user/sys 等)也路由到 donor,使 cgroup 内部统计自洽。
旧流程的问题
- aa4f74dfd42b 引入的
cgroup_account_cputime()计 usage 到 donor。 - tick/vtime 路径中
account_user_time/account_system_time/account_guest_time/__account_forceidle_time调用task_group_account_field(p, index, tmp),p是current,即 execution task。 - donor 与 execution 跨 cgroup 时:donor cgroup 的
usage_usec增长,但user/sys在 execution cgroup 增长;同一 cgroup 内统计不再自洽。
新流程
struct rq新增两个字段:cputime_donor:实际接收 cputime 记账的 task。cputime_donor_hold:在远端切换期间持引用,避免并发释放。
- 三组新接口(
sched_cputime_donor_changed/sched_cputime_donor_defer/sched_cputime_switch):defer用于"非本地上下文"(rq != this_rq() || !in_task()),延迟 donor 切换。changed在切换前先 flush 当前任务的 vtime,再更新cputime_donor。switch在finish_task_switch中更新cputime_donor,并把 prev 的 vtime flush 到旧 donor。
- 把原来的
task_group_account_field拆为两条:account_cpustat_field(index, tmp):per-CPUkernel_cpustat,按 current 视角写入。account_cgroup_field(task, index, tmp):cgroup 字段,按cgroup_account_task(p)返回的 donor 写入。
cgroup_account_task(p)在 proxy execution 下返回rq->cputime_donor,否则返回p。- 新增
vtime_flush_pending(tsk),在 seqcount 保护下把utime/gtime/stimeflush 出去,避免 donor 切换跨边界丢账。
Patch 概览
kernel/sched/core.c:proxy_reset_donor、finish_task_switch、__schedule、init_idle四处分别接入sched_cputime_donor_changed/defer/switch,并把cputime_donor初始化为 idle task。kernel/sched/cputime.c:拆分 cpustat 与 cgroup 字段两条记账路径;新增 donor 引用管理与 flush 接口。kernel/sched/sched.h:在struct rq中增加cputime_donor、cputime_donor_hold,声明三个外部 helper。
关键实现
/* cgroup 字段记账目标 */
static inline struct task_struct *cgroup_account_task(struct task_struct *p)
{
#ifdef CONFIG_SCHED_PROXY_EXEC
struct task_struct *donor;
if (sched_proxy_exec()) {
donor = this_rq()->cputime_donor;
if (donor)
return donor;
}
#endif
return p;
}
/* 把字段写入 per-CPU kernel_cpustat */
static inline void account_cpustat_field(int index, u64 tmp)
{
/* 直接累加到 cpu_rq(this_cpu())->cpustat */
}
/* 把字段写入 task 视角的 cgroup */
static inline void account_cgroup_field(struct task_struct *p, int index, u64 tmp)
{
/* 累加到 p 所属 cgroup 的对应字段 */
}
/* account_user_time 改造后 */
void account_user_time(struct task_struct *p, u64 cputime)
{
struct task_struct *cgroup_task = cgroup_account_task(p);
int index, cgroup_index;
index = (task_nice(p) > 0) ? CPUTIME_NICE : CPUTIME_USER;
cgroup_index = (cgroup_task == p)
? index
: ((task_nice(cgroup_task) > 0) ? CPUTIME_NICE : CPUTIME_USER);
account_cpustat_field(index, cputime);
account_cgroup_field(cgroup_task, cgroup_index, cputime);
}
proxy execution rq cgroup stats
+----------------------------+ +-------------------------+
| rq->donor (owner) |--usage--> | donor-cgroup: usage_usec|
| rq->curr (driver) |--u/sys--> | exec-cgroup : u/sys |
| rq->cputime_donor (owner) | | |
+----------------------------+ +-------------------------+
| sched_cputime_donor_changed()
| 1) flush vtime of rq->curr
| 2) rq->cputime_donor = new_donor
v
+----------------------------+
| vtime_flush_pending(curr) | -- account_user_time / account_system_time
+----------------------------+ routed to cputime_donor
类比
把代理执行想成"代驾":
- donor = 车主,付油费、对外签单。
- execution task = 临时司机,实际驾驶。
cgroup_account_cputime()把"油费总额"记到车主账上(aa4f74dfd42b 的设计)。- 但 tick/vtime 路径上的"驾驶时长 / 怠速时长"却记到了司机的 cgroup。
- 补丁试图把"驾驶/怠速时长"也算给车主,让账统一在车主名下。
- Tejun 的反驳:用户空间习惯"谁开车记谁的账",更合理的修法是把油费总额也从车主账改成司机账——账永远跟着开车的司机走。
- Hui 接受这个更简单的方向,预计 v2 会丢弃 cputime boundary 与 donor 生命周期管理。
Highlight:风险与注意点
cputime_adjust()内部会把 user/sys 按usage_usec的比例重新对齐,原 commit message 把问题描述为"用户可见的 cpu.stat 不一致"有些过度;Tejun 指出 user/sys 与 usage 的偏差在用户态可见窗口里不会长期失衡。cputime_donor_hold+get_task_struct引入新的 task 引用生命周期,异常/抢占路径上若漏put_task_struct会出现 task 引用泄漏。sched_cputime_donor_changed/sched_cputime_switch三种上下文(rq lock held / lockdep released across switch / idle)下手动lockdep_assert_*,任何新增 caller 都必须重新审视锁状态。vtime_flush_pending同时存在于CONFIG_VIRT_CPU_ACCOUNTING_GEN与_NATIVE两条路径下,跨架构(idle 退出、IRQ exit、guest)的一致性需要单独验证。- Tejun 倾向"cgroup 全部跟随 execution context",Hui 也认同;v2 大概率会重写为更小的修复,丢掉 cputime boundary 与 donor 生命周期机制,下游评审应关注重写后是否真的只改 cgroup 总量侧。
- Tejun 提到"先听 John 的意见"——指 John Stultz(proxy execution 的主要维护者),最终落地方向取决于他对该模型的取舍。
- lore body 中 commit message 文本被截断在
...this can ch,review/翻译需自行补全;正式合入前应重放补丁取得完整 diff。
版本变化
本帖仅 v1 草稿;按 Tejun/Hui 的讨论,v2 方向预计:
- 丢弃
cputime_donor/cputime_donor_hold; - 丢弃
sched_cputime_donor_changed/sched_cputime_donor_defer/sched_cputime_switch; - 不再拆
account_cpustat_field/account_cgroup_field,恢复单一task_group_account_field路径; - 改为让
cgroup_account_cputime()按current(execution task)计usage_usec,使 cgroup 整体跟随 execution context; - 关注 v2 是否真的能"砍掉" cputime boundary 与 donor 生命周期作为简化收益。
一句话总结
Hui Su 试图把 cgroup user/system 字段也对齐到 donor,与 aa4f74dfd42b 的 donor-based 用量统计保持自洽;Tejun 指出用户空间以 execution context 为准且 cputime_adjust() 会重对齐 user/sys,cgroup 应整体跟随 execution task,Hui 同意并准备重写为更小的修复,等待 John Stultz 评审 proxy execution 的 cgroup 模型。