sched discussion
FAILED: patch "[PATCH] sched_ext: Take cgroup_lock() first in scx_cgroup_lock()" failed to apply to 6.18-stable tree
LLM 分析
sched_ext:scx_cgroup_lock() 死锁修复与 6.18-stable 回迁
系列概况
- 主题:sched_ext 锁顺序修复 + stable 回迁
- 主线作者:Tejun Heo tj@kernel.org
- Stable 作者:Sasha Levin sashal@kernel.org
- Stable 通知:Greg KH 自动化机器人
- 版本:6.18-stable (6.18.y)
- 规模:1 封 apply-failed 通知 + 1 套 3 段 stable 补丁系列
- 修改文件:
kernel/sched/ext.c(主线原文件路径是kernel/sched/ext/ext.c) - Message-ID:
2026081715-muscular-drizzly-bfad@gregkh;20260819111420.3549656-1/2/3-sashal@kernel.org - 完整性:完整,含主线 commit
5f8b69642d18e1与两个前置 stable-dep0454a604b98a9、dbd542a8fac7的 commit message
补丁目的
修复 sched_ext 在 scx_cgroup_lock() 中错误锁顺序引发的可触发死锁,并把修复连同两个前置 stable-dep 一起回迁到 6.18-stable。
旧流程的问题
scx_cgroup_lock() 的旧顺序是:
percpu_down_write(&scx_cgroup_ops_rwsem);
cgroup_lock();
这会和 kernfs 形成 ABBA 闭环:
- scx enable 路径持
cgroup_lock(),等待 rmdir 释放cgroup_mutex - rmdir 在
kernfs_drain()中等待cpu.weight写者的 active 引用 cpu.weight写者在scx_group_set_weight()里等待 rwsem 上排在前面的 enable writer
三方互相等,整条链路僵死。
新流程
cgroup_lock();
percpu_down_write(&scx_cgroup_ops_rwsem);
理由:set_* 路径在 read 侧不取任何 cgroup 锁,所以一旦 write-lock 进入等待,它前面阻挡它的只是"必然短小且跑完就释放"的读段;不再有任何路径从 rwsem 反向依赖 cgroup_mutex。
+----------------------------+
| cgroup teardown (rmdir) |
| |
| holds cgroup_mutex |
| waits in kernfs_drain() |
+-------------+--------------+
|
v +----------------------------+
| writer (cpu.weight) |
| blocks on scx_cgroup_ops_ |
| rwsem (writer queue) |
+-------------+--------------+
^
| wakes after read sections
| (no cgroup_lock dependency)
|
+-------------+--------------+
| scx_enable() |
| takes cgroup_lock() FIRST |
| then rwsem write |<-- loop broken here
+----------------------------+
Patch 概览
主线作者 Tejun Heo 的修复(5f8b69642d18)依赖两个前置 commit;Sasha Levin 把它们重新打成 [PATCH 6.18.y 1/3]、2/3、3/3 一起投递到 stable 队列:
1/3:把scx_init_task()中p->scx.disallow的告警翻转 if/else2/3:为多 scheduler 重构 enable/disable 路径,把 scheduler 实例显式化3/3:原始的cgroup_lock()顺序修复,删除嵌套#ifdef,并落到 6.18-stable 还在用的kernel/sched/ext.c
关键实现
scx_cgroup_lock() 内部调用顺序对调:
static void scx_cgroup_lock(void)
{
cgroup_lock();
percpu_down_write(&scx_cgroup_ops_rwsem);
}
static void scx_cgroup_unlock(void)
{
percpu_up_write(&scx_cgroup_ops_rwsem);
cgroup_unlock();
}
2/3 把 scx_disable() 改成接受 @sch 参数,全局 scx_root 的 RCU 查找集中到 sysrq 与 BPF 卸载路径;只有当 scx_claim_exit() 成功时才排队 disable_work,避免重复触发。sysrq 处理器在没有 scheduler 加载时打印 sched_ext: BPF schedulers not loaded。
1/3 把原来的 if (!fork) ... else if (SCHED_EXT) warn 翻转为 if (unlikely(fork)) warn; else ...,fork 路径不再被 policy 屏蔽,只要 disallow 就报警。
类比
锁嵌套像进电影院:
- 错误顺序:先在检票口排队(rwsem 写锁),等到门口才领号(cgroup_lock)。一旦检票口塞死,外面的人进不来,里面的人出不去,影院经理(rmdir)也动不了。
- 修正顺序:先在门口领号(cgroup_lock),再排队进检票口。门口不堵之后,经理就能继续办事;检票口的队伍虽然长,但每一波放进去的人不会再回头去门口抢号。
set_* 路径"不取 cgroup 锁"就像"短排队的人不会跑去门口再领一次号"——这是修正顺序能真正打破闭环的关键前提。
Highlight:风险与注意点
- 锁顺序对称性:新顺序下,
set_*读侧必须继续保持"不取 cgroup 锁",否则会把环路接回来;后续任何在 read 侧引入 cgroup 操作的改动都需要重做死锁分析。 - stable 树路径差异:主线把 ext 目录拆到
kernel/sched/ext/ext.c,但 6.18-stable 还是老的kernel/sched/ext.c,3/3显式删除了嵌套的#ifdef CONFIG_EXT_GROUP_SCHED(函数本身已经在一个#ifdef里)。 - 回迁顺序敏感:
2/3改名了scx_enable_workfn,3/3引用scx_enable/scx_disable;若不按 1/3 → 2/3 → 3/3 顺序合并,stable-dep 链就会断。Sasha 在每封 patch 顶部都加了Stable-dep-of: 5f8b69642d18。 - 可观察性:sysrq 路径从静默变成
pr_info,出问题时更容易区分"压根没加载 scx"与"reset 逻辑失败"。
版本变化
主线 v1 → Sasha 回迁过程:
- 失败尝试:直接
git cherry-pick -x 5f8b69642d18,因 6.18-stable 仍是kernel/sched/ext.c而非ext/ext.c触发冲突,Greg KH 机器人发出 FAILED 通知。 - 成功回迁:把
5f8b69642d18(顺序对调)+0454a604b98a9(warning flip)+dbd542a8fac7(enable/disable 重构)三段依次 cherry-pick,并在ext.c里去掉嵌套#ifdef。 - 函数/注释同步:
scx_enable_workfn改为scx_root_enable_workfn,scx_disable()增加@sch参数,与主线 v2 行为对齐。
一句话总结
把 cgroup_lock() 提到 rwsem 写锁之前,断开 enable 与 rmdir 以及 cpu.weight 三方死锁闭环,并把这次修复连同两个 stable-dep 前置一起按 6.18-stable 的 kernel/sched/ext.c 目录结构重新回迁。