0/4 已展开

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.ckernel/sched/cputime.ckernel/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)pcurrent,即 execution task。
  • donor 与 execution 跨 cgroup 时:donor cgroup 的 usage_usec 增长,但 user/sys 在 execution cgroup 增长;同一 cgroup 内统计不再自洽。

新流程

  1. struct rq 新增两个字段:
    • cputime_donor:实际接收 cputime 记账的 task。
    • cputime_donor_hold:在远端切换期间持引用,避免并发释放。
  2. 三组新接口(sched_cputime_donor_changed / sched_cputime_donor_defer / sched_cputime_switch):
    • defer 用于"非本地上下文"(rq != this_rq() || !in_task()),延迟 donor 切换。
    • changed 在切换前先 flush 当前任务的 vtime,再更新 cputime_donor
    • switchfinish_task_switch 中更新 cputime_donor,并把 prev 的 vtime flush 到旧 donor。
  3. 把原来的 task_group_account_field 拆为两条:
    • account_cpustat_field(index, tmp):per-CPU kernel_cpustat,按 current 视角写入。
    • account_cgroup_field(task, index, tmp):cgroup 字段,按 cgroup_account_task(p) 返回的 donor 写入。
  4. cgroup_account_task(p) 在 proxy execution 下返回 rq->cputime_donor,否则返回 p
  5. 新增 vtime_flush_pending(tsk),在 seqcount 保护下把 utime/gtime/stime flush 出去,避免 donor 切换跨边界丢账。

Patch 概览

  • kernel/sched/core.cproxy_reset_donorfinish_task_switch__scheduleinit_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_donorcputime_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:风险与注意点

  1. cputime_adjust() 内部会把 user/sys 按 usage_usec 的比例重新对齐,原 commit message 把问题描述为"用户可见的 cpu.stat 不一致"有些过度;Tejun 指出 user/sys 与 usage 的偏差在用户态可见窗口里不会长期失衡。
  2. cputime_donor_hold + get_task_struct 引入新的 task 引用生命周期,异常/抢占路径上若漏 put_task_struct 会出现 task 引用泄漏。
  3. sched_cputime_donor_changed / sched_cputime_switch 三种上下文(rq lock held / lockdep released across switch / idle)下手动 lockdep_assert_*,任何新增 caller 都必须重新审视锁状态。
  4. vtime_flush_pending 同时存在于 CONFIG_VIRT_CPU_ACCOUNTING_GEN_NATIVE 两条路径下,跨架构(idle 退出、IRQ exit、guest)的一致性需要单独验证。
  5. Tejun 倾向"cgroup 全部跟随 execution context",Hui 也认同;v2 大概率会重写为更小的修复,丢掉 cputime boundary 与 donor 生命周期机制,下游评审应关注重写后是否真的只改 cgroup 总量侧。
  6. Tejun 提到"先听 John 的意见"——指 John Stultz(proxy execution 的主要维护者),最终落地方向取决于他对该模型的取舍。
  7. 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 模型。