sched-ext discussion
[PATCH sched_ext/for-7.4] tools/sched_ext: Skip scx_lib_init_probe() without CONFIG_FUNCTION_TRACER
LLM 分析
tools/sched_ext:无 CONFIG_FUNCTION_TRACER 时跳过 scx_lib_init_probe
系列概况
- 标题:[PATCH sched_ext/for-7.4] tools/sched_ext: Skip scx_lib_init_probe() without CONFIG_FUNCTION_TRACER
- 作者:Cheng-Yang Chou yphbchou0911@gmail.com
- 版本 / 目标分支:单封 patch,目标分支
sched_ext/for-7.4,无独立版本号 - 规模:1 file changed, 15 insertions(+), 1 deletion(-)
- 修改文件:
tools/sched_ext/include/scx/compat.h - Message-ID:20260824155051.20841-1-yphbchou0911@gmail.com
- 完整性:单封 patch + 一封 AI(Sashiko)审阅回复,无 maintainer 跟帖
- 关联来源:与 GitHub
sched-ext/scx#3758的修复互相同步
补丁目的
解决"内核没有打开 CONFIG_FUNCTION_TRACER 时,所有 sched_ext 用户态调度器加载就一起失败"的问题。
scx_lib_init_probe 是每个调度器都会通过 SCX_OPS_LOAD() / SCX_OPS_ATTACH() 自动挂上的 fentry 探针。当内核关掉 function tracer 时,fentry 没法 attach,而 skel__attach() 是 all-or-nothing 语义:只要这一个探针挂不上,整个 BPF skeleton 就一起回滚,调度器根本起不来。补丁在用户态侧加一层"是否能用 fentry"的兼容性兜底,让没有 fentry 支持的内核也能正常加载调度器。
旧流程的问题
SCX_OPS_LOAD() 宏里无条件地用 fentry 挂 scx_lib_init_probe,没有可用的运行时检测:
- 在
CONFIG_FUNCTION_TRACER=n的 vng 复现环境里,attach 阶段直接返回失败; skel__attach()整体回滚,连带整套调度器都无法 attach 到 struct_ops;- 用户层面表现为"换一个 BPF skeleton 就直接崩溃或拒绝运行",并不是单个调度器的实现 bug,而是 tools/ 层的兼容性 bug。
新流程
引入运行时检测,按可用性分支处理:
- 用
/proc/sys/kernel/ftrace_enabled这个 sysfs 文件的"存在性"作为CONFIG_FUNCTION_TRACER是否开启的廉价代理(只在编译进内核时才存在); - 不可用时立刻
bpf_program__set_autoload(scx_lib_init_probe, false),让 libbpf 跳过它; - attach 后的 struct_ops 关联循环里,把判定从"只看类型"放宽为"struct_ops 类型,或者 autoload 未关闭",确保已经禁用的程序不会再被强行关联到一个根本没有 FD 的程序。
Patch 概览
单封 patch,无 patch 系列结构。改动集中在 tools/sched_ext/include/scx/compat.h:
- 新增内联函数
__COMPAT_function_tracer_available(); SCX_OPS_LOAD宏开头插入 autoload 关闭分支;- 宏尾部
bpf_object__for_each_program循环的关联条件增加"autoload 已关闭就跳过"。
关键实现
/*
* Whether the kernel supports function tracing (CONFIG_FUNCTION_TRACER),
* needed for fentry/fexit BPF programs to load or attach.
* /proc/sys/kernel/ftrace_enabled only exists when it's compiled in,
* so its presence is a cheap proxy.
*/
static inline bool __COMPAT_function_tracer_available(void)
{
return access("/proc/sys/kernel/ftrace_enabled", F_OK) == 0;
}
SCX_OPS_LOAD() 关键片段:
#define SCX_OPS_LOAD(__skel, __ops_name, __scx_name, __uei_name) ({ \
struct bpf_program *__prog; \
UEI_SET_SIZE(__skel, __ops_name, __uei_name); \
if (!__COMPAT_function_tracer_available()) \
bpf_program__set_autoload((__skel)->progs.scx_lib_init_probe, false); \
SCX_BUG_ON(__scx_name##__load((__skel)), "Failed to load skel"); \
bpf_object__for_each_program(__prog, (__skel)->obj) { \
if (bpf_program__type(__prog) == BPF_PROG_TYPE_STRUCT_OPS || \
!bpf_program__autoload(__prog)) \
continue; \
__scx_ops_assoc_prog(__prog, (__skel)->maps.__ops_name, #__ops_name); \
} \
})
两处改动的含义:
set_autoload(false)让 libbpf 跳过 fentry 程序生成,等价于"这个探针这次不出席";!bpf_program__autoload(__prog)让循环不要去碰那些本来就不打算加载的程序,避免出现"无 FD 程序被强行塞进 ops map"的非法关联。
整体流程如下:
Old flow (CONFIG_FUNCTION_TRACER=n)
+--------------------------------------+
| SCX_OPS_LOAD() |
| call fentry attach |
| scx_lib_init_probe |
+--------------------------------------+
|
v
+--------------------------------------+
| attach fails |
+--------------------------------------+
|
v
+--------------------------------------+
| skel__attach() all-or-nothing |
| rollback |
+--------------------------------------+
|
v
+--------------------------------------+
| whole scheduler fails to load |
+--------------------------------------+New flow
+---------------------------------------------+
| SCX_OPS_LOAD() |
| check __COMPAT_function_tracer_available()|
| false: set_autoload(scx_lib_init_probe, 0)|
| true : normal fentry attach path |
+---------------------------------------------+
|
v
+---------------------------------------------+
| skel__attach() post-load struct_ops loop |
| for each program __prog: |
| if type == BPF_PROG_TYPE_STRUCT_OPS |
| || autoload == false |
| continue |
| else |
| __scx_ops_assoc_prog(...) |
+---------------------------------------------+
类比
把一支 sched_ext 调度器套件想成一支剧团,每天开演前都要先报到:
scx_lib_init_probe是开演前必到的"灯光师 fentry",剧团本来无条件要求他到场;- 但有些"小剧场"根本没有 fentry 灯光台(
CONFIG_FUNCTION_TRACER=n的内核),灯光师硬要上台只会当场翻车; - 补丁的做法是:报到时先看一眼剧场有没有灯光台,没有就告诉灯光师这次请假(
set_autoload(false));后台登记表(struct_ops 关联循环)同时增加一条规则:请假的人不强制填登记表,否则会因为找不到他而把整场戏取消。
另一个生活类比:插件化软件启动时总会按配置加载若干可选模块;这次相当于在配置里加了一个"该模块依赖的运行时特性不存在则跳过"的开关,让系统能在精简环境里继续启动。
Highlight:风险与注意点
- 硬编译期依赖(Sashiko Medium):
SCX_OPS_LOAD宏直接写死scx_lib_init_probe这个名字,调用方的 BPF skeleton 中若没有定义该探针,会在编译期就失败。任何移除或重命名该探针的调度器都需要同步修改这个宏。 - 运行时与文件存在性的脱节:
/proc/sys/kernel/ftrace_enabled文件存在只代表"编译进内核",并不能反映它是否被临时写成 0 或被 tracing 关停;属于一个粗糙但廉价的代理,不是精确的 fentry 可用性检测。 - 临时兜底而非终态:注释明确写到"一旦不再支持 6.18 之前的内核就可以删掉",上游要记得在合适的时间窗口清理这段 compat 代码。
- 同步来源漂移:本补丁 sync 自 GitHub PR
sched-ext/scx#3758,后续与 GitHub 分叉的修复要持续对齐,避免双线分叉。
版本变化
单封 patch,无 vN 到 vN+1 演进。
与其他相关 patch 系列的关联
- 直接同步自 GitHub
sched-ext/scx仓库的 PR #3758;目前主线仓库和 GitHub fork 是双线维护,保持单向 sync 即可。 - 行为上属于
tools/sched_ext兼容层的工作,目标是更老的内核版本(pre-6.18 缺 fentry 默认开启);后续若 Linux 上游把默认配置改成 always-on,此 patch 自然失效。
一句话总结
SCX_OPS_LOAD() 里加一道"内核是否支持 function tracer"的运行时检查,不可用就跳过 scx_lib_init_probe 的 autoload,并在 struct_ops 关联循环里放过已禁用的程序,从而让没有 CONFIG_FUNCTION_TRACER 的内核也能正常加载 sched_ext 调度器。