首页
/ Linux 内核 RCU 压力测试实战:CONFIG_RCU_TORTURE_TEST 与 rcutorture 自动化测试工具链详解

Linux 内核 RCU 压力测试实战:CONFIG_RCU_TORTURE_TEST 与 rcutorture 自动化测试工具链详解

2026-09-04 20:00:43作者:蔡丛锟

本文以内核文档 RCU Torture Test Operation 为核心,系统讲解 Linux 内核 RCU(Read-Copy Update)压力测试工具 rcutorture 的使用方法:包括 CONFIG_RCU_TORTURE_TEST 编译选项、模块参数、测试输出的逐字段解读,以及 kvm.shkvm-again.shkvm-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 生效的模块参数;末行给出 SUCCESSFAILURE 判定——这是 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()SUCCESSFAILURERCU_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 片段(例如 TREE04SRCU-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.2TREE04.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

system1system5 同样可用、每台都是 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 变更回归验证”流程是:

  1. CONFIG_RCU_TORTURE_TEST=m 构建内核,或在开发中直接用 kvm.sh 工具链构建多个场景内核;
  2. kvm.sh --cpus <N>(必要时加 --configs 收窄场景、--duration/--memory/--bootargs/--kconfig 调整)执行自动化测试;
  3. 运行结束后检查汇总输出中的 GPs 速率与 f0x 标志;若有失败,进入 tools/testing/selftests/rcutorture/res/<时间戳> 目录按 testid.txt → 场景子目录(.configMake.outconsole.logvmlinux)的顺序定位,或用 kvm-find-errors.sh 自动浏览;
  4. 若是罕见启动期问题,用 kvm-again.sh 免重建快速重复;规模大时用 kvm-remote.sh 分摊到多台机器;
  5. 人工复核 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.ctools/testing/selftests/rcutorture/ 工具链的实际内容为依据;具体 Kconfig 组合与场景文件可进一步查阅 configs/rcu/ 目录核实。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
903
1.82 K
docsdocs
暂无描述
Markdown
888
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.51 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341