0/5 已展开

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 是否可调用、是否安全:

  1. Gate peek:给 lockless scx_bpf_dsq_peek() 加内核版本门(≥ 7.1.0),绕开 v7.1 之前 stale / spurious-NULL 实现 bug。
  2. 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/2compat.bpf.h: Gate scx_bpf_dsq_peek kfunc behind kernel version 7.1.0在 peek 选择条件追加 LINUX_KERNEL_VERSION >= KERNEL_VERSION(7, 1, 0)
2/2compat.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:风险与注意点

  1. ENOTSUP vs EOPNOTSUPP:Patch 2 用了 -ENOTSUP。Tejun 指出 ENOTSUP 是 glibc 才有的,BPF 走 asm-generic/errno.h(见 common.bpf.h),只定义 EOPNOTSUPPstatic inline 函数体会在每个 TU 都被验证,当前写法会破坏所有 scheduler 的 BPF 构建。应改为 -EOPNOTSUPP,参考同文件 __COMPAT_bpf_cpumask_populate()
  2. Patch 1 描述失真:commit message 把两个修复都说成"stale pointer"。Tejun 澄清:71d7847cad44 在 v6.19 就和 peek API 一起合入;真正 v7.1 新增的只有 2f2ea7709266,它修的是"非空 FIFO DSQ 误返 NULL",并非 stale pointer。注释需同步改写。
  3. dangling *:Patch 1 注释闭合的 */ 之前残留一行孤立的 *,需清理。
  4. bitwise OR 陷阱:Patch 2 注释明确说明不能用 || 合并 ksym 存在性判断——这是 BPF verifier 在 weak ksym 上的常见踩坑点。
  5. 同步一致性: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 不兼容的问题,预计下一版调整后合入。