sched-ext discussion
[PATCH sched_ext/for-7.3] tools/sched_ext: scx_pair: Convert to sched_switch TP
LLM 分析
scx_pair: 从废弃回调迁移到 sched_switch Tracepoint
系列基线信息
| 字段 | 值 |
|---|---|
| 标题 | [PATCH sched_ext/for-7.3] tools/sched_ext: scx_pair: Convert to sched_switch TP |
| 作者 | Cheng-Yang Chou |
| 版本 | 单 patch,无版本号 |
| 规模 | 1 patch |
| 首封 Message-ID | 20260719142917.34238-1-yphbchou0911@gmail.com |
| 来源 | sched-ext |
| 状态 | 已合入 sched_ext/for-7.3(Tejun Heo 确认 apply) |
明确目的
ops.cpu_acquire/release() 回调已在 commit a3f5d4822253 中废弃;加载 scx_pair 会触发废弃警告。
本 patch 将 pair_cpu_acquire / pair_cpu_release 两个回调替换为 tp_btf/sched_switch tracepoint 程序,通过边沿检测(edge-detect)复现相同的 CPU 抢占/回收语义,消除废弃警告并使 scx_pair 与新 API 对齐。
遍历代码
1. 新增辅助函数
pair_task_is_highpri(p):用p->prio < MAX_RT_PRIO判断任务是否高于 SCX(rt/dl 范围)。选prio而非policy,因为rt_mutex_setprio()做 PI 提升时只改 prio 不改 policy——用 policy 会双向误判。pair_cpu_acquire_locked()/pair_cpu_release_locked():把原来散在回调里的 lock+mask 操作提取成带_locked后缀的辅助函数,收拢锁内逻辑,减少 tracepoint 路径的代码量。
2. sched_switch tracepoint 主体
SEC("tp_btf/sched_switch")
int BPF_PROG(pair_sched_switch, bool preempt,
struct task_struct *prev, struct task_struct *next,
unsigned int prev_state)
核心判断逻辑:
- release 条件:
next是高优先级任务 +prev是非高优先级任务(即 SCX 任务)+ 当前 CPU 的 preempted_mask 位尚未置位(首次抢占)。→ 标记 preempted_mask、清 active_mask、置 draining、kick pair CPU 带SCX_KICK_WAIT。 - acquire 条件:
next不是高优先级任务(CPU 回到 SCX/空闲)+ 当前 CPU 的 preempted_mask 位已置位。→ 清 preempted_mask、kick pair CPU(如果 pair 没也被抢占)。 - idle→高优先级直跳不算 release:CPU 上没有 SCX 任务在跑,无需 drain;kick
SCX_KICK_WAIT会让 pair CPU 无意义地等 rt burst。 - 无状态变化则提前返回:tracepoint 在每次上下文切换都触发,常见情况(无 transition)在取锁前就过滤掉了——per-CPU preempted_mask 位只由本 CPU 写,无锁读取精确安全。
3. 删除旧回调
从 scx_pair_ops 结构体中移除 .cpu_acquire 和 .cpu_release,彻底消掉废弃接口的使用。
ASCII 流程图
sched_switch tracepoint (every ctx switch)
|
read preempted_mask[cpu] ( unlocked, per-CPU )
|
[next highpri?]
/ \
YES NO
| |
[prev non-highpri?] [preempted bit set?]
/ \ / \
YES NO YES NO
| | | |
RELEASE skip ACQUIRE skip
| |
mark preempted clear preempted
clear active kick pair (if pair
kick pair not also preempted)
(SCX_KICK_WAIT)
idle --> highpri direct jump:
NOT a release (no SCX task was running)
Time CPU0 (SCX task T0) CPU1 (pair of CPU0)
t0 T0 running T1 running (paired)
t1 RT task R0 preempts T0 T1 stalls (draining)
--> RELEASE: preempted=1
--> kick CPU1 WAIT
t2 R0 finishes T1 can't dispatch
--> next=idle/SCX
--> ACQUIRE: preempted=0
--> kick CPU1 PREEMPT
t3 T0 or new SCX task T1 re-dispatches
概念类比
想象一对舞伴(paired CPUs)必须同步跳舞。当舞台监督(RT scheduler)突然拉走其中一个舞伴去表演独舞,另一个舞伴得停下等(release + WAIT kick)。等独舞结束、舞伴回到舞台(acquire),通知搭档可以继续。
旧方式是舞台管理员主动喊"你的搭档被拉走了"和"搭档回来了"(回调)。新方式是舞伴自己看节目单每次换人记录(tracepoint),边沿检测搭档是否被拉走或回来了——不再依赖管理员通知,自己判断更精确,而且不会在搭档只是短暂旁观(idle→RT 直跳)时误报"被拉走"。
Highlight 突出问题
-
sched_setscheduler() 的盲区:对一个正在运行的任务改变调度类不会触发上下文切换,所以 tracepoint 只能在下一次切换时才观察到变化。在这期间 stale active_mask 位会让 pair CPU 在
try_dispatch()中等待,但该等待被下一次切换自然截止。这与旧switch_class()回调存在同样的盲区——不是新引入的问题。 -
switch-all 模式的隐含前提:
pair_task_is_highpri()只区分 rt/dl 与非 rt/dl,无法区分 fair 和 SCX——因为在 switch-all 模式下不存在 fair 任务。如果 scx_pair 以后在 partial 模式下运行,fair 任务也会抢占 SCX,但此函数不会检测到,逻辑会出错。 -
tracepoint 开销:
sched_switch在每次上下文切换都触发,虽然无状态变化的常见路径在取锁前就退出,但在高频切换场景下仍有额外 BPF 程序执行开销——需关注大规模部署时的性能影响。 -
PI 提升误判风险:如果未来内核的
rt_mutex_setprio()行为变化(例如也开始修改 policy),p->priovsp->policy的选择可能需要重新审视。
版本演进
本系列为单 patch v1,无多版本迭代。Tejun Heo 直接 apply 到 sched_ext/for-7.3。
与其他相关 patch 系列的关联
- commit
a3f5d4822253(sched_ext: Allow scx_bpf_reenqueue_local() to be called from anywhere):废弃了ops.cpu_acquire/release()回调,是本 patch 的前置动机。 - 其他 sched_ext 示例调度器(scx_qmap 等)也可能需要类似的迁移,可跟进观察。
一句话总结
scx_pair 从废弃的 cpu_acquire/release 回调迁移到 sched_switch tracepoint 边沿检测,用 p->prio 识别高优先级抢占,消除废弃警告,保持与旧回调相同的语义和盲区。