sched-ext discussion
[PATCHSET v10 sched_ext/for-7.3] sched: Make proxy execution compatible with sched_ext
LLM 分析
sched_ext:让 Proxy Execution 与 sched_ext 共存
系列概况
- 标题:
[PATCHSET v10 sched_ext/for-7.3] sched: Make proxy execution compatible with sched_ext - 作者:Andrea Righi(NVIDIA)
- 版本:v10,序列 15/15
- 规模:15 个 patch + 1 封 cover letter;前置工作来自 John Stultz
- 修改文件:
kernel/sched/core.c、fair.c、syscalls.c、sched.h;kernel/sched/ext/ext.c、sub.c、ext.h、internal.h;include/linux/sched/ext.h;init/Kconfig;tools/sched_ext/scx_qmap.{bpf.c,c,h};新文件tools/testing/selftests/sched_ext/enq_blocked.{c,bpf.c};新增 TEST 模块test_modules/scx_enq_blocked_test.ko - 代码统计:删多增多,主要 diff 集中在 sched_ext;最大 patch 是 P11(约 50 行净增)
- Message-ID:
20260728154425.1549660-1-arighi@nvidia.com - 完整性:完整,含 cover、15 patches、3 个 sashiko-bot 评审、8 个 Tejun Heo 评审、5 个作者回复
补丁目的
把 CONFIG_SCHED_PROXY_EXEC=y 与 CONFIG_SCHED_CLASS_EXT=y 由编译期互斥改成可同时开启。
Proxy 执行让等锁任务(donor)把自己的调度上下文“借给”持锁 owner,让 owner 在保持 donor runnable 的情况下推进临界区。sched_ext 走自己的 DSQ/回调,跟 core 的 rq->curr 视图天然不一致。本系列引入 SCX_OPS_ENQ_BLOCKED capability,让 BPF scheduler 显式接收被阻塞的 donor,按需调度,core 把调度上下文迁到 owner 的 rq 后再执行 owner。
旧流程的问题
- 在前 v10 之前,
SCHED_PROXY_EXEC直接depends on !SCHED_CLASS_EXT,发行版无法同时选择两者。 - 即便勉强启用,sched_ext 的
rq->curr/current视图会跟 proxy 后的执行上下文不一致:BPF helper 看到的 running 任务与 core 实际跑的任务不对齐,DSQ/vtime 状态错乱。 - proxy 把 donor 调度上下文搬到 owner rq 后,
set_task_cpu()会误报 migration-disabled warning。 - NOHZ tick 停止检查只看
rq->curr+nr_running==1,FAIR donor 受带宽限但 owner 是另一个 class,tick 会错误停止。
新流程
- BPF 调度器可选地置
SCX_OPS_ENQ_BLOCKED,op 必须实现enqueue(); - proxy 阻塞 donor 被
scx_allow_proxy_exec()放行,进入运行队列等 BPF 调度; - BPF 通过
enqueue()收下后,core 沿 mutex-owner 链迁移 donor 调度上下文到 owner rq; rq->donor持真正消耗 slice 的任务,rq->curr持正在执行 owner;- kfunc (
scx_bpf_task_running/cpu_curr/cid_curr) 一律回报 donor; ops.running/stopping会话边界不再与物理 switch 严格对齐,由SCX_TASK_RUN_TRACKED管控;- remote DSQ 传输在 src_rq 上重检查 proxy 状态,必要时丢进 reject DSQ 待 proxy settle 后再 reenq;
- 最后打开
SCHED_PROXY_EXEC与SCHED_CLASS_EXT同开的 Kconfig 闸门。
donor blocks on mutex
|
v
+-----------------------+ enqueue(SCX_ENQ_BLOCKED)
| core: retain donor | <--------------------------+
| on rq as blocked | |
+-----------------------+ |
| SCX_OPS_ENQ_BLOCKED ? |
| no -> normal block |
v |
+-----------------------+ find_proxy_task() |
| BPF enqueue() | -> walks owner chain |
| (re-)dispatch donor | |
+-----------------------+ |
| |
v |
donor rq -> owner rq (migration-disabled path) |
| |
v |
rq->donor = donor rq->curr = mutex owner |
slice consumed by donor; owner executes critical |
| |
v |
owner releases -> donor wakes -> enqueue clean ----+
Patch 概览
| # | 标题 | 主要改动 |
|---|---|---|
| 01 | sched/core: Avoid false migration warning for proxy donors | 把 blocked+proxy 排除掉 migration-disabled WARN |
| 02 | sched: Make NOHZ CFS bandwidth checks follow proxy donor | CFS bandwidth tick check 改为看 rq->donor 与 cfs.h_nr_queued |
| 03 | sched: Add helper to block retained proxy donors | 新增 sched_proxy_block_task() |
| 04 | sched: Skip class callbacks with SCHED_FLAG_KEEP_PARAMS | KEEP_PARAMS 不再触发 DEQUEUE_CLASS |
| 05 | sched: Add prepare_switch() class callback | sched_change_begin() 接收 next_class 并回调 |
| 06 | sched: Add sched_ext hooks for proxy execution | 引入三个 scx_ 钩子(空实现) |
| 07 | sched_ext: Block proxy donors across scheduler transitions | 默认拒绝;实现 prepare_switch_scx |
| 08 | sched_ext: Fix ops.running/stopping() pairing for proxy-exec | 引入 SCX_TASK_RUN_TRACKED |
| 09 | sched_ext: Generalize the reject DSQ reenqueue path | reject_dsq 解耦 sub-cap,reason 通过 reject_reason 传递 |
| 10 | sched_ext: Handle proxy-exec races in remote DSQ transfers | 新增 task_move_reject_reason + scx_reject_task |
| 11 | sched_ext: Split curr | donor references properly |
| 12 | sched_ext: Delegate proxy donor admission to BPF schedulers | 引入 SCX_OPS_ENQ_BLOCKED 主功能 |
| 13 | sched_ext: Add selftest for blocked donor admission | 新增 enq_blocked selftest + 内核模块 |
| 14 | sched_ext: scx_qmap: Add proxy execution support | -B 选项 + 被拒 donor 转发 SHARED_DSQ |
| 15 | sched: Allow enabling proxy exec with sched_ext | 删除 depends on !SCHED_CLASS_EXT |
关键实现
SCX_OPS_ENQ_BLOCKED:新 ops flag,要求 BPF 实现enqueue();交出 blocked donor 的入队决策权。SCX_ENQ_BLOCKED:传给enqueue()的 flag,提示这是持锁等待任务。SCX_TASK_REENQ_*家族:cap=4、新增 MIGRATION_DISABLED=5、PROXY=6,描述 reenqueue 原因。SCX_RQ_PROXY_REENQ:scx_rq_flags上标志,标记本 rq 有 proxy-pending 任务待 drain。scx_prepare_task_sched_change():在sched_change_begin()前清理不兼容 scheduler 的 proxy 残留。task_move_reject_reason():在持有src_rq后重检查 on_cpu/migration-disabled/current-donor 三个状态。- NOHZ tick:
__need_bw_check()只看 donor,不再要求nr_running==1;新条件cfs.h_nr_queued != 1。 - qmap
-B:blocked donor 走SCX_DSQ_LOCAL_ON|cid,slice 刷新并SCX_ENQ_PREEMPT;被 local DSQ 再次拒绝时切到SHARED_DSQ加kick_cid。
类比
把调度器想象成餐厅调度员:
- Proxy = 让 VIP 把座位借给正要去端菜的厨师:VIP 仍然排着“该轮到我了”,但允许调度员临时让厨师用 VIP 的优先级切菜。
rq->donorvsrq->curr:VIP 是“付款的顾客”,厨师是“真正端盘子的手”,调度员核账看 VIP。- Reject DSQ:是 VIP 因临时状况(路线冲突、厨房不让用)退回“候补队列”,等障碍解除再重新排队。
SCX_OPS_ENQ_BLOCKEDcapability:餐厅声明“我接受帮别人送菜的 VIP”,否则一律按普通顾客排队。
Highlight:风险与注意点
- scalar flag 含义重载:
p->scx.flags现在既要表达 queued、tracked、reject-reason,又要表达 wakeup——Sashiko 已经标记 medium 严重度的文档与实现不一致(patch 11)。 - PREEMPT_RT 仍然互斥:
!PREEMPT_RT仍未解开;RT 与 proxy-exec 的耦合在 patch 7 的回复被 Andrea 显式承认。 - PI/deboost 跨类切换:Andrea 给出 FAIR→RT PI 例子证明需要保留 proxy;Tejun 倾向 class-level capability。
- DSQ 锁次序:
scx_reject_task在src_rq上记录 reason 又要dst_rq退出,锁转换出错会丢 reason(Sashiko 标记为 high)。 - 测试覆盖:enq_blocked selftest 引入新内核模块依赖,需保证 CI 加载/清理顺序。
版本变化
- 本系列为 v10;cover letter 列出 v1–v9 全部 lore 链接;本次相对可见差异:
reject_dsq全局化、SCX_TASK_RUN_TRACKED精细化、文档同步更新。 - Andrea 在 v9→v10 把 sub-cap reject 路径抽到 core,新增
reject_reason字段。
一句话总结
通过 SCX_OPS_ENQ_BLOCKED capability 与 rq->donor / rq->curr 视角分离,让 BPF 调度器显式接管被阻塞的 proxy donor,从而解除 SCHED_PROXY_EXEC 与 SCHED_CLASS_EXT 的编译期互斥。