sched-ext discussion
[PATCH v2 0/4] selftests: shared lib.bpf.mk for building BPF progs and skeletons
LLM 分析
selftests BPF 工具链:共享 lib.bpf.mk 合并与跨 CPU memcg rstat flush 验证
系列概况
- 标题:[PATCH v2 0/4] selftests: shared lib.bpf.mk for building BPF progs and skeletons
- 作者:Ziyang Men (Meta)
- 版本:v2
- 规模:4 patches,新增约 1275 行,删除约 280 行;触及 11 个文件
- 修改文件:
- 新建:
tools/testing/selftests/lib.bpf.mk(247 行)、memcg_stat_cross_cpu.bpf.c(86 行)、memcg_stat_cross_cpu.h(27 行)、test_memcg_stat_cross_cpu.c(780 行) - 修改:
selftests/cgroup/{Makefile,.gitignore,config}、cgroup_util.{c,h}、selftests/hid/Makefile(182→92 行)、selftests/sched_ext/Makefile(153→35 行左右)
- 新建:
- 代码统计:约 +1275 / −280
- Message-ID锚点:
<20260721174833.1232771-1-ziyang.meme@gmail.com>(cover);其余为-2..-5子补丁 - 完整性:cover letter + 4 个 patch + 若干 bot报告 + 至少 3 封 maintainer/作者交互回复;v2 相比 v1 的 changelog 列在 cover letter末尾(泛化 lib.bpf.mk、可配置输出目录、新增 hid/sched_ext 转换 patch)
补丁目的
selftests 下 bpf/、sched_ext/、hid/ 三个子系统各自复制粘贴了 ~100–140 行近乎相同的 Makefile 片段:自己编译 in-tree libbpf.a、构建 host bpftool、从 vmlinux BTF 生成 vmlinux.h、把 *.bpf.c 编成 BPF 目标并生成 skeleton。随着 BPF selftest 数量增长,这种重复不可持续。本系列把这些公共工具链抽到一个可被包含的片段 tools/testing/selftests/lib.bpf.mk,并以三个使用者上链验证它:
- 新增的 cgroup memcg 正确性测试(
selftests/cgroup) - 已存在的
selftests/hid(legacyprogs/<name>.c布局) - 已存在的
selftests/sched_ext(modern*.bpf.c+.bpf.skel.h+ subskeleton)
副产物:patch 2/4 还顺手补齐 memcg BPF kfunc 跨 CPU 的正确性测试(之前 cgroup_iter_memcg 只断言大于零,且不触发 rstat flush)。
旧流程的问题
-三个 Makefile 各自抄一遍同样的工具链定义(HOSTCC/HOSTLD/CLANG、get_sys_includes、VMLINUX_BTF_PATHS、bpftool/sketch 生成规则)。
hid自己的 BPF 编译 没有开-Wall,所以源里遗留几个未用变量也没人提醒;其它两个开了-Werror。sched_ext还要手写 skel 和 subskel 三连 gen + diff 流程(linked1/2/3)。- 新写一个 BPF selftest 必须先把这 ~150 行抄一遍,开销高、易飘移。
新流程
调用方只需:
BPF_SRCS := foo.bpf.c [bar.bpf.c ...]
OVERRIDE_TARGETS := 1
include ../lib.mk
include ../lib.bpf.mk
$(OUTPUT)/foo_test: foo_test.c $(BPF_SKELS)
$(call bpf_link,$@,$<)
lib.bpf.mk 自动负责:
- 探测 vmlinux BTF路径(
O=/in-tree//sys/kernel/btf/vmlinux//boot/vmlinux-…)并bpftool btf dump出vmlinux.h - 用
clang --target=bpf -mcpu=v3编*.bpf.c→*.bpf.o - 用
bpftool gen skeleton(可选用 subskeleton)生成*.skel.h/*.subskel.h - 提供 include 路径、
bpf_link链接宏、BPF_LDLIBS(含可选-lzstd) - 可调旋钮:
BPF_PROG_EXT(默认.bpf.c)、BPF_EXTRA_HDRS/BPF_EXTRA_CFLAGS、BPF_SKEL_EXT、BPF_GEN_SUBSKEL、BPF_OBJ_DIR/BPF_SKEL_DIR - 交叉编译时把 host/target
libbpf拆到两个 build 目录,避免 host bpftool 用错编译器
Patch 概览
| Patch | 作用 | 关键点 |
|---|---|---|
| 1/4 | 新增 lib.bpf.mk 共享片段 | 247 行;暴露上述全部 knob |
| 2/4 | 新增 cgroup memcg_stat_cross_cpu 测试 | 首个 cgroup/ 下 BPF selftest;首个 lib.bpf.mk 的 cgroup 消费者 |
| 3/4 | selftests/hid/Makefile 改用片段 | 182→92 行;保留 legacy progs/*.c 布局(BPF_PROG_EXT=.c) |
| 4/4 | selftests/sched_ext/Makefile 改用片段 | 153→35 行;用 .bpf.skel.h + subskeleton;BPF_CFLAGS 全量覆盖(不加 -Werror) |
关键实现
- vmlinux BTF 自发现:依次尝试
$(O)/vmlinux、$(KBUILD_OUTPUT)/vmlinux、$(top_srcdir)/vmlinux、/sys/kernel/btf/vmlinux、/boot/vmlinux-$(uname -r),全部 miss 直接$(error ...)。 - 跨编译器一致性:先
include Makefile.arch再Makefile.include,把 lib.mk 设好的CC(带--target=arch)保存为lib_bpf_mk_saved_cc再恢复,避免 LLVM=1 cross 编译时 Makefile.include 把 CC 重置成裸clang而丢--target。 - host vs target libbpf:
BPFOBJ(target,给 PIE 测试二进制链)只在非交叉编译时与HOST_BPFOBJ(host,给 bpftool)相等;不等时另起一条规则并以HOSTCC/HOSTLD编译。 - clang system includes:通过
clang -v -E输出解析-idirafter,避免 shadow真实 include;RISC-V 额外按__riscv_xlen注入-D__riscv_xlen=与-D__BITS_PER_LONG=。 - BPF 编译规则:
-O2 --target=bpf -mcpu=$(CLANG_BPF_CPU),CLANG_BPF_CPU通过clang --target=bpf -mcpu=help探测 v3,否则回退 v2。BPF_CFLAGS 默认带-g -Wall -Werror -D__TARGET_ARCH_$(SRCARCH) -fms-extensions -Wno-microsoft-anon-tag -Wno-compare-distinct-pointer-types。 - skeleton 生成:通过
bpftool gen object <linked1.o>+gen skeleton name <basename>;subskeleton 由BPF_GEN_SUBSKEL := 1触发(仅 sched_ext 使用)。 - patch 2 测试要点:
- BPF 端用
SEC("iter.s/cgroup")走DESCENDANTS_PRE遍历整个 cgroup 子树;在seq_num == 0(根)只 flush 一次,因为css_rstat_flush已经是子树范围;每 cgroup 用bpf_get_mem_cgroup取 memcg 后调bpf_mem_cgroup_page_state/bpf_mem_cgroup_vm_events取统计并写入以kn->id为 key 的 hash map。 - 用户态用
name_to_handle_at(2)(cgroupfs handle恒为 8 字节)拿到与 BPF 端cgrp->kn->id对齐的 id,把 hash 结果与memory.stat文本比较;两次独立 round(一次 BPF、一次文件)从同一棵新子树同一起点出发。 - 验证性质:(a) flush 后每个 leaf 的 anon ≥ 该 leaf 被 charge 的量;(b) flush 后 leaf累加 == root 量。
- 新增 helper
cg_get_id()(基于name_to_handle_at)放进cgroup_util。
- BPF 端用
- patch 3/4:用
BPF_EXTRA_HDRS把各自共享 BPF header 当 extra prerequisite;hid 因源里有几个未用变量,传-Wno-unused-variable;sched_ext 因不是-Werrorclean 在 include 后整体覆盖BPF_CFLAGS(片段对BPF_CFLAGS的展开是 lazy 的,安全)。
+-----------------------+
| *.bpf.c (per-sub) |
+----------+------------+
|
v clang --target=bpf -mcpu=v3
+----------+------------+
| *.bpf.o |
+----------+------------+
|
v bpftool gen skeleton [+subskel]
+----------+------------+
| *.skel.h / *.subskel.h|
+----------+------------+
|
v bpf_link macro (BPF_LDLIBS)
+--------------+ +------+------+ +-------------+
| libbpf.a | <---- | test binary| ----> | cgroupfs |
| (in-tree) | | (selftest) | | memory.stat |
+--------------+ +-------------+ +-------------+
^
|
+--------------+ | bpftool gen skeleton -> BPF kfuncs (memcg)
| vmlinux.h | <--- vmlinux BTF dump --- bpftool +--------------+
类比
把 lib.bpf.mk 想象成小区里的 公共厨房:
- 旧流程像每户租户自己买锅、买炉子、自己通燃气,重复又危险(每家 Makefile 各抄一遍)。
- 新流程里物业统一建好中央厨房:锅、炉、抽油烟机(
libbpf、bpftool、vmlinux.h)都已就位,每户(bpf/、hid/、sched_ext/、cgroup/)只要点菜(BPF_SRCS)就行。业主还可以微调口味(BPF_EXTRA_CFLAGS、BPF_GEN_SUBSKEL)。 - patch 2 那条 memcg 测试像 物业安排的试菜:故意把同一道菜的订单分散到不同外卖员(多个 CPU 的 per-cpu rstat),最后验收员(BPF kfunc + 文件读)核对同一份账单是否一致——只有当所有外卖员把数据汇总到前台(rstat flush)才不会出现漏单。
Highlight:风险与注意点
OVERRIDE_TARGETS := 1必须先于lib.mk包含,否则 lib.mk 自带的链接规则会和bpf_link重复或冲突;cgroup Makefile 已显式重新补一条链接规则覆盖纯 C 用例。- CC 保存/恢复:在
LLVM=1 CROSS_COMPILE=arm64-这类场景下,Makefile.include会把CC重置成裸clang,必须用lib_bpf_mk_saved_cc还原,否则 hostbpftool与 targetlibbpf工具链错位(-Wstrict-aliasing=3等 gcc-only flag 会让 clang 罢工)。 - vmlinux BTF 找不到会 hard error:CI 环境若未生成
vmlinux或未挂载/sys/kernel/btf/vmlinux,整目录 build 直接挂;自测试机(VM)需要保留 BTF on。 BPF_GEN_SUBSKEL与BPF_SKEL_EXT是新引入的旋钮,未来若还想让BPF_LDLIBS跟随RELEASE/SAN_CFLAGS走,需要 v3 按 Viktor 的建议补OPT_FLAGS/EXTRA_CFLAGS透传——目前硬编码-g -O0/-g -O2。- patch 2 选址争议:JP Kobryn 建议沿用现有
cgroup_iter_memcg(在selftests/bpf下)扩展,作者答复是 v1 评审与 bpf reviewer 商量后觉得“测的是 cgroup 功能而非 BPF 本身”,并且本系列也同时提供共享 lib.bpf.mk,故放在selftests/cgroup/作为首个新消费者更合适。结论倾向:保留新文件,但建议在 changelog 里贴一段与cgroup_iter_memcg的覆盖差异表,便于未来去重。 - bot 报告:sashiko-bot 与 bpf-ci bot 分别发了 v2 抓取片段和 GitHub Actions run(
bpf/actions/runs/29908293077),需关注 CI 是否绿;目前邮件里只有 URL 与 run id,没有失败日志。 - sched_ext 整体覆盖
BPF_CFLAGS:作者故意去掉了-Werror,因为 scx 现存 BPF 代码并非全 clean;后续若上游修干净,可重新打开-Werror而不再覆盖。
版本变化
v1 → v2(作者 changelog):
- 泛化
lib.bpf.mk:加入BPF_PROG_EXT、BPF_EXTRA_HDRS/BPF_EXTRA_CFLAGS、BPF_SKEL_EXT、BPF_GEN_SUBSKEL、BPF_OBJ_DIR/BPF_SKEL_DIR,以便 hid、sched_ext 也用得起来。 - 新增 patch 3(hid)与 patch 4(sched_ext),把这两处复制粘贴的工具链改成
include ../lib.bpf.mk。
v2 review 后作者已承诺 v3 的改动(截至 2026-08-12):
- 把 target/host
libbpf和 hostbpftool的EXTRA_CFLAGS/OPT_FLAGS抽出来跟随调用方,允许make RELEASE=1走-O2、附加-DFOO。 - 评估是否进一步用
USERCFLAGS(lib.mk 定义)作为传递通道。
一句话总结
把 selftests 里散落在 bpf/、hid/、sched_ext/ 三处的 ~150 行 BPF 工具链抽成可复用片段 lib.bpf.mk,同时新增 cgroup/memcg_stat_cross_cpu 测试覆盖 memcg BPF kfunc 与 mem_cgroup_flush_stats() 在跨 CPU 场景的正确性,并清理 v2/v3 路线上的 EXTRA_CFLAGS 透传问题。