sched discussion
[RFC PATCH 00/20] rv: Add support for BPF monitors
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.h、include/rv/automata.h、include/rv/da_monitor.h、include/trace/events/sched.h、kernel/sched/core.c、kernel/sched/sched.h、kernel/trace/rv/{Kconfig,Makefile,rv.c,rv.h,rv_bpf.c,rv_reactors.c};工具侧tools/build/Makefile.feature、tools/build/feature/Makefile、tools/verification/{models,rv,rvgen,tests} - 代码统计:内核新增
rv_bpf.c174 行;用户态bpf_monitor.c1025 行;示例、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 程序形式按需装卸。
解决的核心问题:
- 新增 monitor 必须重编内核,门槛高
- 缺乏与 BPF 子系统对接的入口,rvgen 不能直接生成 BPF 模板
- tracefs 文件的
private_data没有生命周期保护,BPF monitor 注销后 fd 仍指向悬空指针 - DA monitor 事件只能走 tracepoint,BPF 侧没有对应机制
旧流程的问题
- rv 工具只能发现
kernel/trace/rv/rv.c链表中的 in-kernel monitor,新增 monitor 必须改 Kconfig + Makefile + 重编内核 struct rv_monitor中name/description是const char *,无法被 BPFstruct_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 概览
按作用分组,编号与原邮件对应:
| 编号 | 标题 | 作用 |
|---|---|---|
| 01 | sched: Add task enqueue/dequeue trace points | 给后续 BPF monitor 提供 sched 事件源(Nam Cao 主笔) |
| 02 | tools/rv: Skip empty pid error in selftest if command failed | selftest 容错 |
| 03 | rv: Refactor da_trace() functions to get strings internally | 把字符串转换内化,让 BPF 能复用 |
| 04 | rv: Use static arrays for rv_monitor name and description | 改定长数组,可被 struct_ops 拷贝 |
| 05 | rv: Add in-kernel support for BPF monitors | 新增 rv_bpf.c,struct_ops 接入点 |
| 06 | rv: Add rv_get_monitor_by_name() | RCU-aware 名字查找 |
| 07 | rv: Add reactors support to BPF monitors | 新增 bpf_rv_react kfunc |
| 08 | rv: Cast result of model_get_*_name() | 抑制 const char* 警告 |
| 09 | rv: Handle unregistered monitors safely in tracefs | 防悬空指针 |
| 10 | tools/build: Add a feature test for bpftool-btf | 构建期特性检测 |
| 11 | tools/rv: Move argument parsing from in_kernel to utils | 配置结构提到 utils |
| 12 | tools/rv: Export functionality for in_kernel monitors | 导出 ikm_* 给 BPF 路径复用 |
| 13 | tools/rv: Implement BPF monitor loading and tracing | BPF 加载 + ringbuf 解析(核心 1025 行) |
| 14 | tools/rv: Implement BPF monitor registration logic | rv bpf register/unregister |
| 15 | tools/rv: Copy stripped bpf_atomic.h from libarena | 借用 libarena 的原子宏 |
| 16 | tools/rv: Add BPF monitors | 引入两个示例 nohz / tqueue(dot + c) |
| 17 | tools/rv: Define CONFIG_X86_64 statically for BPF monitors | 绕过 Kconfig 读取 |
| 18 | verification/rvgen: Add support for BPF monitors | rvgen 加 -b 生成 BPF 模板 |
| 19 | tools/rv: Add selftest for rv bpf | 端到端 selftest |
| 20 | verification/rvgen: Add selftest for rvgen -b | rvgen -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:风险与注意点
- ABA 风险(Patch 9):sashiko AI 指出
rv_is_monitor_registered用指针相等判定,monitor 注销后立即注册新 monitor 占用同地址时,旧 fd 可能误操作新 monitor。需要配合 generation counter 或 RCU grace period 后再释放。 - tracepoint 触发时机过早(Patch 1):sashiko AI 指出
sched_enqueue_tp放在enqueue_task()入口,而p->sched_class->enqueue_task()还没执行,vruntime / deadline /on_rq仍是旧值。应当挪到 sched_class 操作之后。 - 历史 NULL deref:
rv_is_container_monitor()在 monitor 是链表最后一项时list_next_entry返回无效节点;pre-existing,本系列间接相关。 - const char* 与 BPF 不兼容:Patch 4/8 用定长数组和
(char *)强制转换绕过;长期方案应在设计上把 state/event 名称固化。 - CONFIG_X86_64 硬编码(Patch 17):依赖 Kconfig 读取不可靠,由 rv 工具按本机架构写死;未来支持其他架构需重新评估。
- 范围限制:目前仅支持 DA monitor,LTL 等没接入;BPF monitor 不能生成 tracepoint,只能从 ringbuffer 取事件。
- patch 数量大:20 补丁、约 2770 行,审查负担重;Rostedt / Starovoitov / Glozar 等已经来回 13 封,v2 必出。
- 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 字符串语义不匹配。