0/33 已展开

LLM 分析

sched: Make proxy execution compatible with sched_ext

系列概况

  • 标题: [PATCHSET v7 sched_ext/for-7.3] sched: Make proxy execution compatible with sched_ext
  • 作者: Andrea Righi arighi@nvidia.com(基于 John Stultz jstultz@google.com 早期工作)
  • 版本: v7(cover letter 含 v1-v6 全部前序链接)
  • 规模: 11 patch + cover letter + 21 回复 = 33 邮件
  • 修改文件: kernel/sched/{core.c,sched.h,syscalls.c}, kernel/sched/ext/{ext.c,ext.h,internal.h,sub.c,sub.h}, include/linux/sched/ext.h, init/Kconfig, tools/sched_ext/{scx_qmap.bpf.c,scx_qmap.c,scx_qmap.h}, tools/testing/selftests/sched_ext/{enq_blocked.bpf.c,enq_blocked.c,Makefile,.gitignore,config}
  • 代码统计: core.c ±27;ext.c 累计 ~±200;scx_qmap +45/-21;selftest ~900;init/Kconfig -2
  • Message-ID: 20260716132229.61603-1-arighi@nvidia.com
  • 完整性: 完整——cover + 11 patch + 多轮 maintainer/作者/Sashiko-bot 交互

补丁目的

CONFIG_SCHED_PROXY_EXEC=yCONFIG_SCHED_CLASS_EXT=y 不再构建期互斥,让发行版可发一份镜像按需启用。

proxy-exec 让 mutex waiter(donor)把自身调度上下文让给 owner,让 owner 以 donor 的优先级/时间片跑临界区。sched_ext 由 BPF 自己调度(vtime/DSQ/who-is-running),core 突然把 BPF 未 dispatch 的 owner 跑在 rq 上,会让 kfunc/helper 看到与 BPF 视角不一致的 current。任务以错误 IRQ/preempt 状态 resume,触发 sleeping-while-atomic 与 lockdep 损坏。

旧流程的问题

  • NOHZ CFS bandwidth tick 仍检 rq->curr 且要求 nr_running==1;retained FAIR donor 不在路径上 → tick 错停,runtime 未 enforce。
  • consume_remote_task() 在 DSQ lockless scan → 源 rq lock 的 handoff 窗口里,task 可能因 proxy-exec 在源 rq 上 on-CPU(task_on_cpu/migration_disabled 已变)但 holding_cpu 仍指旧 CPU。
  • set_next_task_scx() 在 donor 被选中时调 ops.running(),随后 put_prev_task_scx() 又调 ops.stopping(),但 donor 自己不实际执行 → callback 配对虚假。
  • 两特性构建期互斥。

新流程

  • 新 ops flag SCX_OPS_ENQ_BLOCKED:BPF 显式声明接管 blocked donor,需实现 ops.enqueue()
  • scx_allow_proxy_exec(p):非 EXT 一律允许;EXT 看 sch->ops.flags & SCX_OPS_ENQ_BLOCKED__schedule()task_is_blocked(prev) 不再强制 block。
  • sched_can_stop_tick() 改检 rq->donor,去掉 nr_running==1 限制。
  • consume_remote_task() 在拿到源 rq 锁后用 task_can_move_from_locked_rq() 再验;失败时 fall back 到 global DSQ(非 BPF 错误)。
  • SCX_TASK_RUN_TRACKED 标志:donor 走 proxy 时不调 running/stopping;真跑时设上、离开时清掉。
  • 调度类切换(sched_setscheduler / PI de-boost / scx_root_enable)调 sched_proxy_block_task() 把 retained donor 退回普通 block 路径。
  • core 簿记口径统一:rq->donor 是"被调度器看到的 current scheduling context";rq->curr 是"正在执行 instruction 的 task"。BPF kfunc 全读 rq->donor
  • scx_qmap -B:blocked donor 拿 fresh slice 插所在 cid 的 local DSQ 头,立即 preempt。
  • init/Kconfig 去掉 depends on !SCHED_CLASS_EXT

Patch 概览

#主题关键改动
01/11NOHZ CFS bandwidth 跟 donor__need_bw_checkrq->donor,去 nr_running==1
02/11sched_proxy_block_task()helper 把 retained donor 从 rq dequeue
03/11调度类切换 block donorscx_allow_proxy_exec / scx_prepare_setscheduler
04/11修 running/stopping 配对SCX_TASK_RUN_TRACKED
05/11修 TOCTOUtask_can_move_from_locked_rq
06/11curr|donor 拆分update_curr/task_tick/kfunc 读 donor
07/11blocked donor 迁移锁源 rq 时再判;put_prev 放 local DSQ
08/11admission 交 BPFSCX_OPS_ENQ_BLOCKED / SCX_ENQ_BLOCKED
09/11selftest enq_blocked新 BPF+userspace 测试
10/11scx_qmap -Bdemo 策略
11/11Kconfig 解互斥depends on !SCHED_CLASS_EXT

关键实现

/* Patch 03: 默认拒绝 retained EXT donor */
bool scx_allow_proxy_exec(const struct task_struct *p)
{
    return p->sched_class != &ext_sched_class;
}

/* Patch 04: SCX_TASK_RUN_TRACKED 单独配对 running/stopping */
if ((p->scx.flags & SCX_TASK_QUEUED) && !p->is_blocked) {
    if (SCX_HAS_OP(sch, running))
        SCX_CALL_OP_TASK(sch, running, rq, p);
    p->scx.flags |= SCX_TASK_RUN_TRACKED;
}

/* Patch 05: 锁源 rq 后再验能否迁移 */
if (unlikely(!task_can_move_from_locked_rq(sch, p, this_rq, false))) {
    p->scx.dsq = NULL;
    p->scx.holding_cpu = -1;
    scx_dispatch_enqueue(sch, src_rq, find_global_dsq(sch, task_cpu(p)),
                         p, enq_flags | SCX_ENQ_CLEAR_OPSS | SCX_ENQ_GDSQ_FALLBACK);
    switch_rq_lock(src_rq, this_rq);
    return false;
}

/* Patch 08: BPF 接管 donor admission */
enq_blocked = (sch->ops.flags & SCX_OPS_ENQ_BLOCKED)
           && p->is_blocked && !(enq_flags & SCX_ENQ_WAKEUP);
if (enq_blocked)
    enq_flags |= SCX_ENQ_BLOCKED;
   +------------------+     blocking on mutex     +------------------+
   |     Donor (D)    | <----------------------- |      Mutex (M)   |
   |   prio: high     |                          +------------------+
   +------------------+                                   ^
          |                          proxy execution:      | held by
          |                          donor's slice/class   |
          v                          drives the CPU        |
   +------------------+                                 +------------------+
   |  rq->donor = D    |  scheduler sees D            |    Owner (O)     |
   |  rq->curr  = O    |<-- BPF view: D               |   prio: low      |
   +------------------+                                 +------------------+
          ^                                                   |
          | preempted by                                      | runnable
          |                                                   v
   +------------------+
   |     Task T        |
   +------------------+

   D's slice / class / priority drive scheduling;
   O's instructions execute on the CPU.

类比

proxy-exec 像"借身份办事"。Donor 把银行账户(优先级/时间片)借给 Owner,让 Owner 拿 Donor 的额度去柜台办业务。sched_ext 是柜台经理,原本只认"自己叫号派发的人"。如果没看见 Donor 就看到 Owner 来取钱,会怀疑账户对不上。

SCX_OPS_ENQ_BLOCKED 是经理说:"我现在接受账户借出,请把 Donor 的号也通知我,我来决定要不要现在插队、插哪个柜台。"

rq->donor 是"柜台账面上的客户";rq->curr 是"真站在窗口的人"。经理只信 donor 这本账——Owner 借走期间 BPF 簿记仍按 donor 更新。

Highlight:风险与注意点

  • Tejun 关注 TOCTOU 检查语义:未开 proxy-exec 时 DSQ lock + holding_cpu handshake 已能阻止状态变化;新增迁移检查应仅在 proxy-exec 启用时启用并显式说明,避免误导——v8 已按此收窄。
  • Sashiko 标"潜在无限 livelock"(Patch 05):fallback 到 global DSQ 后下个 idle CPU 再次 pick 该 task,可能反复重演。需观察 idle CPU 命中率。
  • Sashiko 标"缺 TICK_DEP_BIT_SCHED 保护"(Patch 06):跨类 proxy-exec 时远端 NOHZ tick 状态可能错乱。
  • John Stultz 提 scx_pair 不设 SCX_OPS_ENQ_BLOCKED 时 donor 仍被走 wakeup 路径(Patch 08):activate_blocked_waiters() 在 owner 醒来后无条件 enqueue donor,触发 WARN_ON。修复方向是在 do_activate_blocked_waiter()scx_allow_proxy_exec()
  • John Stultz 报 scx_qmap BPF 加载 -EACCES:clang 19.1.7 + BPF arena 生成错,迁到 clang 22/23 后正常——环境问题,非代码 bug。
  • 调度类切换漏点:retained EXT donor 必须先 dequeue;漏一处(如只走 root enable、不走 sched_setscheduler)会让 donor 带旧 EXT 状态进新 EXT scheduler。
  • scx_qmap -B 刻意不公平:demo 时好看但生产部署不能照抄。

版本变化

  • v1→v7:v1 起步自 John Stultz 早期工作;Andrea 接手后逐步加入 donor/curr 拆分、SCX_TASK_RUN_TRACKED、TOCTOU 修复、selftest、scx_qmap -B。
  • v7→v8 走向(讨论中浮现):Tejun 要求 TOCTOU 修复合围在 proxy-exec 条件内;John Stultz 的 "distinguish proxy activations from wakeups" 补丁会并入其 sleeping-owner 系列,本系列独立推进。

一句话总结

让 BPF 调度器通过 SCX_OPS_ENQ_BLOCKED 显式接管 mutex-blocked donor 的调度决策,统一 rq->donor/rq->curr 簿记口径,并修补 NOHZ tick、TOCTOU 迁移、running/stopping 配对三处回归,从而把 proxy-exec 与 sched_ext 从互斥改成可同开的两个特性。