RCU 压力测试操作¶
CONFIG_RCU_TORTURE_TEST¶
CONFIG_RCU_TORTURE_TEST 配置选项适用于所有 RCU 实现。它会创建一个 rcutorture 内核模块,该模块可以加载以运行压力测试。该测试会定期通过 printk()
输出状态消息,这些消息可以通过 dmesg 命令查看(可能需要 grep “torture”)。测试在模块加载时开始,在模块卸载时停止。
模块参数在 内核的命令行参数 中以 “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:” 将在大多数系统中提取此信息。在更深奥的配置中,可能需要使用其他命令来访问 RCU 压力测试使用的 printk()
的输出。 printk()
使用 KERN_ALERT,因此它们应该很明显。 ;-)
第一行和最后一行显示 rcutorture 模块参数,最后一行显示 “SUCCESS” 或 “FAILURE”,这基于 rcutorture 对 RCU 是否正常运行的自动判断。
条目如下:
“rtc”:当前对读者可见的结构的十六进制地址。
“ver”:自启动以来 RCU 写入器任务更改读者可见结构的次数。
“tfle”:如果非零,则表示包含要放入 “rtc” 区域的结构的 “torture freelist” 为空。这种情况很重要,因为它会让你误以为 RCU 工作正常,而实际上并非如此。 :-/
“rta”:从 torture freelist 分配的结构数量。
“rtaf”:由于列表为空而导致 torture freelist 分配失败的次数。这通常不为零,但如果它占 “rta” 指示的值的很大一部分,则是不好的。
“rtf”:释放到 torture freelist 的次数。
“rtmbe”:非零值表示 rcutorture 认为
rcu_assign_pointer()
和rcu_dereference()
未正确工作。此值应为零。“rtbe”:非零值表示
rcu_barrier()
系列函数之一未正确工作。“rtbke”:rcutorture 无法创建用于强制 RCU 优先级反转的实时 kthread。此值应为零。
“rtbre”:尽管 rcutorture 成功创建了用于强制 RCU 优先级反转的 kthread,但它无法将它们设置为实时优先级级别 1。此值应为零。
“rtbf”:RCU 优先级提升未能解决 RCU 优先级反转的次数。
“rtb”:rcutorture 尝试强制 RCU 优先级反转条件的次数。如果要通过 “test_boost” 模块参数测试 RCU 优先级提升,则此值应为非零值。
“nt”:rcutorture 从计时器处理程序中运行 RCU 读取端代码的次数。仅当您指定 “irqreader” 模块参数时,此值才应为非零值。
“Reader Pipe”:读者看到的结构“年龄”的直方图。如果前两个条目之后的任何条目为非零值,则 RCU 已损坏。并且 rcutorture 会打印错误标志字符串 “!!!” 以确保您注意到。新分配的结构的年龄为零,当从读者可见性中移除时,其变为一,并且在随后的每个宽限期增加一次 - 并在通过(RCU_TORTURE_PIPE_LEN-2)个宽限期后被释放。
上面显示的输出取自正常工作的 RCU。如果您想看看它在损坏时是什么样子,请自行将其损坏。 ;-)
“Reader Batch”:读者看到的结构“年龄”的另一个直方图,但以计数器翻转(或批次)而不是宽限期来衡量。非零条目的合法数量再次为两个。此单独视图的原因是,有时更容易在 “Reader Batch” 列表中显示第三个条目,而不是在 “Reader Pipe” 列表中显示。
“Free-Block Circulation”:显示已到达管道中给定点的 torture 结构的数量。第一个元素应与已分配的结构数量紧密对应,第二个元素应与已从读者视图中删除的结构数量紧密对应,并且除最后一个元素外,所有剩余元素都应与通过宽限期的相应次数对应。最后一个条目应为零,因为它仅在 torture 结构的计数器以某种方式增加到超出其应有的范围时才会增加。
RCU 的不同实现可以提供特定于实现的附加信息。例如,Tree SRCU 提供以下附加行:
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 的计数器状态,在本例中,对于使用动态分配的 srcu_struct 的 Tree SRCU(因此是 “srcud-” 而不是 “srcu-”)。括号中的数字是相应 CPU 的 “旧” 和 “当前” 计数器的值。“idx” 值将 “旧” 和 “当前” 值映射到基础数组,并且对调试很有用。最后的 “T” 条目包含计数器的总和。
在特定内核构建上的使用¶
有时需要在特定的内核构建上对 RCU 进行压力测试,例如,在准备将该内核构建投入生产时。在这种情况下,应使用 CONFIG_RCU_TORTURE_TEST=m 构建内核,以便可以使用 modprobe 启动测试,并使用 rmmod 终止测试。
例如,可以使用以下脚本来对 RCU 进行压力测试:
#!/bin/sh
modprobe rcutorture
sleep 3600
rmmod rcutorture
dmesg | grep torture:
可以手动检查输出中是否存在错误标志 “!!!”。当然,可以创建一个更复杂的脚本来自动检查此类错误。 “rmmod” 命令强制使用 printk()
输出 “SUCCESS”、“FAILURE” 或 “RCU_HOTPLUG” 指示。前两个是自我解释的,而最后一个表示虽然没有 RCU 故障,但检测到 CPU 热插拔问题。
在主线内核上的使用¶
当使用 rcutorture 测试 RCU 本身的更改时,通常需要构建多个内核,以便跨相关 Kconfig 选项和相关内核引导参数的各种组合来测试该更改。在这种情况下,使用 modprobe 和 rmmod 可能会非常耗时且容易出错。
因此,tools/testing/selftests/rcutorture/bin/kvm.sh 脚本可用于 x86、arm64 和 powerpc 的主线测试。默认情况下,它将运行 tools/testing/selftests/rcutorture/configs/rcu/CFLIST 指定的一系列测试,每个测试在来宾操作系统中使用由自动生成的 initrd 提供的最小用户空间运行 30 分钟。测试完成后,将分析生成的构建产品和控制台输出中的错误,并汇总运行结果。
在较大的系统上,可以通过将 --cpus 参数传递给 kvm.sh 来加速 rcutorture 测试。例如,在 64 核 CPU 系统上,“--cpus 43” 将使用多达 43 个 CPU 并行运行测试,在 v5.4 中,这将分两批完成所有场景,从而将完成时间从大约 8 小时减少到大约 1 小时(不包括构建 16 个内核的时间)。 “--dryrun sched” 参数不会运行测试,而是会告诉您如何将测试安排到批次中。这在计算 --cpus 参数中要指定的 CPU 数量时很有用。
并非所有更改都需要运行所有场景。例如,对 Tree SRCU 的更改可能仅使用 --configs 参数运行 SRCU-N 和 SRCU-P 场景,如下所示:“--configs ‘SRCU-N SRCU-P’”。大型系统可以运行完整场景集的多个副本,例如,具有 448 个硬件线程的系统可以并行运行完整集的五个实例。要实现此目的:
kvm.sh --cpus 448 --configs '5*CFLIST'
或者,这样的系统可以同时运行单个 8 核 CPU 场景的 56 个并发实例:
kvm.sh --cpus 448 --configs '56*TREE04'
或者,同时运行每个 8 核 CPU 场景的两个场景的 28 个并发实例:
kvm.sh --cpus 448 --configs '28*TREE03 28*TREE04'
当然,每个并发实例都会使用内存,可以使用 --memory 参数进行限制,该参数默认为 512M。较小的内存值可能需要使用下面讨论的 --bootargs 参数禁用回调泛洪测试。
有时,额外的调试很有用,在这种情况下,可以使用 kvm.sh 的 --kconfig 参数,例如,--kconfig 'CONFIG_RCU_EQS_DEBUG=y'
。此外,还有 --gdb、--kasan 和 --kcsan 参数。请注意,--gdb 将您限制为每次 kvm.sh 运行一个场景,并且要求您打开另一个窗口,从中按照脚本的指示运行 gdb
。
也可以提供内核引导参数,例如,控制 rcutorture 的模块参数。例如,要测试 RCU 的 CPU 停顿警告代码的更改,请使用 “--bootargs ‘rcutorture.stall_cpu=30’”。当然,这将导致脚本报告失败,即生成的 RCU CPU 停顿警告。如上所述,减少内存可能需要禁用 rcutorture 的回调泛洪测试:
kvm.sh --cpus 448 --configs '56*TREE04' --memory 128M \
--bootargs 'rcutorture.fwd_progress=0'
有时只需要一组完整的内核构建。这就是 --buildonly 参数的作用。
--duration 参数可以覆盖默认的 30 分钟运行时间。例如,--duration 2d
将运行两天,--duration 3h
将运行三小时,--duration 5m
将运行五分钟,而 --duration 45s
将运行 45 秒。最后一个对于跟踪罕见的启动时故障很有用。
最后,`--trust-make` 参数允许每个内核构建重用上一个内核构建中的可用部分。请注意,如果没有 `--trust-make` 参数,您的标签文件可能会被删除。
kvm.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`),而每个场景的文件都位于以场景命名的子目录中(例如,“TREE04”)。如果给定的场景运行多次(如上面的“--configs ‘56*TREE04’”),则与该场景的第二次及后续运行相对应的目录将包含一个序列号,例如“TREE04.2”,“TREE04.3”等等。
顶层目录中最常用的文件是 `testid.txt`。如果测试在 git 存储库中运行,则此文件包含已测试的提交以及 diff 格式的任何未提交的更改。
每个场景运行目录中最常用的文件是:
- `.config`
此文件包含 Kconfig 选项。
- `Make.out`
此文件包含特定场景的构建输出。
- `console.log`
此文件包含特定场景的控制台输出。内核启动后可以检查此文件,但如果构建失败,则该文件可能不存在。
- `vmlinux`
此文件包含内核,可用于 `objdump` 和 `gdb` 等工具。
还有许多其他文件可用,但不太常用。许多文件旨在调试 rcutorture 本身或其脚本。
从 v5.4 开始,在 12 核系统上使用默认场景集成功运行后,会在运行结束时生成以下摘要:
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
重复运行¶
假设您正在追查罕见的启动时故障。虽然您可以使用 kvm.sh,但这样做会在每次运行时重建内核。如果您需要(例如)运行 1,000 次才能确信您已修复该错误,那么这些毫无意义的重建可能会变得非常烦人。
这就是 `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.sh` 非常有用,但其测试仅限于单个系统。使用您喜欢的框架让(例如)5 个 `kvm.sh` 实例在您的 5 个系统上运行并不难,但这很可能会不必要地重建内核。此外,手动将所需的 rcutorture 场景分配到可用系统上可能既费力又容易出错。
这就是 `kvm-remote.sh` 脚本存在的原因。
如果以下命令有效:
ssh system0 date
并且如果它也适用于 system1、system2、system3、system4 和 system5,并且所有这些系统都有 64 个 CPU,则您可以键入:
kvm-remote.sh "system0 system1 system2 system3 system4 system5" \
--cpus 64 --duration 8h --configs "5*CFLIST"
这将在本地系统上构建每个默认场景的内核,然后将每个场景的五个实例分散到列出的系统上,每个场景运行八小时。在运行结束时,将收集、记录和打印结果。可以传递给 `kvm-remote.sh` 的大多数 `kvm.sh` 接受的参数,但系统列表必须放在第一位。
`kvm.sh` 的 `--dryrun scenarios
参数对于计算可以在一组系统中一次运行多少个场景非常有用。
您还可以以类似于 `kvm.sh` 的方式重新运行之前的远程运行:
- `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` 参数。