0/2 已展开

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.h
    • tools/testing/selftests/bpf/libarena/src/common.bpf.c
    • tools/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 暴露成全局符号。

关键实现

  1. 头里把"定义加一段长注释"删掉,剩下一行:

    extern struct arena_qnode __arena __hidden
        qnodes[_Q_MAX_CPUS][_Q_MAX_NODES];
    
  2. 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];
    
  3. progs/arena_spin_lock.c 紧贴 int cs_count; 之后写完全对称的一份定义,并提示读者"libarena 的那份在 libarena/src/common.bpf.c"。

  4. 测试: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 的用户也会跟着缩小生成代码,这点对 paholebpftool 链路没有兼容性问题。
  • v2 仅 rebase 到 bpf-next HEAD,没有功能性改动,意味着 v1 在 reviewer 看来是 clean 的;后续若想把这条约束写进文档(注释或 docs),可以单独立项。

版本变化

  • v1 到 v2:仅做 rebase,无代码改动。

一句话总结

把头文件里那份 64KB 的 qnodes 从"每个 TU 弱定义"改成"只声明、谁用谁定义",让 bpftool gen object 拼接 .addr_space.1 时不再白背 9 份冗余字节,libarena 对象与 skel 直接缩水一个数量级。