0/4 已展开

LLM 分析

sched_ext:避免对相同 idle 值重复派发 cgroup_set_idle 回调

系列概况

  • 标题:[PATCH] sched_ext: don't deliver duplicate ops.cgroup_set_idle() for same value
  • 作者:Tao Cui cuitao@kylinos.cn
  • 版本:v1(单封 patch,邮件标题无 [vN] 前缀)
  • 规模:1 个文件,+2/-1 行
  • 修改文件:kernel/sched/ext/ext.c
  • 代码统计:1 file changed, 2 insertions(+), 1 deletion(-)
  • Message-ID:20260901031101.731943-1-cui.tao@linux.dev
  • 完整性:commit message + diff + Fixes tag + Signed-off-by 完整;Link: 标签为空(被 Andrea Righi 在 review 中指出)

补丁目的

ops.cgroup_set_idle() 在文档里被定义成"cgroup 在 idle 与非 idle 之间发生跃迁时"才触发;但 scx_group_set_idle() 当前是无条件派发——只要用户再次写入与当前相同的 cpu.idle 值,BPF 调度器都会收到一次伪"跃迁"。对基于切换计数或累计的 BPF 调度器而言,重复写入会被错算成多次状态切换。

本补丁在派发条件里追加 tg->scx.sched_idle != idle,让"无变化"的写入短路掉,与 scx_group_set_weight() 已有的 value-preserving 跳过路径对齐。

旧流程的问题

每次写入 cpu.idle 都直接通过 SCX_CALL_OP 触发 BPF 侧的回调:

if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle))
    SCX_CALL_OP(sch, cgroup_set_idle, NULL, tg_cgrp(tg), idle);

等值写入会让 BPF 调度器误以为发生了一次 idle ↔ non-idle 跃迁:做 toggle 的调度器多翻一次开关,做累计的调度器多计一次 idle 周期,依赖"只在边界触发"语义的实现会失真。

新流程

if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle) &&
    tg->scx.sched_idle != idle)
    SCX_CALL_OP(sch, cgroup_set_idle, NULL, tg_cgrp(tg), idle);

只有当目标 idle 值与上次记录的 sched_idle 不一致时才真正调用 BPF 回调,与 scx_group_set_weight() 的 guard 模式对称。

+-----------+      old flow (unconditional)       +-----------+
|  write    | -- always -->  SCX_CALL_OP(...) --> | BPF sched |
| cpu.idle  |                                      +-----------+
+-----------+

+-----------+   new flow (guarded)                 +-----------+
|  write    | -- guard sched_idle != idle ? -----> | BPF sched |
| cpu.idle  |       \                              +-----------+
|           |        \--> skip if same value (no callback)
+-----------+

Patch 概览

  • 修改点:kernel/sched/ext/ext.c 的 scx_group_set_idle()
  • 修改前后对比:
- if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle))
+ if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle) &&
+     tg->scx.sched_idle != idle)
      SCX_CALL_OP(sch, cgroup_set_idle, NULL, tg_cgrp(tg), idle);
  • 对称做法:与 scx_group_set_weight() 跳过等值写入的模式对齐。
  • 修复目标 commit:347ed2d566da ("sched/ext: Implement cgroup_set_idle() callback")

关键实现

void scx_group_set_idle(struct task_group *tg, bool idle)
{
    percpu_down_read(&scx_cgroup_ops_rwsem);
    sch = scx_tg_knob_sched(tg);

    if (scx_cgroup_enabled && sch && SCX_HAS_OP(sch, cgroup_set_idle) &&
        tg->scx.sched_idle != idle)            /* new guard */
        SCX_CALL_OP(sch, cgroup_set_idle, NULL, tg_cgrp(tg), idle);

    /* Update the task group's idle state */
}

守卫条件 tg->scx.sched_idle != idle 借助 task_group 的 scx 子结构里记录的"上次派发值"做比较,避免重复跃迁事件。

类比

把它想成宿舍楼里"住户在家 / 外出"的门牌翻牌器:

  • 旧流程:管理员每次路过都随手翻一次门牌,哪怕住户其实没变。结果楼栋统计系统以为又发生了一次出入,电梯流量计费也跟着虚增。
  • 新流程:管理员翻牌前先瞄一眼当前牌面,住户真变了才翻——楼栋统计、电梯计费都只反映真实事件,BPF 调度器也就只收到"真跃迁"。

Highlight:风险与注意点

  1. tg->scx.sched_idle 字段不一定存在:sashiko-bot 静态扫描以 Low 严重度报警,指出 tg->scx 中可能没有 sched_idle 成员,理论上会导致编译失败。Andrea Righi 仍给出 Reviewed-by,意味着该字段在他基于的内核树里存在,或者由另一个配套 patch 引入;合入前应核对上游是否同步具备成员定义,必要时把字段定义也带上。
  2. patch 没有展示对 sched_idle 的赋值更新。原函数 SCX_CALL_OP 之后还有 /* Update the task group's idle state */ 注释——对称地看本 patch 可能缺少"回调之后写回 sched_idle"那一行,否则 guard 会一直读到同一个旧值,重复写入仍可能继续派发。建议 v2 同步补齐赋值。
  3. Link: 标签为空:Andrea Righi 在 review 里明确指出 "this link seems broken",需要在重新提交时补上一个真实可访问的归档 / 上游链接(例如 lore.kernel.org 自己的 URL)。
  4. 测试口径只有"打印回调次数"的探针:足以证明回调不再重复,但未覆盖 BPF 侧 toggle / 累计统计是否仍正确;后续 v2 可补一份对照实验,把"两次相同 idle 写入"前后 BPF 内部计数器的快照记录下来。
  5. 命名一致性:把 tg->scx.sched_idleidle 命名风格放在一起略显不一致(一个是"sched idle"语义字段,一个是 bool 形参),维护者可能在 v2 中建议改成 tg->scx.idle 或类似形式以减少认知负担。

一句话总结

scx_group_set_idle() 的派发条件里追加"新旧值不同才回调"的守卫,避免相同 idle 值被反复误算成跃迁事件,与 scx_group_set_weight() 的现有做法保持对称。