sched-ext discussion
[PATCH v2] selftests/bpf: Remove duplicate copies of the arena spinlock qnodes
LLM 分析
selftests/bpf arena spinlock:消除 qnodes 的重复拷贝
系列概况
- 标题:[PATCH v2] selftests/bpf: Remove duplicate copies of the arena spinlock qnodes
- 作者:Changwoo Min changwoo@igalia.com
- 版本:v2(仅一条 patch)
- 规模:3 files changed, 15 insertions(+), 6 deletions(-)
- 修改文件:
tools/testing/selftests/bpf/libarena/include/bpf_arena_spin_lock.htools/testing/selftests/bpf/libarena/src/common.bpf.ctools/testing/selftests/bpf/progs/arena_spin_lock.c
- 代码统计:原行内定义被改为
extern,并在两个.bpf.c中各加一份实定义加注释 - Message-ID:20260817160249.655916-1-changwoo@igalia.com
- 完整性:patch 加 patchwork-bot 应用回执(含 commit 链接
9d19ca5d0e8b),由 Kumar Kartikeya Dwivedi 接受入库
补丁目的
bpf_arena_spin_lock.h 在头文件里直接定义了一个64KB 量级的 qnodes[_Q_MAX_CPUS][_Q_MAX_NODES] 全局数组,并加 __weak 指望所有翻译单元折叠到同一实例。但 BPF 链接链路上 bpftool gen object 只合并符号、把每个输入 .addr_space.1 的字节顺序拼接,于是每个 TU 那份被丢弃的弱拷贝仍然把字节数塞进了最终对象。libarena 链入十个 TU,就白白浪费九份 64KB。本 patch 把"定义"从公共头里挪走,改成 extern 声明,要求每个真正用到的程序自己提供一份唯一定义,从根上消灭冗余。
旧流程的问题
bpf_arena_spin_lock.h
|
+-- struct arena_qnode __weak qnodes[...]; <- defined in header
|
+-- TU_A (.bpf.c) --> generates a weak instance
+-- TU_B (.bpf.c) --> generates a weak instance
+-- ...
+-- TU_J (.bpf.c) --> generates a weak instance
|
v
bpftool gen object
+-- symbol table: merged into one qnodes symbol
+-- .addr_space.1: concatenates bytes from every TU
|
v
libarena.bpf.o carries 10 copies of 64KB, 9 are dead weight
.addr_space.1 不识别弱符号去重,导致"符号表干净、字节表臃肿"的不一致。
新流程
bpf_arena_spin_lock.h
|
+-- extern struct arena_qnode __arena qnodes[...]; <- declaration only
|
+-- libarena/src/common.bpf.c <- unique definition #1
| struct arena_qnode __arena qnodes[...];
|
+-- progs/arena_spin_lock.c <- unique definition #2
struct arena_qnode __arena qnodes[...];
|
v
bpftool gen object
+-- each input contributes exactly 1 copy of bytes
|
v
libarena.bpf.o .addr_space.1 drops from 676200 to 86376 bytes
链接器把"实例化责任"显式下放给两个具体 .bpf.c,"谁用谁定义",定义就只出现一次。
Patch 概览
只一封 patch,跨越三个文件做对称改造:
header decl TU-internal definition ----------- -------------------------
bpf_arena_spin_lock.h extern qnodes[...]
libarena/src/common.bpf.c struct qnodes[...] <- for libarena
progs/arena_spin_lock.c struct qnodes[...] <- for selftest
__weak __arena __hidden 三个属性里只保留 __arena __hidden,因为 __weak 的"防重复"职责已被"只声明不定义"接替;__hidden 仍要保留,避免 BPF 程序把内部 qnodes 暴露成全局符号。
关键实现
-
头里把"定义加一段长注释"删掉,剩下一行:
extern struct arena_qnode __arena __hidden qnodes[_Q_MAX_CPUS][_Q_MAX_NODES]; -
libarena/src/common.bpf.c顶部新增注释加实定义:/* * Storage for the queue nodes declared by bpf_arena_spin_lock.h. * Each program linking the arena spinlock provides exactly one * definition, so that the array is emitted once rather than once * per translation unit. */ struct arena_qnode __arena __hidden qnodes[_Q_MAX_CPUS][_Q_MAX_NODES]; -
progs/arena_spin_lock.c紧贴int cs_count;之后写完全对称的一份定义,并提示读者"libarena 的那份在libarena/src/common.bpf.c"。 -
测试:
test_progs -t arena_spin_lock -t libarena通过。
类比
- 这相当于把"每个班级都复印一份全校通讯录"改成"教务处统一印发一份,谁要谁来领"。原本用"复印件很薄、靠弱引用标记可丢弃"做去重,听上去合理,但装订时没人去撕掉那 9 张多余的纸,于是书包一直鼓鼓囊囊;现在干脆只印一份,放在唯一负责的办公室里(
common.bpf.c和测试程序),谁的抽屉里也找不到副本。 - 也像厨房里多人都放了一瓶相同的酱油:
__weak相当于标签写着"随便扔一瓶就行",但搬箱子的人把十瓶全装上了车。新方案要求"只放一瓶",并指定哪两个人负责各管一瓶(libarena 加 selftest),车上立刻轻了几十公斤。
Highlight:风险与注意点
extern声明依赖于"链接集合里恰好有一份定义",如果将来新增第三个使用bpf_arena_spin_lock.h的.bpf.c又忘了写自己的定义,链接阶段会以"undefined symbol"形式失败,而不是悄悄再回到弱符号副本。维护者需要把"每个使用者都要补一份qnodes定义"当成一条隐性 API 约束。- BPF selftest 改的是 test 侧产物体积(
libarena.bpf.o、skel 头),不影响 vmlinux,但下游消费这些 skel 的用户也会跟着缩小生成代码,这点对pahole、bpftool链路没有兼容性问题。 - v2 仅 rebase 到 bpf-next HEAD,没有功能性改动,意味着 v1 在 reviewer 看来是 clean 的;后续若想把这条约束写进文档(注释或 docs),可以单独立项。
版本变化
- v1 到 v2:仅做 rebase,无代码改动。
一句话总结
把头文件里那份 64KB 的 qnodes 从"每个 TU 弱定义"改成"只声明、谁用谁定义",让 bpftool gen object 拼接 .addr_space.1 时不再白背 9 份冗余字节,libarena 对象与 skel 直接缩水一个数量级。