0/48 已展开

LLM 分析

RV:增加 BPF monitor 支持(RFC 20 补丁系列)

系列概况

  • 标题:[RFC PATCH 00/20] rv: Add support for BPF monitors
  • 作者:Gabriele Monaco gmonaco@redhat.com(Patch 01 由 Nam Cao 主笔)
  • 版本:RFC v1(标题写 RFC PATCH,未带版本号),2026-08-31 首发
  • 规模:20 个补丁,46 个文件改动,2770 行新增 / 220 行删除
  • 修改文件:内核侧 include/linux/rv.hinclude/rv/automata.hinclude/rv/da_monitor.hinclude/trace/events/sched.hkernel/sched/core.ckernel/sched/sched.hkernel/trace/rv/{Kconfig,Makefile,rv.c,rv.h,rv_bpf.c,rv_reactors.c};工具侧 tools/build/Makefile.featuretools/build/feature/Makefiletools/verification/{models,rv,rvgen,tests}
  • 代码统计:内核新增 rv_bpf.c 174 行;用户态 bpf_monitor.c 1025 行;示例、selftest、模板约 1500 行
  • Message-ID:20260831090524.106845-1-gmonaco@redhat.com(首封 cover);后续 20 封 RFC PATCH + 13 封 maintainer 讨论
  • 完整性:20 补丁齐全;带 sashiko AI 自动评审;Steven Rostedt、Alexei Starovoitov、Nam Cao、Tomas Glozar 多轮回复

补丁目的

把 RV(Runtime Verification)从"只能跑内核内置 monitor"升级为"可加载 BPF monitor",让验证模型既能编进内核,也能以 BPF 程序形式按需装卸。

解决的核心问题:

  1. 新增 monitor 必须重编内核,门槛高
  2. 缺乏与 BPF 子系统对接的入口,rvgen 不能直接生成 BPF 模板
  3. tracefs 文件的 private_data 没有生命周期保护,BPF monitor 注销后 fd 仍指向悬空指针
  4. DA monitor 事件只能走 tracepoint,BPF 侧没有对应机制

旧流程的问题

  • rv 工具只能发现 kernel/trace/rv/rv.c 链表中的 in-kernel monitor,新增 monitor 必须改 Kconfig + Makefile + 重编内核
  • struct rv_monitorname / descriptionconst char *,无法被 BPF struct_ops.init_member 直接拷贝
  • rv_monitors_list 是普通 list,没有 RCU 保护,无法并发遍历
  • tracefs 文件的 private_data 没有"monitor 是否仍存在"的校验,BPF monitor 注销后仍可能被读写
  • DA monitor 的事件 trace 只能输出到 tracefs 的 per-monitor tracepoint,BPF monitor 无法生成 tracepoint
  • rvgen 只生成 in-kernel 模板,缺少 BPF monitor 形态的代码生成器

新流程

+----------------+   rv bpf register     +----------------------+
| user (rv tool) | --------------------> | libbpf open .o file  |
+----------------+                       +----------------------+
                                                  |
                                                  v
                                         +----------------------+
                                         | bpf_map__attach_     |
                                         | struct_ops(rv_xxx)   |
                                         +----------------------+
                                                  |
                                                  v
                                         +----------------------+
                                         | pin link to          |
                                         | /sys/fs/bpf/rv/      |
                                         +----------------------+
                                                  |
                                                  v
+----------------------+ rv_register_monitor    +-------------------+
| kernel rv_monitors_  | <--------------------- | bpf_rv_monitor_   |
| list (RCU)           |                         | reg()             |
+----------------------+                         +-------------------+
        |
        v
+----------------------+ ringbuf event / printk  +-------------------+
| tracefs rv/<mon>/    | ---------------------> | user rv mon       |
+----------------------+                         +-------------------+

BPF monitor 通过 struct_ops 伪装成 in-kernel monitor;事件回传则走 ringbuffer(BPF 不能生成 tracepoint)。

Patch 概览

按作用分组,编号与原邮件对应:

编号标题作用
01sched: Add task enqueue/dequeue trace points给后续 BPF monitor 提供 sched 事件源(Nam Cao 主笔)
02tools/rv: Skip empty pid error in selftest if command failedselftest 容错
03rv: Refactor da_trace() functions to get strings internally把字符串转换内化,让 BPF 能复用
04rv: Use static arrays for rv_monitor name and description改定长数组,可被 struct_ops 拷贝
05rv: Add in-kernel support for BPF monitors新增 rv_bpf.c,struct_ops 接入点
06rv: Add rv_get_monitor_by_name()RCU-aware 名字查找
07rv: Add reactors support to BPF monitors新增 bpf_rv_react kfunc
08rv: Cast result of model_get_*_name()抑制 const char* 警告
09rv: Handle unregistered monitors safely in tracefs防悬空指针
10tools/build: Add a feature test for bpftool-btf构建期特性检测
11tools/rv: Move argument parsing from in_kernel to utils配置结构提到 utils
12tools/rv: Export functionality for in_kernel monitors导出 ikm_* 给 BPF 路径复用
13tools/rv: Implement BPF monitor loading and tracingBPF 加载 + ringbuf 解析(核心 1025 行)
14tools/rv: Implement BPF monitor registration logicrv bpf register/unregister
15tools/rv: Copy stripped bpf_atomic.h from libarena借用 libarena 的原子宏
16tools/rv: Add BPF monitors引入两个示例 nohz / tqueue(dot + c)
17tools/rv: Define CONFIG_X86_64 statically for BPF monitors绕过 Kconfig 读取
18verification/rvgen: Add support for BPF monitorsrvgen 加 -b 生成 BPF 模板
19tools/rv: Add selftest for rv bpf端到端 selftest
20verification/rvgen: Add selftest for rvgen -brvgen -b 的 golden 测试

关键实现

1. struct_ops 接入(Patch 5 + 7)

static struct bpf_struct_ops bpf_rv_monitor_ops = {
    .verifier_ops = &bpf_rv_monitor_verifier_ops,
    .init         = bpf_rv_monitor_init,
    .init_member  = bpf_rv_monitor_init_member,
    .reg          = bpf_rv_monitor_reg,
    .unreg        = bpf_rv_monitor_unreg,
    .validate     = bpf_rv_monitor_validate,
    .name         = "rv_monitor",
};
late_initcall(rv_monitor_init_bpf);

bpf_rv_monitor_reg 直接调用现有 rv_register_monitor();tracefs 条目对用户透明。

2. BPF 侧 reactor(Patch 7)

__bpf_kfunc void bpf_rv_react(char *name__str, char *msg, u32 msg__sz)
{
    char safe_msg[256];
    if (msg__sz == 0)
        return;
    msg__sz = min_t(u32, msg__sz, sizeof(safe_msg));
    if (strncpy_from_kernel_nofault(safe_msg, msg, msg__sz) < 0)
        return;
    safe_msg[msg__sz - 1] = '\0';

    guard(rcu)();
    monitor = rv_get_monitor_by_name(name__str);
    if (monitor)
        rv_react(monitor, "%s", safe_msg);
}

BPF 不支持 variadic,所以要求调用者用 bpf_snprintf 预格式化。

3. BTF 自省 + ringbuf 解析(Patch 13)

bpf_monitor.c 通过 btf__find_by_name_kind(btf, "rv_trace_entry", BTF_KIND_STRUCT) 拿到事件结构字段偏移,再用 bpf_* 读取器直接从 ringbuf 拷贝字段;config.has_id 由 "id" 字段是否存在动态决定。

4. rv bpf register/unregister(Patch 14)

register:  scan /usr/share/rv/bpf_monitors/*.o
              -> open_bpf_monitor
              -> bpf_map__attach_struct_ops(rv_<mon>_kern)
              -> pin to /sys/fs/bpf/rv/rv_<mon>_kern

unregister: three-pass scan
              CHECK_ACTIVE -> UNLINK_HANDLERS -> UNLINK_STRUCT_OPS

类比

把 RV 想象成小区门卫系统。in-kernel monitor 就像焊死在岗亭里的摄像头——要换摄像头必须关电、拆墙、走线。

BPF monitor 则像即插即用的无线摄像头:只要通电、连上 Wi-Fi,就能把画面接到同一个门卫中控室。struct_ops 就是无线接入协议,bpf_rv_react 是门卫的对讲按钮——无线摄像头也能用它呼叫住户。

rvgen -b 像给摄像头厂商提供通用模具说明书,原来要画 PCB,现在改个参数就能批量生产。bpf_atomic.h 则是说明书附带的螺丝刀工具箱,复用 libarena 的现成工具,省去从头造轮子。

Highlight:风险与注意点

  1. ABA 风险(Patch 9):sashiko AI 指出 rv_is_monitor_registered 用指针相等判定,monitor 注销后立即注册新 monitor 占用同地址时,旧 fd 可能误操作新 monitor。需要配合 generation counter 或 RCU grace period 后再释放。
  2. tracepoint 触发时机过早(Patch 1):sashiko AI 指出 sched_enqueue_tp 放在 enqueue_task() 入口,而 p->sched_class->enqueue_task() 还没执行,vruntime / deadline / on_rq 仍是旧值。应当挪到 sched_class 操作之后。
  3. 历史 NULL deref:rv_is_container_monitor() 在 monitor 是链表最后一项时 list_next_entry 返回无效节点;pre-existing,本系列间接相关。
  4. const char* 与 BPF 不兼容:Patch 4/8 用定长数组和 (char *) 强制转换绕过;长期方案应在设计上把 state/event 名称固化。
  5. CONFIG_X86_64 硬编码(Patch 17):依赖 Kconfig 读取不可靠,由 rv 工具按本机架构写死;未来支持其他架构需重新评估。
  6. 范围限制:目前仅支持 DA monitor,LTL 等没接入;BPF monitor 不能生成 tracepoint,只能从 ringbuffer 取事件。
  7. patch 数量大:20 补丁、约 2770 行,审查负担重;Rostedt / Starovoitov / Glozar 等已经来回 13 封,v2 必出。
  8. BPF monitor 注册后用户态进程退出,依赖 link pin 才能保持注册;如果 pin 失败需明确清理路径,否则会出现孤儿 link。

版本变化

本帖只有 v1(RFC),无 v1 -> v2 官方 diff。从讨论主题与 sashiko AI 评审可推断 v2 需要关注:

  • sched_enqueue_tp / sched_dequeue_tp 移到 sched_class 操作之后
  • rv_is_monitor_registered 加 generation counter 解决 ABA
  • 修复 rv_is_container_monitor() 末尾节点的 NULL 处理
  • BPF monitor 的 kfunc 需要更严格的 verifier 类型检查
  • selftest 在不同 Kconfig / 编译器下保持稳定

一句话总结

本 RFC 把 RV 从"内核内置 monitor"升级为"可热加载的 BPF monitor"——通过 struct_ops 把 BPF 程序接入 tracefs、用 ringbuf 回传事件,并扩展 rvgen 与 selftest 让用户能在不重编内核的情况下部署新 monitor;关键风险是 tracepoint 触发时机、ABA 下的 fd 误用、以及 BPF 字符串语义不匹配。