0/1 已展开

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-ID20260813045931.8691-1-devnexen@gmail.com
完整性仅 bug 报告,未附带 patch

补丁目的

作者报告 sched_ext 在 core scheduling 场景下的一个 oops 路径:scx_process_sync_ecaps() 在调度 ops.sub_ecaps_updated() 之前把 dispatch context (dspc) 指向 目标 CPUpcpu->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)