0/2 已展开

LLM 分析

sched_ext:扩展 prolog 迁移探测以覆盖 cid-form 调度器

系列概况

  • 标题:[PATCH] tools/sched_ext: Extend the prolog migration probe to cid-form schedulers
  • 作者:David Carlier devnexen@gmail.com
  • 版本:单封 PATCH(无 v 编号),由 maintainer 转为走 scx 仓库流程
  • 规模:2 文件变更,+32 / -24
  • 修改文件tools/sched_ext/include/scx/common.bpf.htools/sched_ext/include/scx/compat.h
  • 代码统计common.bpf.h 53 行变更;compat.h 3 行新增(一个 __SCX_OPS_OPEN 宏片段)
  • Message-ID20260829122035.58018-1-devnexen@gmail.com(回复:d6cfdbdd795ef5b4c546fa8eddc6ad23@kernel.org
  • 完整性:单封 PATCH + 一封 Tejun Heo 回复(建议转到 scx 仓库,并指出默认值的翻转已在 888a907d84b5 合入)

补丁目的

sched_ext 的 tools 侧头文件里有一个静态标志 __scx_prolog_disables_migration,用来告诉 is_migration_disabled() 当前 task 在非睡眠 BPF prolog (__bpf_prog_enter) 之后是否真的处于禁止迁移状态。原来这个标志只通过一个 fentry probe 挂在 bpf_scx_reg() 上。

但自从 v7.2 之后,kernel 新增了 cid-form 调度器,对应 bpf_sched_ext_ops_cid,它的 .reg 回调指向另一个函数 bpf_scx_reg_cid(),于是原 probe 永远不会触发——标志位就只能保留编译期默认值。这会让 is_migration_disabled(current) 给出错误答案,进而导致本应可以跨 CPU 分派的任务被错误地限制在本地队列上。

补丁要做两件事:

  1. bpf_scx_reg_cid() 同样挂一个 probe;
  2. __scx_prolog_disables_migration 的默认值从 true(保守)改为 false,让未挂 probe 的 loader 行为与注释描述的 safe 方向一致。

旧流程的问题

        struct_ops link created |
                v
        +-------------------+
        |   bpf_scx_reg()   |  <----- cpu-form: enters here
        +-------------------+
                |
        fentry probe fires
                |
                v
  __scx_prolog_disables_migration = true
 (default = true, kept forever)

 cid-form path:
    bpf_sched_ext_ops_cid.reg -> bpf_scx_reg_cid()
    (a SEPARATE function, no probe attached)
    -> flag stays at compile-time default
 -> is_migration_disabled(current) returns stale value

旧代码假设所有调度器都从 bpf_scx_reg() 入口经过。cid-form 调度器走的是 bpf_scx_reg_cid(),所以 probe 不触发;默认值 true 让所有未触发 probe 的路径都"看起来"已经禁迁移,与注释里说的 safe 方向(over-reporting)刚好相反——准确地说,原作者用 true 兜底是为了避免 under-reporting 引发的崩溃,但代价是把 cid-form 路径悄悄错误地标记成"已禁迁移"。

新流程

        struct_ops link created
                |
        +-------+-------+
        |               |
        v               v
  bpf_scx_reg()   bpf_scx_reg_cid()   (cid-form, since v7.2)
        |               |
  fentry probe    ?fentry probe
        |               |
        +-------+-------+
                |
                v
   __scx_record_prolog_migration()
                |
                v
   __scx_prolog_disables_migration = (md > 0)

  Loader that never attaches any probe:
    default flag = false
    -> is_migration_disabled(current) returns false
    -> over-reporting side -> safe (matches comment)

新代码把两个 probe 共用同一个 body __scx_record_prolog_migration(),避免逻辑漂移。cid-form probe 用 ?fentry/... 形式,让 libbpf 在找不到符号时跳过 attach;__SCX_OPS_OPEN() 通过 __COMPAT_has_ksym("bpf_scx_reg_cid") 决定是否启用该 program 的 autoload。

关键实现

common.bpf.h 的核心变化:

/* body shared by both probes */
static __always_inline void __scx_record_prolog_migration(void)
{
    if (bpf_core_field_exists(
            ((struct task_struct *)0)->migration_disabled)) {
        const struct task_struct *p = bpf_get_current_task_btf();
        __scx_prolog_disables_migration = md > 0;
    }
}

SEC("fentry/bpf_scx_reg") __weak
int scx_lib_init_probe(void *ctx)
{
    __scx_record_prolog_migration();
    return 0;
}

SEC("?fentry/bpf_scx_reg_cid") __weak
int scx_lib_init_probe_cid(void *ctx)
{
    __scx_record_prolog_migration();
    return 0;
}

注意 __weak:fentry program 自身不能内联,必须保留符号;但即便如此,编译器仍允许该函数被复制到多个翻译单元里(这里是 common.bpf.h,被多个 BPF skeleton 共享),所以通过 __weak 避免链接冲突。__scx_prolog_disables_migration 同样声明为 __weak

compat.h 里通过 __SCX_OPS_OPEN 宏启用 cid probe 的 autoload:

if (__COMPAT_has_ksym("bpf_scx_reg_cid"))
    bpf_program__set_autoload(
        (__oskel)->progs.scx_lib_init_probe_cid, true);

默认值翻转:

- bool __scx_prolog_disables_migration __weak = true;
+ bool __scx_prolog_disables_migration __weak;

注释里特意强调:prolog 至多给 migration_disabled 加 1;如果读到 >1,说明上游 bpf_struct_ops_link_create -> bpf_scx_reg{,_cid} 路径里有人在 prolog 之前禁了迁移,结果不可信——这条警告留给上游路径审计。

类比

bpf_scx_reg()bpf_scx_reg_cid() 想象成两扇不同的大门:

  • bpf_scx_reg() 是 cpu-form 顾客进商场走的那扇正门,门口保安(probe)会在每位顾客胸口贴一张"本层禁换"标签;
  • bpf_scx_reg_cid() 是 cid-form 顾客走的后门,原来根本没有保安,所以这些顾客出来时都没有标签;
  • is_migration_disabled() 就是商场广播:看到标签就广播"请勿换层",没标签就广播"可换层"。

旧实现里后门顾客默认被贴了"本层禁换"标签(保守,但与注释里说的 safe 方向相反),其实是不准确的。新补丁在后门也安了保安,并要求后门没保安时统一贴"未贴标签"——这样广播才能正确决定该不该让顾客换层,避免把后门顾客错误地限制在本层。

Highlight:风险与注意点

  • probe attach 失败的 fallback:把默认值改为 false 后,任何不挂 probe 的 loader 都会走 over-reporting 安全路径;如果未来 cid-form probe 在某些 libbpf 版本上 ?fentry 解析失败,需要在 loader 端验证 autoload 实际成功。
  • migration_disabled 累积问题:注释提到 "adds at most 1",但代码判断是 md > 0。如果在 bpf_struct_ops_link_create -> bpf_scx_reg{,_cid} 路径里有人先 migrate_disable(),会出现 md > 1,此时整个标志不可信——这条警告需要后续真的有人踩到再决定是否需要 abort 而不是继续用。
  • Tejun 已经把默认值翻转提到 scx 仓库:commit 888a907d84b5 已经处理默认值变更,作者应当把整个 patch 重新基于 scx 仓库发送,避免在 kernel 镜像上重复提交。
  • SEC("?fentry/...") 行为依赖 libbpf 版本:旧版 libbpf 可能忽略前缀 ? 而直接尝试 attach 失败,需要确认仓库 CI 用的是较新版本。

一句话总结

为 sched_ext 工具头文件中的 prolog 迁移探测补上 cid-form 调度器的入口 bpf_scx_reg_cid(),并将默认标志翻为 false 使其在未挂 probe 时落到注释声明的 safe 方向;maintainer 要求改走 scx 仓库提交。