sched discussion
FAILED: patch "[PATCH] sched/core: Make core-sched flips wait for in-flight" failed to apply to 6.12-stable tree
LLM 分析
core-sched flip 需等待 in-flight 选择完成
系列概况
- 标题: FAILED: patch "[PATCH] sched/core: Make core-sched flips wait for in-flight" failed to apply to 6.12-stable tree
- 作者: gregkh(stable 通知者);原始 patch 作者 Tejun Heo tj@kernel.org
- 版本: 单封 stable 回退失败通知,不构成 series
- 规模: 2 个文件,约 +23 行
- 修改文件: kernel/sched/core.c、kernel/sched/sched.h
- 代码统计: core.c +22、sched.h +1
- Message-ID: 2026090310-heftiness-overbite-2cac@gregkh
- 完整性: 仅通知邮件 + 内嵌原始 commit 文本,无后续讨论
补丁目的
core scheduling(CONFIG_SCHED_CORE)让 SMT 兄弟线程在一次共享核心锁获取期间统一完成任务选择。若某个 ->pick_task() 需要临时释放 rq 锁(lock-dropping),所有兄弟 __lock 会在这一瞬间全部空闲,__sched_core_flip(false) 就能在选择中途完成并把 rq_lockp() rebind 到新 leader。
选择恢复后继续在拆分锁上执行,触碰它已经不再保护的兄弟状态;最后 __schedule() 释放了一把从未持有的锁,同时泄漏了真正持有的那把。本 patch 用计数器让 flip 等到所有 in-flight 选择排空后再 rebind。
旧流程的问题
pick_next_task(): hold shared core lock
|
v
->pick_task() drops rq lock (lock-dropping)
---> all sibling __lock momentarily free <---
|
v
__sched_core_flip(false) completes in this window
rq_lockp() rebound to per-cpu split locks
|
v
selection resumes, still assumes shared lock
touches sibling state it no longer protects
|
v
__schedule(): unlock(never-taken) + leak(taken)
新流程
pick_next_task() entry
rq->core->core_pick_in_flight++ (under shared lock)
|
v
__sched_core_flip() holds shared lock, samples counter
while (core_pick_in_flight != 0):
sched_core_unlock() -> cpu_relax() -> sched_core_lock()
|
v
counter == 0 ==> safe to rebind rq_lockp()
|
v
pick_next_task() exit
rq->core->core_pick_in_flight--
Patch 概览
/* kernel/sched/sched.h */
struct rq {
/* ... */
unsigned int core_pick_in_flight;
};
/* kernel/sched/core.c: __sched_core_flip() */
while (cpu_rq(cpu)->core->core_pick_in_flight) {
sched_core_unlock(cpu, &flags);
cpu_relax();
sched_core_lock(cpu, &flags);
}
/* pick_next_task(): entry / exit */
rq->core->core_pick_in_flight++;
/* ... core-wide selection ... */
rq->core->core_pick_in_flight--;
/* sched_core_cpu_deactivate(): move, don't copy */
core_rq->core_pick_in_flight = rq->core_pick_in_flight;
rq->core_pick_in_flight = 0;
/* sched_init() */
rq->core_pick_in_flight = 0;
关键实现
-
计数器放在 leader 的
rq->core:所有 sibling 共用一个 leader rq,一个计数即可代表整组 SMT。 -
无需额外内存序:commit message 明确说明——计数只在共享锁下修改,而 flip 采样时正持有同一把共享锁,因此不需要 barrier。
-
退避式等待循环:flip 解锁、
cpu_relax()、再加锁重采样。选择重叠时可能重复等待,但 flip 属于罕见的 cookie-lifetime 事件,代价可接受。 -
sched_core_cpu_deactivate()必须 move 而非 copy:注释直言残留值会永久抬高计数——若该 CPU 日后重新成为自己的 leader,flip 会永远看到非零。 -
sched_init()清零:为字段提供确定初值。
类比
把一个 SMT core 想成餐厅里由同一位服务员统一照看的一排桌子,共享核心锁就是「统一点单广播」。pick_next_task() 是服务员一次性给整排桌子点单;->pick_task() 的 lock-dropping 相当于他中途去仓库取调料,把点单本先放桌上。
旧流程里,老板趁这个空档宣布「这排桌子拆开,各归各管」,广播频道当场换掉。服务员回来仍按旧频道继续写单,把别桌的菜算进来;收工时又签退了一张自己根本没接的桌子,真正接的那张反而留在手上——账目全乱。
新流程是老板动手前先看一眼「还有几桌正在点单」的计数牌:不为零就退回柜台稍等(cpu_relax()),归零后才宣布拆桌。而 deactivate 时的 move 语义,等于老店关门时把计数牌交给新店主,而不是复印一份留在原地;否则老店重开时那份陈旧读数会让老板永远以为还有人在点单。
Highlight:风险与注意点
-
无法直接 cherry-pick 到 6.12-stable:这封邮件本身就是 Greg KH 的失败通知,commit
f3629c63a4af3e491381780bc6c123cb498c4c40需人工解决冲突并按PATCH 6.12.y前缀重新提交,否则 v5.14+ 长期分支拿不到该修复。 -
等待循环的极端 stall:若某个
->pick_task()因其它 bug 永不归零,flip 会持续 spin。排查 hotplug/cookie 相关 hang 时应留意 flip 是否卡在此循环。 -
move 语义易被 refactor 破坏:漏掉
rq->core_pick_in_flight = 0;这一行不会立刻报错,但会在后续 hotplug 场景埋下永久偏差。 -
拓扑假设:单一 leader-local 计数依赖「sibling 共享同一
rq->core」的不变式;若未来 core 分组跨 die 重组,需重新评估。
一句话总结
该 patch 用 leader rq 上的 core_pick_in_flight 计数强制 __sched_core_flip() 等待所有 in-flight 核心级选择排空后再 rebind rq_lockp(),并在 CPU deactivate 时 move 而非 copy 计数;stable 维护者已通知它无法干净应用到 6.12-stable,需要手工回退。