sched discussion
[PATCH 7.1 001/271] mount: honour SB_NOUSER in the new mount API
LLM 分析
Linux 7.1.9-rc1 stable review:sched_ext / sched-psi / rqspinlock 多个 scheduler 补丁的合订
系列概况
- 标题:
[PATCH 7.1 000/271] 7.1.9-rc1 review - 作者:Greg Kroah-Hartman(gregkh@linuxfoundation.org),stable 维护者
- 目标内核:Linux 7.1 stable 分支,候选版本 7.1.9-rc1
- 规模:本系列共 271封邮件,作为
[PATCH 7.1 000/271]的回复一次性发出;thread 末尾另有 13 封 Tested-by/Ack 类回复,总计286 封 - 修改文件:横跨众多子系统(sched、sched_ext、psi、scsi、net、ovpn、nfs、xfs、arm64 dts、drm、usb、alsa、bpf、tracing、ring-buffer、mm …),scheduler 直接相关补丁8 枚
- 代码统计:难以在邮件正文中获得精确 diffstat,单 patch 体量多在 1–40 行
- Message-ID(封顶):
20260817132536.752504388@linuxfoundation.org - 完整性:完整,271 枚 patch + 13 封 acked-by/tested-by 回复均已收录
本 thread 来自
stable频道(Greg KH 的稳定分支公告),虽标记sched,但实质是覆盖整个内核的稳定版本合订,并非纯 scheduler 设计讨论;下文聚焦 scheduler 子树,并简要概述其他 patch 的多样性。
补丁目的
7.1.9-rc1 是一次例行的 stable 回滚聚合,覆盖三类 scheduler 相关问题:
- sched_ext 子树(patch 013/014/015/022/260):修复
cgroup sub-sched enable/disable在 race / 错误状态下的拒绝逻辑与生命周期,让非-ext任务(SCX_OPS_SWITCH_PARTIAL下可能存在)不会被错误地调用ops.enable(),并修正disallow标志位在非 enable 路径上的策略,避免 silently-rewritten policy。 - sched/psi(patch 258/259):把
psimonkthread 的创建移出cgroup_mutex临界区,并保证rtpoll_timer在 cgroup 销毁时被及时关闭,避免 uaf / lockdep误报。 - rqspinlock(patch 115):在 deadlock-保留队列场景下重置 tail,避免后续 queue waiter 永远卡死。
旧流程的问题
ops.init_task()在 root enable 之外(例如 sub-sched re-init)通过disallow强行把 SCHED_EXT 改回 SCHED_NORMAL:没有 reader 真的去读这个 policy,结果任务继续在 ext class 上以"已被重写"的策略运行,nr_rejected计数与实际行为不一致。- sub-sched 在
scx_link_sched()之前 enable失败 →scx_sub_disable()仍执行 task-walk/cgroup复位,与 root disable 的drain_descendants()失序,出现WARN_ON(!scx_task_on_sched)与 re-home 出错任务的 UAF。 numa_select_cpu()在 task affinity 迁移期间可能选择task_cpu落在cpus_ptr之外,导致scx_pick_*_cpu_node()返回 -EBUSY,self-test 退出。- psi
psimonkthread 在cgroup_mutex下创建,可能与 cgroup 文件系统交互形成循环依赖;rtpoll_timer在 cgroup 析构时未关闭,留下 dangling timer。 - rqspinlock 在 deadlock 检测后保留队列但未重置 tail,后续 waiter 在尾节点上 spin永远等不到 owner。
新流程
+----------------------------------+ +----------------------------+
| ops.init_task() == fork branch | | ops.init_task() == re-init |
| -> scx_error("...during fork") | | -> SCX_ENABLING only, |
+----------------------------------+ | otherwise scx_error |
+----------------------------+
scx_sub_disable(sch):
if list_empty(&sch->sibling) /* never linked, skip walk */
goto dump;
/* cgroup reset + task walk + membership WARN stay valid */
scx_enable_task(parent, p): /* sub enable loop */
if (p->sched_class == &ext_sched_class)
scx_enable_task(parent, p); /* non-ext stays READY */
Patch 概览
| # | Subsystem | 关键变更 |
|---|---|---|
| 013 | sched_ext | disallow 仅在 SCX_ENABLING 路径上接受,文档中错误的 debugfs 路径修正 |
| 014 | sched_ext | scx_sub_disable() 对 sibling 为空的 sch 直接走 dump标签 |
| 015 | sched_ext | sub enable/disable 循环中 scx_enable_task 加 class判定 |
| 022 | selftests/sched_ext | numa_select_cpu 在 affinity 变化时回退 prev_cpu |
| 115 | rqspinlock | deadlock-保留路径重置 tail |
| 258 | sched/psi | psi_cgroup_free() 关掉 rtpoll_timer |
| 259 | sched/psi | psimon kthread 移到 cgroup_mutex 外创建 |
| 260 | sched_ext | scx_cgroup_lock() 先拿 cgroup_lock(),再 percpu_down_write(&scx_fork_rwsem) |
关键实现
/* patch 013:仅在 enable 路径下允许 init_task() 拒绝 SCX */
} else if (unlikely(scx_enable_state() != SCX_ENABLING)) {
scx_error(sch,
"ops.init_task() set task->scx.disallow for %s[%d %s outside the enable path",
p->comm, p->pid);
} else if (unlikely(fork)) {
scx_error(sch,
"ops.init_task() set task->scx.disallow for %s[%d] during fork",
p->comm, p->pid);
}
/* patch 014:从未链入的 sub-sched跳过 walk */
if (list_empty(&sch->sibling))
goto dump;
/* ... cgroup reset + task walk ...
* By the time control reaches here, all linked descendant
* schedulers should have been disabled.
*/
/* patch 260:锁顺序——cgroup_lock 在外,fork_rwsem 在内 */
void scx_cgroup_lock(struct cgroup_subsys_state *css_css)
{
percpu_down_write(&scx_fork_rwsem);
cgroup_lock(); /* 反过来即 patch 260 的修法 */
}
ASCII流程图
userland kernel data path
-------- ------ ---------
| | |
| OVPN_CMD_PEER_SET (REMOTE_IP) | |
|------------------------------------>| |
| | ovpn_peer_reset_sockaddr() |
| | ovpn_peer_hash_vpn_ip() [old] |
| | ovpn_peer_hash_transp_addr() [new] |
| |-------------------------------------->|
| | bind->remote (IPv6 scope_id)|
| | |
| | ovpn_peer_get_by_transp_addr() |
| |<-------------------------------------| RX
| | key: family + addr + port (no scope) |
| | -> lookup bucket |
| | |
| __ovpn_peer_hash_transp_addr | |
| hlist_nulls_del_init_rcu() | |
| hlist_nulls_add_head_rcu() |-------------------------------------->|
| | bucket == lookup bucket OK |
cgroup hierarchy
+-----------+
| root |
+-----+-----+
|
+-----------+-----------+
| |
+----+-----+ +-----+----+
| sub-A | | sub-B |
| linked | | never |
| (sibling)| | linked |
+----+-----+ +-----+----+
| |
drain_descendants scx_sub_disable()
waits for sub-B sibling empty -> dump (no walk, no re-home)
ops.init_task() state machine
---------------------------- fork = true ? --yes--> scx_error("during fork")
|
no |
scx_enable_state() == SCX_ENABLING ? --no--> scx_error("outside enable path")
|
yes
|
accept disallow, revert policy to SCHED_NORMAL
类比
可以把 sched_ext 想象成一家俱乐部门口的"VIP通道"。ops.init_task() 就像门口的安检员,他有权把某个客人(task)从 VIP 名单里划掉(设置 disallow)。
- 旧逻辑:安检员不管是谁来了都能改名单(fork路径、sub-sched 重新初始化路径都允许),结果名单被改了,但内部排班系统没有读这个变更,VIP 客人继续进场。
- patch 013/014/015 相当于:只有"营业开始前的早班"或"已上锁的会议室"才能动名单;其他时段改名单,安检员必须上报;而且只是"挂名"但还没真正上线的 VIP 包厢,重新分配时不真的去清场。
- patch 022(NUMA 测试)相当于:客人睡着了被挪到别的楼层,醒来时系统允许安检员按"上次房间"继续处理,而不是粗暴拒绝。
- rqspinlock patch 115 类似停车场闸机检测到死锁后保留队列,但需要把"队尾指针"清零,否则下一辆车永远以为前面还有人。
Highlight:风险与注意点
- scx_cgroup_lock 锁顺序变更(patch 260):从
cgroup_lock() → percpu_down_write()改成相反顺序,会与已经按旧顺序拿锁的代码路径产生锁顺序反转;升级前应确认没有调用方在持cgroup_lock的同时反向触发scx_cgroup_lock,否则会触发 lockdep splat。 - sched_ext 拒绝路径(patch 013):旧代码把 task 改回 SCHED_NORMAL 后不报错,新代码会触发
scx_error()调度器自杀,BPF 子调度器作者需要重新审视.init_task实现,否则可能在新内核上"突然挂掉"。 - numa test(patch 022):判定
task_cpu是否在cpus_ptr之外依赖的是"affinity 改变且未重新选 runqueue"的窗口,对超低延迟负载可能仍存在 -EBUSY,需要持续观察。 - psi psimon 移出 cgroup_mutex(patch 259):kthread 创建路径变更后,cgroup 卸载路径若还在等 psimon,可能出现新的依赖关系,需要重新跑 cgroup stress test。
- stable 回滚风险:本系列 271 枚 patch 中部分来自多版本 queued fixes(如 sched_ext 一连串),与其他 stable patch 叠加可能造成 bisect 跳点;建议在测试报告中按子系统分块标注是否覆盖。
- ovpn 相关 patch(025–034):不在 scheduler 子树,但 thread 内大量修补了 netns/sk_user_data/RCU 与哈希 bucket 同步问题,可与 scheduler 的 RCU 使用模式横向参考。
版本变化
7.1.9-rc1 是 7.1 stable 分支的一个累积 release,无独立 vN→vN+1 的拆分;不过 sched_ext 相关 patch 在主线已经过若干轮迭代:
- 013 在上游 commit
477869b...,属 root-vs-sub启停生命周期 bug 的最后一公里。 - 014/015 来自同一 commit group(
8c13364d.../5cdc9285...),先修 sibling sentinel,再修 task-class 判定。 - 022 上游
d4a00d61...,针对 selftests/numa。 - 115 rqspinlock 单独 patch,不依赖前面 patch。
- 258/259/260 是 psi/Sched_ext 与 cgroup_lock 联动的稳定版本。
一句话总结
7.1.9-rc1 是 Greg KH 发布的 stable 合订,scheduler 子树里集中加固了 sched_ext 在 sub-sched 启停、disallow 拒绝、cgroup 锁顺序上的正确性,并修复了 psi kthread 创建点与 rqspinlock deadlock 路径,是7.1 长期支持用户必须评估的稳定基线。