0/5 已展开

LLM 分析

sched_ext 子调度器 kobject 删除顺序修复

系列概况

  • 标题:[PATCH] sched_ext: Delete sub-scheduler kobjects before releasing scx_enable_mutex
  • 作者:Qiurong Fang fangqiurong@kylinos.cn
  • 版本:v1(单 patch)
  • 规模:1 file changed, 6 insertions(+), 6 deletions(-)
  • 修改文件:kernel/sched/ext/sub.c
  • Message-ID:20260901102240.2671888-1-fangqiurong@kylinos.cn
  • 完整性:完整;包含 diff、Fixes: ebeca1f930ea、Signed-off-by

补丁目的

sched_ext 的 cgroup 子调度器(sub-scheduler)在 disable 路径上,释放 scx_enable_mutex删除 sysfs 的 sub_ksetsch->kobj;而 root 路径正好相反。

这个顺序错位会让并发的 enable 在同一 cgroup 上做 kobject_add("sub-%llu") 时撞上还没拆掉的同名 kobject,返回 -EEXIST。此时新的调度器已经走完校验、绑定、甚至 sub_detach,却被"看起来很健康"的状态机判定为 attach 失败,被自己 ops.exit() 拆掉,而 attach 系统调用返回 0,userspace 没有任何等待手段。

旧流程的问题

  • 释放锁时 cgroup 已经回到 parent,旧 scheduler 也已经 scx_unlink_sched
  • 唯一残留的"可见状态"就是 sysfs 上 sub-%llu 这个名字。
  • 新调度器抢到锁、find_parent_sched() 通过、create/validate/link 全通过,死在 scx_sched_sysfs_add()
  • attach() 返回 0,但 ops.exit() 自杀,错误表现对用户态完全不可观测。

新流程

kobject_del() 移到 mutex_unlock() 之前,与 root 路径保持一致。锁内先摘名字,再放锁;后续 enable 抢到锁时名字已经消失,不会撞 -EEXIST

关键实现

diff 修改 scx_sub_disable(),将以下 6 行:

if (sch->sub_kset)
    kobject_del(&sch->sub_kset->kobj);
/* not added if enable failed before scx_sched_sysfs_add() */
if (sch->kobj.state_in_sysfs)
    kobject_del(&sch->kobj);

mutex_unlock(&scx_enable_mutex) 之后挪到 scx_unlink_sched(sch) 之后、mutex_unlock 之前。净代码量没变,只是位置换了,函数出口位置仍保留 WRITE_ONCE(sch->dead, true)synchronize_rcu() 的语义。

修复 commit 链:Fixes: ebeca1f930ea ("sched_ext: Introduce cgroup sub-sched support")

类比

酒店前台:客人 A 退房,办事员先把房卡挂回卡槽(释放 mutex),但房间门牌还没摘。这时客人 B 同一时间来办入住:查系统空房、看证件、配房间、签单一路绿灯,到"挂新门牌"才发现旧牌还在,撞名报错,系统把 B 当作异常客赶走。其实 B 全程合规,问题在 A 的名牌没及时摘。把"摘名牌"挪到"还房卡"之前,就不会再撞名了。

Highlight:风险与注意点

  • 复现需要 ops.exit() 较慢(作者给的是 ~100ms),普通 BPF scheduler 太快,可能线上偶发但被当偶发吞掉。
  • 必须不同 struct_ops 实例并发才会触发;同一实例串行不会。
  • attach 返回 0 + 内部 ops.exit() 自杀的现象极具迷惑性,缺少用户态可等待事件是更大的可观测性问题。
  • follow-up:作者已用 40/40 命中验证,愿把 reproducer 转成 selftest,等 maintainer 拍板。

一句话总结

把 sub-scheduler 的 sysfs kobject 删除挪到 mutex_unlock() 之前,与 root 路径对齐,消除并发 enable 的"幽灵 -EEXIST"误杀。

   exit disable workfn                 new enable workfn
   -------------------                 ------------------
t0 mutex_lock(scx_enable_mutex)
t1 set_cgroup_sched(cgrp,parent)
t2 scx_unlink_sched(sch)
t3 mutex_unlock(scx_enable_mutex)     mutex_lock(scx_enable_mutex)
t4                                  find_parent_sched() ok
t5                                  create/validate/link ok
t6                                  scx_sched_sysfs_add()
t7                                  kobject_add("sub-%llu") -> -EEXIST
t8 kobject_del("sub-%llu")           err_disable: tear down
t9 synchronize_rcu()                 ops.exit() ~100ms -> death