sched-ext discussion
[BUG] sched_ext: ops.sub_ecaps_updated() dispatch context is set up on the wrong CPU under core scheduling
LLM 分析
sched_ext:sub_ecaps_updated() 在 core scheduling 下被错误地建立在错误的 CPU 上
系列概况
| 字段 | 内容 |
|---|---|
| 标题 | [BUG] sched_ext: ops.sub_ecaps_updated() dispatch context is set up on the wrong CPU under core scheduling |
| 作者 | David Carlier |
| 版本 | 无(讨论帖,未附 patch) |
| 规模 | 单封邮件 |
| 修改文件 | 未提供 |
| 代码统计 | 不适用 |
| Message-ID | 20260813045931.8691-1-devnexen@gmail.com |
| 完整性 | 仅 bug 报告,未附带 patch |
补丁目的
作者报告 sched_ext 在 core scheduling 场景下的一个 oops 路径:scx_process_sync_ecaps() 在调度 ops.sub_ecaps_updated() 之前把 dispatch context (dspc) 指向 目标 CPU 的 pcpu->dsp_ctx,但 sub_ecaps_updated() 实际是在某个 执行 CPU(sibling)上运行的。该执行 CPU 上的 dspc->rq 若还是 NULL,回调里调用 dispatch kfunc(如 scx_bpf_dsq_move_to_local())就会走进 cpu_of(NULL) 触发 oops。该问题与 commit 3dd52416e44a 修过的 this_rq() 假设属于同一类 bug,但 scx_process_sync_ecaps() 是被遗漏的实例。
旧流程的问题
scx_process_sync_ecaps()中:
这里struct scx_dsp_ctx *dspc = &pcpu->dsp_ctx; dspc->rq = rq;pcpu是从 llist 节点取到的目标 CPU 的 context。dispatch_core_pick()引入balance_one()处理 sibling rq 之前,目标 CPU 与 执行 CPU 总是同一个,所以两种写法等价。- 引入
balance_one()后 sync 可能由 sibling 处理。pcpu(target)与this_cpu_ptr(sch->pcpu)(executing)解耦:dspc->rq被设到 target 的 rq,但后续 dispatch kfunc 看到的是 executing CPU 的 dsp_ctx(rq仍是 NULL)。 - attach 时 cap-grant 通知首次触发,执行 CPU 上还没写过该 sub 的
dsp_ctx.rq,因此rq为 NULL。
新流程
作者建议的 one-liner,与 scx_dispatch_sched() 对齐:
- struct scx_dsp_ctx *dspc = &pcpu->dsp_ctx;
+ struct scx_dsp_ctx *dspc =
+ &this_cpu_ptr(pcpu->sch->pcpu)->dsp_ctx;
并加 Fixes: b81a6c018cde ("sched_ext: Add sub_ecaps_updated() effective-cap change notifier")。
Patch 概览
本帖未附 patch(作者明确说 "Happy to send a patch if nobody is on it"),仅描述修复方向。
关键实现
- 触发条件:
dispatch_core_pick()→balance_one()让 sibling 处理 sync。 - 暴露路径:attach 时刻触发 cap-grant 通知,首次
scx_bpf_sub_dispatch()尚未在任何 CPU 上发生过。 - 失败链:
ops.sub_ecaps_updated()→ BPF 程序调用scx_bpf_dsq_move_to_local()(带SCX_KF_ALLOW_DISPATCH)→ 非空 DSQ →scx_consume_dispatch_q(sch, NULL, ...)→task_can_run_on_remote_rq()→cpu_of(NULL)→ oops。 - 隐蔽情况:若
dspc->rq残留了先前 dispatch 的 rq,"成功"消费但派发到错误的 rq,破坏 group 负载均衡语义。
类比
把 dispatch context 想成"厨房里的备餐台":
- 旧流程:厨师只在自己厨房备餐,备餐台永远属于当前厨房,"做菜的人"和"菜要端给的厨房"是同一个。
- core scheduling 引入后:另一个厨房的厨师(sibling CPU)被中央调度叫来帮忙,但备餐台留在了 target CPU。结果客人点的菜(dispatch 操作)找不到真正的灶台(
rq为 NULL),要么 oops,要么送错桌(错误 rq)。 - 修复就是把备餐台搬到"现在真正掌勺的厨房"——
this_cpu_ptr(pcpu->sch->pcpu)。
Highlight:风险与注意点
- 触发需满足:attach 时刻 + core scheduling + BPF 在
sub_ecaps_updated()内调用 dispatch kfunc。任一条件不满足都可能掩盖此 bug。 - 即便不 oops,残留 rq 也会让任务派发到错误的 sibling rq,破坏 group 负载均衡。
- 提示:所有
this_rq()/per-cpu context 的设置点都应对齐this_cpu_ptr(sch->pcpu);commit 3dd52416e44a 可能未覆盖所有 kfunc 入口,应审计类似pcpu->dsp_ctx的引用。 - 建议加 "attach 时刻 cap-grant 通知触发 dispatch kfunc" 的回归测试。
Fixes:tag 应指向 b81a6c018cde,便于 stable backport。
版本变化
无(单封讨论帖)。
一句话总结
作者报告 sched_ext 在 core scheduling 下 scx_process_sync_ecaps() 把 dispatch context 错绑到目标 CPU 的 pcpu,导致执行 CPU 的 dsp_ctx.rq 为 NULL,建议采用 scx_dispatch_sched() 同款的 this_cpu_ptr(pcpu->sch->pcpu) 一行修复。
流程图
+---------------------------------+
| sync_ecaps llist node (target) |
| pcpu->dsp_ctx.rq = rq (set) |
+---------------------------------+
|
v
+---------------------------------+
| scx_process_sync_ecaps() |
| dispatch_core_pick() picks |
| sibling to run balance_one() |
+---------------------------------+
|
+------------------+------------------+
| |
v v
+---------------------+ +---------------------+
| target CPU | | executing CPU |
| sch->pcpu | | sch->pcpu |
| .dsp_ctx.rq = rq | | .dsp_ctx.rq = NULL|
+---------------------+ +---------------------+
|
v
+-----------------------------+
| ops.sub_ecaps_updated() |
| BPF calls |
| scx_bpf_dsq_move_to_local() |
+-----------------------------+
|
v
scx_consume_dispatch_q(sch, NULL, ...)
|
v
task_can_run_on_remote_rq()
|
v
cpu_of(NULL) --> OOPS
Fix: dspc = &this_cpu_ptr(pcpu->sch->pcpu)->dsp_ctx
^--- executing CPU's per-cpu struct (matches scx_dispatch_sched)