Linux 内核 RCU 压力测试实战:CONFIG_RCU_TORTURE_TEST 与 rcutorture 自动化测试工具链详解
本文以内核文档 RCU Torture Test Operation 为核心,系统讲解 Linux 内核 RCU(Read-Copy Update)压力测试工具 rcutorture 的使用方法:包括 CONFIG_RCU_TORTURE_TEST 编译选项、模块参数、测试输出的逐字段解读,以及 kvm.sh、kvm-again.sh、kvm-remote.sh 三大自动化脚本在单机、重复复现与多机分布式场景下的完整工作流。读完本文,你可以独立完成一次 RCU 实现变更的回归验证,并能读懂每一份 rcutorture 统计输出的含义。
一、CONFIG_RCU_TORTURE_TEST:可用的 RCU 压力测试模块
rcutorture 的入口是内核编译选项 CONFIG_RCU_TORTURE_TEST,该选项对所有 RCU 实现(Tree RCU、Tiny RCU、SRCU、Tasks RCU 等)均可用。定义见 kernel/rcu/Kconfig,模块源码位于 kernel/rcu/rcutorture.c。
编译后它会生成一个名为 rcutorture 的内核模块,行为特征如下:
- 测试在模块加载时自动启动,在模块卸载时自动停止;
- 测试通过
printk()周期性打印状态消息(使用KERN_ALERT级别,因此一定能在日志中看见),可用dmesg命令配合grep "torture"查看; - 所有模块参数以
rcutorture.为前缀,完整列表记录在 Documentation/admin-guide/kernel-parameters.txt 中。
1.1 常用模块参数速览
从 kernel-parameters.txt 中登记的 rcutorture.* 参数来看,测试强度和行为主要由以下参数控制(节选与本文输出示例直接相关的参数):
| 参数 | 含义 |
|---|---|
rcutorture.nreaders |
读者线程数(示例统计中为 16) |
rcutorture.nfakewriters |
“伪写者”线程数,专门用来检查 rcu_assign_pointer()/rcu_dereference() 与 rcu_barrier() 家族函数(示例中为 4) |
rcutorture.stat_interval |
统计信息打印间隔(秒,示例中为 30) |
rcutorture.shuffle_interval |
CPU 亲和性洗牌间隔,用于打散 rcutorture 线程到不同 CPU |
rcutorture.stutter |
周期性暂停/恢复 RCU 的间隔,模拟负载波动(示例中为 5) |
rcutorture.irqreader |
是否在定时器中断上下文中运行读侧代码(示例中为 1,对应统计里的 nt 计数) |
rcutorture.test_boost |
是否主动制造 RCU 优先级反转以验证优先级提升机制(示例中为 1/0) |
rcutorture.stall_cpu |
人为触发 CPU stall 警告,用于测试 stall 告警代码路径 |
rcutorture.fwd_progress |
前向进展(callback flooding)测试开关,小内存环境下建议关闭 |
除这些基础参数外,kernel-parameters.txt 中还登记了大批面向特定 RCU API 行为的条件型参数,例如 rcutorture.gp_cond=、rcutorture.gp_poll=、rcutorture.gp_exp= 等,用于精确指定“哪些 API 调用算作宽限期结束的条件”,适合在验证某一具体 RCU API 语义时使用。
二、读懂 rcutorture 的统计输出
测试运行期间(以及结束时),rcutorture 会打印如下格式的统计块:
rcu-torture:--- Start of test: nreaders=16 nfakewriters=4 stat_interval=30 verbose=0 test_no_idle_hz=1 shuffle_interval=3 stutter=5 irqreader=1 fqs_duration=0 fqs_holdoff=0 fqs_stutter=3 test_boost=1/0 test_boost_interval=7 test_boost_duration=4
rcu-torture: rtc: (null) ver: 155441 tfle: 0 rta: 155441 rtaf: 8884 rtf: 155440 rtmbe: 0 rtbe: 0 rtbke: 0 rtbre: 0 rtbf: 0 rtb: 0 nt: 3055767
rcu-torture: Reader Pipe: 727860534 34213 0 0 0 0 0 0 0 0 0
rcu-torture: Reader Batch: 727877838 17003 0 0 0 0 0 0 0 0 0
rcu-torture: Free-Block Circulation: 155440 155440 155440 155440 155440 155440 155440 155440 155440 155440 0
rcu-torture:--- End of test: SUCCESS: nreaders=16 nfakewriters=4 stat_interval=30 verbose=0 test_no_idle_hz=1 shuffle_interval=3 stutter=5 irqreader=1 fqs_duration=0 fqs_holdoff=0 fqs_stutter=3 test_boost=1/0 test_boost_interval=7 test_boost_duration=4
在大多数系统上,dmesg | grep torture: 即可提取这些信息;在更特殊的配置下,可能要用其他方式访问 printk() 输出。
首行与末行分别显示本次 rcutorture 生效的模块参数;末行给出 SUCCESS 或 FAILURE 判定——这是 rcutorture 对“RCU 是否正确运行”的自动结论。下面逐字段解释统计行中的计数器(以下均引自 torture.rst 原文定义):
2.1 计数器字段
rtc:当前对读者可见的结构体地址(十六进制)。ver:自系统启动以来,RCU 写者任务变更“读者可见结构体”的次数。tfle:非零表示“torture freelist”(存放即将放入rtc区域的结构体的空闲链表)已空。这个状态很危险——它可能骗你以为 RCU 正常工作,实际上没有。rta:从 torture freelist 分配出的结构体总数。rtaf:因链表为空而失败的分配次数。非零不罕见,但如果占rta的比例过大则说明有问题。rtf:释放回 torture freelist 的次数。rtmbe:非零表示 rcutorture 认为rcu_assign_pointer()与rcu_dereference()工作异常。应始终为零。rtbe:非零表示某个rcu_barrier()家族函数工作异常。rtbke:rcutorture 无法创建用于强制 RCU 优先级反转的实时内核线程。应始终为零。rtbre:实时线程创建成功,但无法将其设置为实时优先级 1。应始终为零。rtbf:RCU 优先级提升未能解决优先级反转的次数。rtb:rcutorture 尝试制造 RCU 优先级反转状态的次数。如果你在用test_boost模块参数测试优先级提升,这个值应当非零。nt:rcutorture 在定时器处理程序中运行 RCU 读侧代码的次数。仅当你指定了irqreader模块参数时这个值才应非零。
2.2 三个直方图
- Reader Pipe:读者看到的结构体“年龄”直方图,以宽限期(grace period)为单位。新分配的结构体年龄为 0,从读者可见性中移除后变为 1,此后每经过一个宽限期加 1,最终在经过 (RCU_TORTURE_PIPE_LEN-2) 个宽限期后被释放。如果前两项之后的任何一项非零,说明 RCU 是坏的,rcutorture 会打印错误标志串
!!!提醒你注意。 - Reader Batch:同样是结构体“年龄”直方图,但以计数器翻转(batch)而非宽限期为单位。合法的非零项同样只有前两项。之所以单列这一视角,是因为“第三项出现非零”有时更容易在 Reader Batch 中暴露,而难以在 Reader Pipe 中暴露。
- Free-Block Circulation:显示到达流水线各个阶段的 torture 结构体数量。第一项应接近分配总数,第二项接近已被移出读者视野的数量,其余各项(除最后一项外)对应穿过各轮期限期的数量。最后一项应恒为零——它只在结构体计数器被错误地多加时才会递增。
2.3 实现相关的附加信息
不同的 RCU 实现还会输出实现专属的额外信息。例如 Tree SRCU(动态分配 srcu_struct 时前缀为 srcud-)会输出每 CPU 计数器状态:
srcud-torture: Tree SRCU per-CPU(idx=0): 0(35,-21) 1(-4,24) 2(1,1) 3(-26,20) 4(28,-47) 5(-9,4) 6(-10,14) 7(-14,11) T(1,6)
括号中的两个数字分别是相应 CPU 的“old”与“current”计数器值;idx 将 old/current 值映射到底层数组,对调试很有用;最后的 T 项是所有计数器的总和。
三、在指定内核构建上运行 rcutorture
当你希望“对某个特定的内核构建做 RCU 拷问”(例如该构建即将进入生产环境)时,推荐做法是把内核构建成 CONFIG_RCU_TORTURE_TEST=m,这样就能用 modprobe 启动测试、rmmod 终止测试。文档给出的最小脚本如下:
#!/bin/sh
modprobe rcutorture
sleep 3600
rmmod rcutorture
dmesg | grep torture:
要点:
- 输出需人工检查错误标志
!!!,当然也可以编写更复杂的脚本自动检测; rmmod命令会强制printk()出SUCCESS、FAILURE或RCU_HOTPLUG指示之一:前两者不言自明;RCU_HOTPLUG表示没有发现 RCU 故障,但检测到了 CPU 热插拔问题。
四、主线开发中的自动化测试:kvm.sh 与场景配置
当用 rcutorture 测试 RCU 本身的变更时,往往需要在大量 Kconfig 选项组合与内核启动参数组合上分别构建多个内核逐一验证。此时反复 modprobe/rmmod 既耗时又易错,因此主线测试推荐使用 tools/testing/selftests/rcutorture/bin/kvm.sh(支持 x86、arm64 和 powerpc)。
4.1 默认行为与场景集合
kvm.sh 默认执行 configs/rcu/CFLIST 指定的场景序列,每个场景在“自动生成的最小 initrd + 最小用户态”提供的 guest OS 中运行 30 分钟。测试结束后,构建产物与控制台输出会被自动分析,运行结果汇总输出。
当前仓库中 CFLIST 的内容为:
TREE01
TREE02
TREE03
TREE04
TREE05
TREE07
TREE09
SRCU-N
SRCU-P
SRCU-T
SRCU-U
TINY01
TINY02
TASKS01
TASKS02
TASKS03
RUDE01
TRACE01
TRACE02
每个场景对应 configs/rcu/ 目录下的一个同名 Kconfig 片段(例如 TREE04、SRCU-N),并配有 .boot 文件描述该场景的启动参数;此外 configs/rcu/ 下还有 BUSTED(故意破坏 RCU 用于校验测试本身有效)、NOCB01/NOCB02(offload 回调)等扩展场景,可通过 --configs 显式选用。
4.2 常用 kvm.sh 参数与示例
-
--cpus:并发加速。例如 64 CPU 系统上使用--cpus 43,可让测试并发使用多达 43 个 CPU。按文档所述,这会把全部场景压缩到两批内完成,总耗时从约 8 小时降到约 1 小时(不含构建 16 个内核的时间)。--dryrun sched不实际运行测试,只显示测试如何分批调度,适合用来决定--cpus取多少。 -
--configs:只跑相关场景。例如修改了 Tree SRCU 时,只跑 SRCU 场景:kvm.sh --configs 'SRCU-N SRCU-P'大机器上可以让整套场景并发多份,例如 448 硬件线程系统:
kvm.sh --cpus 448 --configs '5*CFLIST'或者并发 56 份单场景:
kvm.sh --cpus 448 --configs '56*TREE04'或者两个八 CPU 场景各 28 份:
kvm.sh --cpus 448 --configs '28*TREE03 28*TREE04' -
--memory:限制每个 guest 内存,默认 512M。内存设得很小时,可能需要通过--bootargs关闭 callback-flooding 测试:kvm.sh --cpus 448 --configs '56*TREE04' --memory 128M \ --bootargs 'rcutorture.fwd_progress=0' -
--bootargs:传入内核启动参数,例如测试 CPU stall 告警代码时:--bootargs 'rcutorture.stall_cpu=30'。注意这会“故意”让脚本报告失败(即出现 RCU CPU stall 警告),属预期结果。 -
--kconfig:追加 Kconfig 调试选项,例如--kconfig 'CONFIG_RCU_EQS_DEBUG=y';此外还有--gdb、--kasan、--kcsan参数。--gdb限制一次 kvm.sh 只跑一个场景,并需要另开一个终端按脚本提示运行gdb。 -
--buildonly:只构建全部内核,不运行测试——当只需要一组内核构建产物时很有用。 -
--duration:覆盖默认 30 分钟的运行时长,支持2d/3h/5m/45s等格式。例如--duration 45s适合快速定位罕见的启动期失败。 -
--trust-make:允许本次内核构建复用上一次构建的产物。注意:不加该参数时,你的 tags 文件可能被破坏(文档原文如此提醒)。
其余更冷门参数的说明直接写在 kvm.sh 脚本源码中。
4.3 一次成功运行的汇总输出示例
按文档记载,在 12 CPU 系统上以默认场景集成功运行后,结尾会输出类似如下的汇总(GPs 指完成的宽限期数,括号内为速率):
SRCU-N ------- 804233 GPs (148.932/s) [srcu: g10008272 f0x0 ]
SRCU-P ------- 202320 GPs (37.4667/s) [srcud: g1809476 f0x0 ]
SRCU-t ------- 1122086 GPs (207.794/s) [srcu: g0 f0x0 ]
SRCU-u ------- 1111285 GPs (205.794/s) [srcud: g1 f0x0 ]
TASKS01 ------- 19666 GPs (3.64185/s) [tasks: g0 f0x0 ]
TASKS02 ------- 20541 GPs (3.80389/s) [tasks: g0 f0x0 ]
TASKS03 ------- 19416 GPs (3.59556/s) [tasks: g0 f0x0 ]
TINY01 ------- 836134 GPs (154.84/s) [rcu: g0 f0x0 ] n_max_cbs: 34198
TINY02 ------- 850371 GPs (157.476/s) [rcu: g0 f0x0 ] n_max_cbs: 2631
TREE01 ------- 162625 GPs (30.1157/s) [rcu: g1124169 f0x0 ]
TREE02 ------- 333003 GPs (61.6672/s) [rcu: g2647753 f0x0 ] n_max_cbs: 35844
TREE03 ------- 306623 GPs (56.782/s) [rcu: g2975325 f0x0 ] n_max_cbs: 1496497
CPU count limited from 16 to 12
TREE04 ------- 246149 GPs (45.5831/s) [rcu: g1695737 f0x0 ] n_max_cbs: 434961
TREE05 ------- 314603 GPs (58.2598/s) [rcu: g2257741 f0x2 ] n_max_cbs: 193997
TREE07 ------- 167347 GPs (30.9902/s) [rcu: g1079021 f0x0 ] n_max_cbs: 478732
CPU count limited from 16 to 12
TREE09 ------- 752238 GPs (139.303/s) [rcu: g13075057 f0x0 ] n_max_cbs: 99011
行中 [rcu: ... f0x0 ] 括号内的 g 为宽限期相关计数、f0x… 为失败标志(如 f0x2 表示出现了对应位标志的失败类别);n_max_cbs 记录运行期间回调队列峰值规模;CPU count limited from 16 to 12 说明该场景声明的 CPU 数被系统实际可用 CPU 数限制。
五、失败排查:res 结果目录与 kvm-find-errors.sh
如果运行中出现失败,kvm.sh 输出末尾会列出构建期失败与运行期失败的数量——建议把完整输出重定向到文件。每次运行的构建产物和控制台输出保存在 tools/testing/selftests/rcutorture/res 下按时间戳命名的目录中。把某个目录传给 kvm-find-errors.sh,它会带你逐条浏览错误摘要与完整错误日志:
tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh \
tools/testing/selftests/rcutorture/res/2020.01.20-15.54.23
不过直接翻文件往往更方便,目录结构约定如下:
- 顶层目录(如
2020.01.20-15.54.23)存放本次运行中所有场景共有的文件,其中最常用的是testid.txt:如果在 git 仓库中运行,该文件包含被测试的 commit 以及未提交变更的 diff 格式内容; - 每个场景一个子目录(如
TREE04);同一场景运行多次时(例如--configs '56*TREE04'),第二次及以后的目录会带序号:TREE04.2、TREE04.3依此类推。
每个场景子目录中最常用的文件:
| 文件 | 内容 |
|---|---|
.config |
该场景使用的 Kconfig 选项 |
Make.out |
该场景的内核构建输出 |
console.log |
该场景的控制台输出(内核启动后即可检查;构建失败时可能不存在) |
vmlinux |
内核二进制,可配合 objdump、gdb 使用 |
另有若干使用频率较低的文件,多用于 rcutorture 工具链自身的调试。
六、重复运行:kvm-again.sh 快速复现
追踪罕见的启动期失败时,若每次都用 kvm.sh,则每次运行都会重新构建内核——需要跑 1000 次才敢确信修好时,这种无谓的重建会极其烦人。kvm-again.sh 正是为此而生:给定上一次 kvm.sh 留下的结果目录,不重新构建即可重跑。
假设上一次运行的输出在:
tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28
则直接重跑:
kvm-again.sh tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28
部分原始 kvm.sh 参数可以覆盖,最常用的是 --duration 与 --bootargs:
kvm-again.sh tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28 \
--duration 45s
这样重跑只持续 45 秒,非常适合快速定位上文提到的罕见启动期失败。
七、分布式运行:kvm-remote.sh 跨机器并行
kvm.sh 的测试局限在单机内。用任何框架让 5 台机器各跑一份 kvm.sh 并不难,但那样几乎必然导致内核被重复构建;手工把 rcutorture 场景分配到各台机器上则繁琐且易错。kvm-remote.sh 就是为解决这两个问题设计的。
前提是对每台目标机器免密 SSH 可用,例如:
ssh system0 date
且 system1 至 system5 同样可用、每台都是 64 CPU。那么执行:
kvm-remote.sh "system0 system1 system2 system3 system4 system5" \
--cpus 64 --duration 8h --configs "5*CFLIST"
行为:在本地系统上构建每个默认场景的内核,然后把每个场景的 5 份实例分摊到所列机器上,每个场景运行 8 小时;运行结束后结果会被收集、记录并打印。kvm.sh 接受的大多数参数都可以传给 kvm-remote.sh,但系统列表必须放在最前面。
配合 --dryrun scenarios,可以估算一批跨机器运行能塞下多少个场景。
和 kvm-again.sh 一样,也可以在 kvm-remote.sh 上复跑上一次远程运行,只需把旧运行结果目录路径(注意 -remote 后缀)跟在系统列表之后:
kvm-remote.sh "system0 system1 system2 system3 system4 system5" \
tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28-remote \
--duration 24h
此时大多数 kvm-again.sh 支持的参数都可以在旧运行结果目录路径之后给出。
八、小结:一条完整的验证链路
把全文串起来,一次典型的“RCU 变更回归验证”流程是:
- 以
CONFIG_RCU_TORTURE_TEST=m构建内核,或在开发中直接用 kvm.sh 工具链构建多个场景内核; - 用
kvm.sh --cpus <N>(必要时加--configs收窄场景、--duration/--memory/--bootargs/--kconfig调整)执行自动化测试; - 运行结束后检查汇总输出中的 GPs 速率与
f0x标志;若有失败,进入tools/testing/selftests/rcutorture/res/<时间戳>目录按testid.txt→ 场景子目录(.config、Make.out、console.log、vmlinux)的顺序定位,或用 kvm-find-errors.sh 自动浏览; - 若是罕见启动期问题,用 kvm-again.sh 免重建快速重复;规模大时用 kvm-remote.sh 分摊到多台机器;
- 人工复核
dmesg | grep torture:中的统计块:确认无!!!标志、rtmbe/rtbe/rtbke/rtbre为零、Reader Pipe 与 Reader Batch 的前两项之后全零、Free-Block Circulation 末项为零,并以rmmod触发的SUCCESS/FAILURE/RCU_HOTPLUG判定收尾。
以上所有行为均以当前仓库 Documentation/RCU/torture.rst 文档及 kernel/rcu/rcutorture.c、tools/testing/selftests/rcutorture/ 工具链的实际内容为依据;具体 Kconfig 组合与场景文件可进一步查阅 configs/rcu/ 目录核实。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00