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 模块参数,最后一行根据 rcutorture 对 RCU 是否正常运行的自动判断显示 “SUCCESS” 或 “FAILURE”。
条目如下
“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 的计数器状态,在本例中为 Tree SRCU,它使用动态分配的 srcu_struct(因此为 “srcud-” 而不是 “srcu-“)。括号中的数字是相应 CPU 的 “old” 和 “current” 计数器的值。“idx” 值将 “old” 和 “current” 值映射到底层数组,这对于调试很有用。最后一个 “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 提供的最小用户空间的 Guest OS 中运行 30 分钟。测试完成后,将分析生成的构建产品和控制台输出是否存在错误,并汇总运行结果。
在较大的系统上,可以通过将 --cpus 参数传递给 kvm.sh 来加速 rcutorture 测试。例如,在 64 CPU 系统上,“--cpus 43” 将使用多达 43 个 CPU 并发运行测试,在 v5.4 中,这将以两个批次完成所有场景,从而将完成时间从大约 8 小时减少到大约 1 小时(不包括构建 16 个内核的时间)。 “--dryrun sched” 参数不会运行测试,而是告诉您如何将测试安排到批次中。在计算在 --cpus 参数中指定多少个 CPU 时,这可能很有用。
并非所有更改都需要运行所有场景。例如,对 Tree SRCU 的更改可能只运行 SRCU-N 和 SRCU-P 场景,使用 kvm.sh 的 --configs 参数,如下所示:“--configs ‘SRCU-N SRCU-P’”。大型系统可以运行完整场景集的多个副本,例如,具有 448 个硬件线程的系统可以并发运行五个完整集的实例。为了实现这一点
kvm.sh --cpus 448 --configs '5*CFLIST'
或者,这样的系统可以运行单个八 CPU 场景的 56 个并发实例
kvm.sh --cpus 448 --configs '56*TREE04'
或每个八 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 参数,您的 tags 文件可能会被破坏。
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 CPU 系统的运行结束时生成以下摘要
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 个系统上运行(例如)5 个 kvm.sh 实例并不难,但这很可能会不必要地重建内核。此外,手动将所需的 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.sh 将接受的大多数参数都可以传递给 kvm-remote.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 参数。