0/2 已展开

LLM 分析

sched:cgroup CPU 时间归到执行上下文(v2)

系列概况

  • 标题:[PATCH v2] sched: Account cgroup CPU time to the execution context
  • 作者:Hui Su sh_def@163.com
  • 版本:v2(共一封 patch,无系列号)
  • 规模:1 file changed, 1 insertion(+), 3 deletions(-)
  • 修改文件kernel/sched/fair.cupdate_se() 函数)
  • 代码统计:-3 +1
  • Message-ID20260904034707.268416-1-sh_def@163.com
  • 完整性:完整 diff + commit message + Fixes tag + Suggested-by + Signed-off-by + Changes since v1 + Testing 矩阵

补丁目的

修复 proxy execution 场景下 cgroup CPU 时间被错误归到 donor cgroup 的回归。当 donor(提供调度上下文的 task)和真正执行代码的 task 处于不同 cgroup 时,旧代码会把执行 task 的 CPU 时间算到 donor 的 cgroup,破坏 cgroup 资源隔离的语义。补丁让 cgroup CPU usage 跟随真正在跑的那个 task(rq->curr),与已有的 per-task / thread-group runtime 归属规则保持一致。

旧流程的问题

Commit aa4f74dfd42b("sched: Fix runtime accounting w/ split exec & sched contexts")引入了「split exec & sched contexts」:per-task 和 thread-group 的 runtime 改为跟随真正执行的 task,但 cgroup CPU usage 仍记到 donor。结果在跨 cgroup 的 proxy execution 中,同一段执行时间在 per-task 维度归自己、在 cgroup 维度归别人——两个视角不一致。

        donor (cgroup A)                executor (cgroup B)
              |                                |
              | proxy_exec sets donor           | actually runs code
              v                                v
        +-----------+                    +-----------+
        | sched ctx |  ----------------> |  exec ctx |
        +-----------+                    +-----------+
              |                                |
   per-task / tg runtime ----follows executor----+
   cgroup CPU usage -------charged to donor A----+  <-- WRONG

新流程

update_se() 中把 cgroup_account_cputime 的入参从 donor 改为 running(即 rq->curr,当前 rq 实际执行者)。scheduling 状态仍归 donor,cgroup user/system time 也仍由 task 维度提供——三方都遵循「执行上下文优先」这一原则。

        donor (cgroup A)                executor (cgroup B)
              |                                |
              | proxy_exec sets donor           | actually runs code
              v                                v
        +-----------+                    +-----------+
        | sched ctx |  ----------------> |  exec ctx |
        +-----------+                    +-----------+
              |                                |
   per-task / tg runtime ----follows executor----+
   cgroup CPU usage -------follows executor B---+  <-- FIXED
   (sched state still on donor A)

Patch 概览

只改 kernel/sched/fair.cupdate_se(),三行删一行增:

@@ -1404,7 +1404,6 @@ static s64 update_se(struct rq *rq, struct sched_entity *se)
 
     se->exec_start = now;
     if (entity_is_task(se)) {
-        struct task_struct *donor = task_of(se);
         struct task_struct *running = rq->curr;
-        /* cgroup time is always accounted against the donor */
-        cgroup_account_cputime(donor, delta_exec);
+        cgroup_account_cputime(running, delta_exec);
         account_group_exec_runtime(running, delta_exec);
         account_mm_sched(rq, running, delta_exec);
     } else {

核心改动:去掉 donor 局部变量与注释,把 cgroup 归属对象换成 running

关键实现

  • 函数update_se(),每次 task 离开 running 状态时被调用,是把"刚刚跑了多久"分摊到各账本的入口。
  • 分支entity_is_task(se) 表示当前 sched_entity 直接挂在一个 task_struct 上;否则是 group_entity,时间记到 se->sum_exec_runtime
  • 设计选择:v1 曾引入 per-rq cputime owner、deferred boundary handling、donor lifetime tracking 等机制试图追踪跨 cgroup 边界,被 Tejun Heo 指出过度设计。v2 直接复用 rq->curr——它就是当前 rq 上真正在跑的 task,归属于它天然不歧义。
  • cgroup user/system time:原本就以 task 为单位统计,不需要再切;这次只需要纠正 cgroup CPU usage 这一条漏网之鱼。

类比

把 cgroup 想象成「公司各部门的成本中心」。Proxy execution 就像 A 部门(donor)的主管被派到 B 部门顶岗干活:旧规则下这位主管在 B 部门打卡上班,但工资成本仍走 A 部门的账——B 部门的「本部门人工成本」凭空少了一截,A 部门则被多算。新规则下成本归实际干活的部门,B 部门的成本中心终于能反映真实工作负荷。

Highlight:风险与注意点

  • 回归来源清晰Fixes: aa4f74dfd42b 直接指向引入 split 的 commit,是 stable 回溯候选;任何启用了 CONFIG_SCHED_PROXY_EXEC 且跨 cgroup 部署的 workload 都会受影响。
  • 测试覆盖够广:作者给了 cgroup v2 proxy reproducer、RT donor reproducer、cgroup v1 cpuacct legacy、sched_proxy_exec=off 对照组、CONFIG_SCHED_PROXY_EXEC / CONFIG_CGROUPS / CONFIG_CGROUP_CPUACCT 构建矩阵、W=1checkpatch --strict
  • 后续观察点:跨 cgroup 边界处若 cpu.stat 仍依赖 donor 视角的旧接口,下游监控/计费可能仍读错;需要关注 cgroup v2 cpu.stat 各字段在 proxy exec 下的语义说明是否同步更新。
  • 未触达边界:CPU hotplug、cgroup throttled 边界、NMI/IRQ 时间不经过 update_se,不在本 patch 范围;不要误以为全 cgroup 计量都已被对齐。
  • Tejun Heo 的回复仅引用了 patch 上下文,文本上未给进一步的 LGTM / Reviewed-by 字样,落地状态以后续 maintainer 邮件为准。

版本变化

  • v1:引入 per-rq cputime owner、deferred boundary handling、donor lifetime tracking,并尝试把 cgroup user/system accounting 也搬到 scheduling context 上。结构复杂,被 Tejun 指出过度工程。
  • v2:放弃上述机制,最小改动——cgroup_account_cputime(running, delta_exec) 跟随执行上下文,scheduling state 仍归 donor。代码量从「多机制叠加」压缩到 -3 +1,机制简单也更对。

与其他相关 patch 系列的关联

  • aa4f74dfd42b("sched: Fix runtime accounting w/ split exec & sched contexts"):本 patch 直接修复的回归引入点,所有 split exec & sched contexts 概念都源于它。
  • CONFIG_SCHED_PROXY_EXEC:proxy execution 子系统本身的开关注入点;任何围绕「donor vs executor」归属的 patch 都要回答「算到谁头上」这个问题,本 patch 是「cgroup 维度跟随 executor」的最小范例。

一句话总结

把 cgroup CPU 时间从 donor 改归真正的执行 task,纠正 proxy execution 引入的 cgroup 归属回归。