sched discussion
FAILED: patch "[PATCH] sched/core: Make core-sched flips wait for in-flight" failed to apply to 6.6-stable tree
LLM 分析
core-sched: 让 flip 等待 in-flight selection 完成
系列概况
- 标题:FAILED: patch "[PATCH] sched/core: Make core-sched flips wait for in-flight" failed to apply to 6.6-stable tree
- 作者:Greg Kroah-Hartman(stable 机器人回信);原始作者 Tejun Heo
<tj@kernel.org> - 版本:单封 backport 失败通知;上游原始提交
f3629c63a4af3e491381780bc6c123cb498c4c40 - 规模:1 封邮件,附带 1 个 4 文件的 diff(stable 6.6 上无法干净应用)
- 修改文件:
kernel/sched/core.c、kernel/sched/sched.h - 代码统计:core.c 约 +15 行,sched.h +1 行(新增字段
core_pick_in_flight) - Message-ID:
2026090311-paramedic-jaybird-0ada@gregkh - 完整性:原始 patch 完整提供;stable 回放命令也给出,但因为 6.6 代码已偏移无法自动 cherry-pick
补丁目的
core scheduling 下,pick_next_task() 在共享的 core-wide rq lock 内为所有 SMT sibling 选下一个任务。但 ->pick_task() 调度类回调允许在选核过程中临时释放 rq lock。一旦 lock 被 drop,__sched_core_flip()(用于启用/关闭 core sched 的开关)就可能在 selection 中途完成,把 rq_lockp() 重新绑定到拆分后的 per-rq lock。
结果 selection 会在"分家后"的锁上继续操作兄弟 rq 的状态,而它已经不再持有对应的锁了;最后 __schedule() 释放一把根本没拿到的锁,并泄漏另一把真正拿到的锁。这是一个典型的锁序错乱导致的资源泄漏和状态损坏。本 patch 通过引入 core_pick_in_flight 计数器,让 flip 在仍有 in-flight 的 core-wide selection 时阻塞等待。
旧流程的问题
- Leader CPU 拿到 shared core lock,开始
pick_next_task()。 - 某个 SMT sibling 的
->pick_task()因为 I/O、调度等原因 drop 了 rq lock。 - 此刻
__sched_core_flip(false)在另一线程抢占成功,把rq_lockp()重新指向拆分后的 per-rq lock。 - 原来的 selection 在拆分锁上恢复执行,触摸兄弟状态却没有持有对应的核心锁。
__schedule()退出时还按"shared lock"释放——错位释放导致锁泄漏。
新流程
- 在 leader rq 上维护
core_pick_in_flight:pick_next_task()入口++,出口--。__sched_core_flip()在采样时若发现计数非零,drop shared lock、relax、重抢,直到 drain。
- 因为计数器只在 shared core lock 下变化,flip 在持锁时采样,不需要额外的内存屏障或顺序规则。
- flip 极少发生(cookie 生命周期事件),selection 也不会无限重叠,所以这种自旋等待代价可接受。
sched_core_cpu_deactivate()在 leader 移交时移动而不是拷贝这个计数——否则将来该 CPU 重新成为自己 leader 时,旧计数会"永远偏置"。
Patch 概览
kernel/sched/core.c—__sched_core_flip():增加while (core_pick_in_flight)等待循环。kernel/sched/core.c—pick_next_task()头尾:core_pick_in_flight++ / --。kernel/sched/core.c—sched_core_cpu_deactivate():转移计数到新 leader。kernel/sched/core.c—sched_init():初始化为 0。kernel/sched/sched.h:struct rq新增unsigned int core_pick_in_flight。
关键实现
static void __sched_core_flip(bool enabled)
{
...
while (cpu_rq(cpu)->core->core_pick_in_flight) {
sched_core_unlock(cpu, &flags);
cpu_relax();
sched_core_lock(cpu, &flags);
}
...
for_each_cpu(t, smt_mask)
cpu_rq(t)->core_enabled = enabled;
}
pick_next_task(struct rq *rq, struct rq_flags *rf)
{
...
rq->core->core_pick_in_flight++;
...
rq->core->core_pick_in_flight--;
}
sched_core_cpu_deactivate(unsigned int cpu)
{
...
core_rq->core_pick_in_flight = rq->core->core_pick_in_flight;
rq->core->core_pick_in_flight = 0;
}
类比
想象一栋写字楼的"全楼广播系统":保安主任(leader)要向所有楼层同时下达同一指令,必须等所有楼层经理(sibling)都签收完毕才算一次"完整广播"。
某天 3 楼的经理说"我去趟洗手间",暂时放下手中的签收单(drop rq lock)。这时物业总经理(flip)跑过来说"切换广播频道",于是 3 楼回来时听到的指令来自另一套系统——这位经理接下的指令和手里那份旧签收单对不上号,最后交回一把"根本没用过的钥匙"。
修法就是在大堂放一块白板,写着"当前正在广播 N",每次广播开始 +1、结束 -1;总经理想切频道时先看一眼白板,不为零就先去倒杯咖啡等广播结束。
Highlight:风险与注意点
- stable 树冲突:6.6-stable 上的
kernel/sched/core.c已经偏移,机器人无法自动 cherry-pick。需要stable@vger.kernel.org接收人工 backport,提交时附原始 commit idf3629c63a4af…。 - Fixes 标签:标记为
Fixes: 539f65125d20(v5.14+ 引入 core scheduling),所以理论上需要回灌到所有受影响的 stable/longterm 分支。 - drop-lock 的合法窗口:
->pick_task()drop rq lock 是 EINTR/EAGAIN 友好的设计,本身没问题;问题是 flip 不能在这个窗口里把锁"换皮"。 - hotplug 计数移动:
sched_core_cpu_deactivate()用"move"语义而不是"copy",否则旧 leader 重新上线时计数偏差。 - Acks:已被 Peter Zijlstra ack,社区认可度高,但仍需 maintainer 进一步合入。
+--------+ shared core lock +-----------+
| CPU A | <-------------------------> | CPU A' |
| leader | | sibling |
+--------+ +-----------+
| pick_next_task begins |
| core_pick_in_flight = 1 |
| v
| ->pick_task() drops rq_lock ---> temporarily unlocked
| ^
| __sched_core_flip() tries |
| sees count != 0, backs off |
| (unlock/relax/relock loop) |
v |
| selection resumes under shared lock
| core_pick_in_flight = 0
v
flip proceeds, rebinds rq_lockp() safely
版本变化
本线程只是 stable backport 失败通知,没有 vN→vN+1 的多版本迭代。原始 patch f3629c63a4af 即最终形态,已并入 Linus 树。
一句话总结
通过 leader rq 上的 core_pick_in_flight 计数器,让 __sched_core_flip() 在仍有 core-wide selection 进行时阻塞等待,避免 rq_lockp() 在 drop-lock 窗口被错误地重绑定,从而修掉一个潜伏 5 年之久的 core-sched 锁泄漏与状态损坏隐患。