sched-ext discussion
[PATCH 0/2] sched_ext: sync compat.bpf.h peek/reenqueue fixes from sched-ext/scx
LLM 分析
sched_ext:compat.bpf.h peek/reenqueue 修复同步
系列概况
- 标题:
[PATCH 0/2] sched_ext: sync compat.bpf.h peek/reenqueue fixes from sched-ext/scx - 作者: Changwoo Min changwoo@igalia.com(Patch 1 实际作者 Gavin Guo gavinguo@igalia.com)
- 版本: v1(无版本号),base-commit:
e5a0a3d6b05a62a2921698a60f2c84ea3da97d51 - 规模: 2 patches,全部同步同一文件
- 修改文件:
tools/sched_ext/include/scx/compat.bpf.h - 代码统计:
1 file changed, 37 insertions(+), 11 deletions(-) - Message-ID:
20260817143126.562923-1-changwoo@igalia.com - 完整性: 完整(cover letter + 2 patches + Tejun Heo 对两 patch 各一条评审)
补丁目的
把 sched-ext/scx 仓库中 reference 实现的 compat.bpf.h 两处修复同步进内核树,让 BPF scheduler 在用户态 wrapper 层能稳定判断某个 kfunc 是否可调用、是否安全:
- Gate peek:给 lockless
scx_bpf_dsq_peek()加内核版本门(≥ 7.1.0),绕开 v7.1 之前 stale / spurious-NULL 实现 bug。 - Generic reenqueue helper:新增
scx_bpf_reenqueue_local_from_anywhere(),让任意上下文(tracepoint 等)有统一入口;v7.1 通用scx_bpf_dsq_reenq()优先,否则返回错误码。
旧流程的问题
Patch 1:peek 选路过于宽松__COMPAT_scx_bpf_dsq_peek() 只判断 bpf_ksym_exists(scx_bpf_dsq_peek),ksym 存在就直接走 lockless 实现。后果:
- v6.19/v7.0 在某些 FIFO DSQ 上
scx_bpf_dsq_peek()会错误返回 NULL; - 与
dsq->first_task相关的 peek 路径需要2f2ea7709266(v7.1)才稳定。
继续裸调用可能拿到 NULL / 过期指针并误用,引发调度错乱。
Patch 2:reenqueue 在任意上下文无统一入口
scx_bpf_reenqueue_local()现有 wrapper 内联 v1 fallback,但 v1 只能在 ops.cpu_release 调用。在 tracepoint 等上下文排空本地 DSQ 时:
- 没有 supported 的 kfunc,veristat 在不支持 v2 的内核上直接拒绝构建;
- 各 scheduler 各自重复实现"判断 + 调用"逻辑。
新流程
Patch 1:版本门 + bpf_iter 兜底
if (bpf_ksym_exists(scx_bpf_dsq_peek) &&
LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0))
return scx_bpf_dsq_peek(dsq_id);
/* 否则走 bpf_iter_scx_dsq_new/next */
Patch 2:通用 helper + 优先级链
static inline int scx_bpf_reenqueue_local_from_anywhere(void)
{
if (__COMPAT_has_generic_reenq()) { /* v7.1 dsq_reenq */
scx_bpf_dsq_reenq___compat(SCX_DSQ_LOCAL, 0);
return 0;
}
if (__COMPAT_scx_bpf_reenqueue_local_from_anywhere()) {
scx_bpf_reenqueue_local___v2___compat(); /* v6.19 v2 */
return 0;
}
return -ENOTSUP; /* ← Tejun 指出要改为 -EOPNOTSUPP */
}
每条分支单独判 ksym,分别调用——|| 折叠会被 BPF verifier 拒绝(弱符号地址按位 OR 后类型不合规)。
Patch 概览
| # | 标题 | 关键改动 |
|---|---|---|
| 1/2 | compat.bpf.h: Gate scx_bpf_dsq_peek kfunc behind kernel version 7.1.0 | 在 peek 选择条件追加 LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0) |
| 2/2 | compat.bpf.h: add scx_bpf_reenqueue_local_from_anywhere() compat helper | 提取 __COMPAT_has_generic_reenq();新增 scx_bpf_reenqueue_local_from_anywhere();scx_bpf_reenqueue_local() 也改为 generic 优先 |
关键实现
Patch 1 实质改动只 7 行:
- if (bpf_ksym_exists(scx_bpf_dsq_peek))
+ if (bpf_ksym_exists(scx_bpf_dsq_peek) &&
+ LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0))
Patch 2 把"判断 + 调用"拆成两个独立分支:
/* 上移到 v6.19 block 之上,让两个 wrapper 都能看到 */
void scx_bpf_dsq_reenq___compat(u64 dsq_id, u64 reenq_flags) __ksym __weak;
static inline bool __COMPAT_has_generic_reenq(void)
{
return bpf_ksym_exists(scx_bpf_dsq_reenq___compat);
}
并把 scx_bpf_reenqueue_local() 也改为先尝试 generic 版本。
类比
把 compat 头想成"酒店前台服务指南":
- Patch 1:锁匠工具(lockless peek)在 v7.1 之前是"哑火的锁芯",能开门但偶尔卡死。指南写明:"v7.1 之前请走传统钥匙孔(bpf_iter)"。
- Patch 2:旧版"立即重排"只在退房时段(cpu_release)提供。新增"随时叫前台"按钮(
reenqueue_local_from_anywhere):v7.1 通用调度室优先接单,v6.19 v2兜底,否则明确告诉你"今天不支持(错误码)"。
+-------------------+ +-----------------------+
| scheduler.bpf.c | -----> | compat.bpf.h (wrapper)|
+-------------------+ +-----------+-----------+
|
+--------------------------------+--------------------+
| | |
v v v
+-----------------+ +-------------------+ +----------------+
| generic reenq | | peek via lockless | | bpf_iter_scx_ |
| (v7.1 dsq_reenq)| | kfunc (gated>=7.1)| | dsq fallback |
+-----------------+ +-------------------+ +----------------+
| | |
+----------------+---------------+--------------------+
|
v
+--------------------+
| BPF verifier |
+--------------------+
Highlight:风险与注意点
- ENOTSUP vs EOPNOTSUPP:Patch 2 用了
-ENOTSUP。Tejun 指出ENOTSUP是 glibc 才有的,BPF 走asm-generic/errno.h(见common.bpf.h),只定义EOPNOTSUPP。static inline函数体会在每个 TU 都被验证,当前写法会破坏所有 scheduler 的 BPF 构建。应改为-EOPNOTSUPP,参考同文件__COMPAT_bpf_cpumask_populate()。 - Patch 1 描述失真:commit message 把两个修复都说成"stale pointer"。Tejun 澄清:71d7847cad44 在 v6.19 就和 peek API 一起合入;真正 v7.1 新增的只有 2f2ea7709266,它修的是"非空 FIFO DSQ 误返 NULL",并非 stale pointer。注释需同步改写。
- dangling
*:Patch 1 注释闭合的*/之前残留一行孤立的*,需清理。 - bitwise OR 陷阱:Patch 2 注释明确说明不能用
||合并 ksym 存在性判断——这是 BPF verifier 在 weak ksym 上的常见踩坑点。 - 同步一致性:cover letter 强调必须把这两个 fix 同步到内核树,否则用户态 scheduler 在老内核 + 新 compat.h 组合下会再次触发历史 bug。
版本变化
本系列为 v1,无前序版本。Tejun 已对两个 patch 各提一条具体 review,预计下一版出 v2:
- Patch 1:改描述 + 改注释 + 删除 dangling
*; - Patch 2:
-ENOTSUP→-EOPNOTSUPP,描述同步更新;sched-ext/scx 端 pending PR 也要打同样补丁。
与其他相关 patch 系列的关联
- 2f2ea7709266 / 71d7847cad44:上游 v7.1 / v6.19 的真正内核侧修复。本 compat patch 是用户态使用层的配套保险。
- ac863374ce4f(scx_lavd: Gate dsq_peek_task_load behind no-fast-lb):lavd 在 scheduler 侧围绕同一个 bug 做过一次规避,本次是补上通用兜底。
- sched-ext/scx 端 pending PR:Tejun 提到需要打同一份
-EOPNOTSUPP补丁,否则用户态仓库与内核仓库会再次分叉。
一句话总结
本系列把 sched-ext/scx 仓库 compat.bpf.h 的 peek 版本门与 reenqueue 通用入口两处修复同步进内核树,Tejun 已逐 patch指出描述/注释失真和 -ENOTSUP 与 BPF errno 不兼容的问题,预计下一版调整后合入。