0/6 已展开

LLM 分析

sched_ext:回退稳定版时跳过对已禁用任务的 ops.set_weight() 调用

系列概况

  • 标题[PATCH] sched_ext: Skip ops.set_weight() for disabled tasks(稳定线回退相关)
  • 作者:Kuba Piecuch(Google),Tejun Heo Ack;Greg KH(stable 维护者);Sasha Levin(stable 机器人)
  • 版本:上游主分支为单个 [PATCH];随后作为 [PATCH 7.1.y] 单独回退到 7.1 稳定线
  • 规模:仅 kernel/sched/ext.c(7.1.y 路径,主线为 kernel/sched/ext/ext.c),+11/-0,单 hunk
  • 修改文件kernel/sched/ext.c(stable)或 kernel/sched/ext/ext.c(主线)
  • 代码统计:1 file changed, 11 insertions(+)
  • Message-ID2026072131-citation-reshape-587c@gregkh(失败通知);DK4AN7P1B5KK.15R8G3B5L5E85@google.com(作者回应);2026072141-spotless-tarantula-5421@gregkh2026072139-phrasing-scroll-e4fa@gregkh(Greg KH 跟进,被截断);20260721150403.3680077-1-jpiecuch@google.com(正式 7.1.y 回退 patch);20260721201114.0009-stable-reply@kernel.org(Sasha 接收)
  • 完整性:主线 patch 已存在(commit 0e2f4ab68a89fad42e0f5a9ff4b740738e7aa1d6),回退路径经过 Greg KH 拒收 → 作者手工 cherry-pick → Sasha 入队 7.1 完整闭环

补丁目的

主线 commit 0e2f4ab68a89fad42e0f5a9ff4b740738e7aa1d6 已经合并了同一改动,本线程关注的是把这一改动同步到 7.1 稳定线。

触发的根本 bug 如下:

__sched_setscheduler() sequence when leaving SCX
+------------------------+
| sched_change_begin() |
|   switched_from_scx()  |
|   scx_disable_task(p)  | <- task enters SCX_TASK_DISABLED
|   ops.disable(p)       |   <- BPF scheduler may "forget" the task
|   __setscheduler_params()
|   set_load_weight()
|   reweight_task_scx(p) |
|     ops.set_weight(p)  |   <- called AFTER ops.disable(); breaks contract
|   p->sched_class = next_class
| sched_change_end()     |
+------------------------+

按照 BPF SCX ops 约定,ops.disable() 之后只能跟 ops.exit_task()ops.enable(),再调用 ops.set_weight() 等通用 ops 会让 BPF 端对一个已经"忘记"的对象做写操作。修复方法是在 reweight_task_scx() 顶部判断状态,若已不是 SCX_TASK_ENABLED 就直接 return

旧流程的问题

[scx task] -- __sched_setscheduler() --> [next class]
   |
   |-- scx_disable_task(p)         -- SCX_TASK_DISABLED
   |-- ops.disable(p)              -- BPF may have freed/cleaned p's BPF ctx
   |-- reweight_task_scx(p)
   |    +-- ops.set_weight(p, w)   -- writing to a "forgotten" object
   v may: stale ctx / negative ref / BPF writes out of band

风险点不是一定崩,而是违反 enable/disable 之间的对称约束。BPF 调度器实现可能假设 disable 之后不会再收到针对该任务的 ops,从而出现内部 map 负引用、release 后再 use,或在 exit_task/enable 之外发生状态写。

新流程

reweight_task_scx(rq, p, lw):
   if (task_dead_and_done(p)) return;
+ if (scx_get_task_state(p) != SCX_TASK_ENABLED)
+      return;
   p->scx.weight = sched_weight_to_cgroup(scale_load_down(lw->weight));
   if (SCX_HAS_OP(sch, set_weight))
       SCX_CALL_OP_TASK(sch, set_weight, rq, p, p->scx.weight);

任务如果以后重新回到 SCX,权重会在 scx_enable_task() 时被重新计算并下发,因此这里 return 是无损的。

Patch 概览

  • 主线:commit 0e2f4ab68a89fad42e0f5a9ff4b740738e7aa1d6,文件 kernel/sched/ext/ext.c:3967,作者 Kuba Piecuch。
  • 稳定线 7.1.y:commit adc748e669cc1564255430299abf086b9ecd9b45(cherry-pick 自 0e2f4ab68),文件 kernel/sched/ext.c:3904,同样是 +11/-0。

仅一处 hunk,新增 11 行(含 5 行注释和 6 行实际判断 + 提前返回)。

关键实现

/*
 * When switching sched_class away from SCX, reweight_task_scx()
 * is called _after_ scx_disable_task(). Skip calling ops.set_weight()
 * since the BPF scheduler may have already forgotten the task in
 * ops.disable().
 *
 * p->scx.weight will be recalculated in scx_enable_task() if the task
 * ever returns to SCX class.
 */
if (scx_get_task_state(p) != SCX_TASK_ENABLED)
    return;

判断位置选在 reweight_task_scx() 顶部,避开了 p->scx.weight = ... 赋值与后续 SCX_CALL_OP_TASK(...),所以即使跳过也不会让 p->scx.weight 残留错值;下一次进入 SCX 走 scx_enable_task() 时会重新算。

Fixes: 637b0682821b 指向 fold switch_{to,from} 的 commit,正是这次回调序变化的源头。Cc: stable 注明 v6.19+,所以同步给 7.1 稳定线。

##旧版 vs 新版对比图

                OLD (buggy)                       NEW (fixed)
+-----------------+--------------------+   +--------------------+----------+
| scx_disable(p)  |  task=DISABLED     |   | scx_disable(p)     | DISABLED |
| ops.disable(p)  |  BPF forgets p |   | ops.disable(p)     | BPF forgets |
| reweight_task_scx(p)                 |   | reweight_task_scx(p)         |
|   set_weight(p, w) <-- BAD: late op |   |   state != ENABLED  -> return  |
+-----------------+--------------------+   +--------------------+----------+
 |
 v
 scx_enable_task() recalcs weight

类比

把 SCX 任务想成酒店住客的房卡:退房(ops.disable)时前台已经把这张房卡注销;之后房间清扫时如果系统按旧卡号去记录清洁工时(ops.set_weight),就会把工时记到一张"已经销毁"的卡上,账单系统要么报错要么记错。

新版做法是:扫房时先确认房卡仍有效(scx_get_task_state(p) == SCX_TASK_ENABLED),无效就直接跳过;下一次入住办新卡时(scx_enable_task)会按新卡号重新计算工时,不会丢失。

Highlight:风险与注意点

  • 路径差异7.1.ykernel/sched/ext.c 是单文件,主线已拆成 kernel/sched/ext/ext.c。stable 机器人按上游 SHA 自动回退时只比对 blob 内容,不感知目录重命名,因此 cherry-pick 会失败,这是线程里 Greg KH 报告失败的真正源头,不是逻辑冲突。
  • 截断回复:第 3、4 封 Greg KH 的邮件在 lore 上看起来是空或被截断的回执,依赖 stable 机器人的反压机制(第 5、6 封才闭环);下游不能据此判断 Greg 是否真的认可改动。
  • 上游 commit Fixes: 637b0682821b 暗示只要内核还携带该 fold 改动,就都需要这一修复;任何在 6.19 之后手工定制的 backport 都应补这一 hunk。
  • 状态机局限scx_get_task_state(p) 只能反映 SCX 自身状态机;如果未来其他 BPF 调度器引入更细的 ops 状态,需要重新评估这一短路是否会漏掉合法调用。

版本变化

仅主线 commit → 7.1.y 回退这一段:

  • 主线:以 0e2f4ab68a89fad42e0f5a9ff4b740738e7aa1d6 入 master,文件路径 kernel/sched/ext/ext.c,偏移 3967。
  • 7.1.y 回退:以 adc748e669cc1564255430299abf086b9ecd9b45 重新打包,文件路径 kernel/sched/ext.c,偏移 3904(行号偏移来自缺少 -4@ 段头),差异仅是文件路径与 hunk 上下文,本体逻辑不变。

一句话总结

主线 sched_ext 修复让 reweight_task_scx 在任务已经 disable 后跳过 ops.set_weight();本线程用 [PATCH 7.1.y] 把同一短路逻辑同步进 7.1 稳定线,Sasha 已入队。