[置顶] 泰晓 RISC-V 实验箱,配套 30+ 讲嵌入式 Linux 系统开发公开课
LicheePi 4A 实时性测试实践
Corrector: TinyCorrect v0.2-rc2 - [tounix spaces header tables] Author: 王杰迅 wangjiexun@foxmail.com Date: 2023/09/17 Revisor: falcon falcon@tinylab.org Project: RISC-V Linux 内核剖析 Sponsor: PLCT Lab, ISCAS
前言
在 之前的文章 中介绍了 LicheePi 4A 开发板构建并运行 Linux v6.5-rc1 的过程,当时实现的效果为:内核可以成功启动并进入 initramfs 命令行界面。
在本文中,将介绍为 LicheePi 4A 开发板添加 eMMC 支持的过程,使得内核可以从 eMMC 上的 rootfs 启动,同时将内核版本更新到 v6.5,打入对应的 PREEMPT_RT 补丁。
除此之外,本文还将介绍使用 cyclictest 和 ftrace 进行延迟追踪的小技巧。
软件版本
Software | Version |
---|---|
Linux | 6.5 |
U-Boot | 2020.01 |
OpenSBI | thead-opensbi |
Buildroot | 2023.02.2 |
添加 eMMC 支持
LicheePi 4A 开发板提供了 eMMC 作为外部存储,eMMC 是 Flash 的一种,在嵌入式系统中使用广泛。
官方 Linux 内核从 v6.5 版本开始添加了对 LicheePi 4A 开发板的设备树支持,具体见 arch/riscv/boot/dts/thead
文件夹下的各文件。但其中的设备树目前不够完善,主要的外设只实现了串口,没有办法使用外部存储。因此想要使用 LicheePi 4A 的 eMMC,必须借助社区中其他开发者提供的补丁。
Drew Fustini 的补丁 为开发板 Beaglev-Ahead 添加了 eMMC 支持。由于 Beaglev-Ahead 和 LicheePi 4A 都是使用 TH1520 作为主控芯片,因此该补丁也可适用于 LicheePi 4A。
该补丁添加了对 eMMC 的设备树支持,并修改了驱动文件 drivers/mmc/host/sdhci-of-dwcmshc.c
,在开启内核配置项 CONFIG_MMC_SDHCI_OF_DWCMSHC=y
之后,可以实现对 eMMC 外设的基本读写。但目前该驱动还没有支持 DMA,效率较低,仍在迭代开发中。
将 eMMC 驱动和设备树替换完毕后,仍出现了多个小问题,最终通过选择合适的 OpenSBI,才实现了稳定地使用 eMMC 进行读写。
load access fault
最初时,由于使用的 OpenSBI 版本过低,没有支持设备树中的 “thead,c900-clint”,在进行初始化时会出现如下报错:
[ 0.000000] Oops - load access fault [#1]
...
[ 0.000000] epc : __plic_toggle+0x6a/0x72
[ 0.000000] ra : __plic_init.constprop.0+0x31e/0x4ac
更换为较新版本的 OpenSBI(v1.2 及以上)后,报错消失,可以正常启动。
illegal instruction
当尝试给 eMMC 施加压力测试时,如使用如下命令:
$ while true; do /bin/dd if=/dev/zero of=bigfile bs=1024000 count=1024; done &
会出现如下报错导致系统崩溃:
sbi_trap_error: hart1: illegal instruction handler failed (error -2)
经 Jisheng Zhang 老师提醒,使用 最新的 thead-opensbi 后,eMMC 在压力测试下也可以稳定使用,不再报错。猜测该报错产生的原因为:TH1520 芯片的部分指令没有被官方 OpenSBI 实现。
总之,目前应使用 最新的 thead-opensbi,以保证系统正常运行。
rootfs
当 eMMC 可以正常使用后,就可以不再使用 initramfs。将 rootfs 烧写到 eMMC 后,系统就可以从 eMMC 的 rootfs 启动。使用 Buildroot 的 menuconfig,选择构建的 rootfs 文件系统类型为 ext4:
Filesystem images --->
[*] ext2/3/4 root filesystem
ext2/3/4 variant (ext4) --->
(rootfs) filesystem label
(60M) exact size
LicheePi 4A 提供的烧录工具最大可以将 4GB 的 rootfs 烧录到 eMMC 中,但实际的 rootfs 可能并没有那么大,在制作 rootfs 时可以选择较小的大小,如 60MB。在系统启动后,可以使用 resize2fs 命令对 rootfs 进行扩容:
$ resize2fs /dev/mmcblk0p3
使用该命令会自动将 rootfs 扩容,将 eMMC 的未使用存储空间全部并入其中。
编译 PREEMPT_RT v6.5 内核
为了提高系统实时性,首先打入和 Linux 内核版本对应的 官方 PREEMPT_RT 补丁。同时,由于 LicheePi 4A 开发板基于 RISC-V 架构,因此还需要打入 针对 RISC-V 架构的 PREEMPT_RT 补丁。
解决 redefinition 错误
在将 Linux 内核更新到 v6.5,打入 PREEMPT_RT 补丁并配置抢占模式为 PREEMPT_RT 后,尝试编译时出现报错:
arch/riscv/kernel/irq.c:64:6: error: redefinition of 'do_softirq_own_stack'
64 | void do_softirq_own_stack(void)
| ^~~~~~~~~~~~~~~~~~~~
In file included from ./arch/riscv/include/generated/asm/softirq_stack.h:1,
from arch/riscv/kernel/irq.c:15:
./include/asm-generic/softirq_stack.h:8:20: note: previous definition of 'do_softirq_own_stack' was here
8 | static inline void do_softirq_own_stack(void)
| ^~~~~~~~~~~~~~~~~~~~
经过查看后发现,在 arch/riscv/kernel/irq.c
和 include/asm-generic/softirq_stack.h
文件中出现了对 do_softirq_own_stack
函数的重复定义。
该错误与以下两个配置项有关:
CONFIG_HAVE_SOFTIRQ_ON_OWN_STACK
CONFIG_SOFTIRQ_ON_OWN_STACK
正常的逻辑是,如果内核配置 CONFIG_SOFTIRQ_ON_OWN_STACK=y
,则该函数由 include/asm-generic/softirq_stack.h
文件负责实现。
如果内核配置 CONFIG_SOFTIRQ_ON_OWN_STACK=n
,则该函数在 include/asm-generic/softirq_stack.h
文件中只负责声明,而由特定架构负责具体实现,如 RISC-V 架构就在 arch/riscv/kernel/irq.c
文件中实现。
但在 arch/riscv/kernel/irq.c
文件中,错把实现 do_softirq_own_stack
函数的条件 SOFTIRQ_ON_OWN_STACK
写成了 HAVE_SOFTIRQ_ON_OWN_STACK
选项,当内核配置项如下时:
CONFIG_HAVE_SOFTIRQ_ON_OWN_STACK=y
CONFIG_SOFTIRQ_ON_OWN_STACK=n
两个文件都会实现 do_softirq_own_stack
函数,出现 redefinition 错误。
值得一提的是,当没有开启 PREEMPT_RT 时,开启 HAVE_SOFTIRQ_ON_OWN_STACK
时默认会开启 SOFTIRQ_ON_OWN_STACK
,因此不会暴露出错误,只有当开启 PREEMPT_RT 时,才会导致编译失败。
目前,该补丁已发送至 邮件列表。
延迟追踪技巧
在优化系统实时性能时,首先需要分析产生最大延迟的原因。常用的延迟测试及追踪工具为 cyclictest 和 ftrace。这两个软件的基本用法,可以参考泰晓科技的往期直播分享 RISC-V 实时抢占优化实践 及 对应 PPT。
实际测试时,容易遇到的一个问题是:测试得到的最大延迟并不是 cyclictest 产生的,这对优化实时性能没有任何帮助。例如:
# wakeup_rt latency trace v1.1.5 on 6.5.0-rt6-r1208-00003-g999d221864bf-dirty
# --------------------------------------------------------------------
# latency: 12086 us, #6/6, CPU#0 | (M:preempt_rt VP:0, KP:0, SP:0 HP:0 #P:4)
# -----------------
# | task: irq/12-ttyS0-74 (uid:0 nice:0 policy:1 rt_prio:50)
# -----------------
……
而实际希望得到的最大延迟和调用栈应该是由 cyclictest 产生的,例如:
# wakeup_rt latency trace v1.1.5 on 6.5.0-rt6-r1208-00003-g999d221864bf-dirty
# --------------------------------------------------------------------
# latency: 879 us, #6/6, CPU#2 | (M:preempt_rt VP:0, KP:0, SP:0 HP:0 #P:4)
# -----------------
# | task: cyclictest-212 (uid:0 nice:0 policy:1 rt_prio:99)
# -----------------
……
如果进行了较长时间的测试之后,查看 ftrace 的结果发现没有任何帮助,难免会令人大失所望。因此需要使用一些技巧保证最大延迟和调用栈是由 cyclictest 产生的。
cyclictest 提供了参数 --breaktrace=
和 --tracemark
以更好地锁定最大延迟的产生原因。--breaktrace
表示,如果测试时 cyclictest 的最大延迟超过给定的数值,就中断测试。--tracemark
表示保存产生最大延迟的函数调用栈。在 cyclictest 超过指定延迟后立即中断测试并记录,基本可以保证最大延迟是由 cyclictest 产生的。
在使用 --tracemark
参数前首先要保证 ftrace 挂载好,并在 current_tracer 文件中选择合适的 tracer。测试前无需手动将 tracing_on 文件置 1,cyclictest 在测试时会自动将 tracing_on 文件置 1,测试结束会自动置 0。测试因超过设定数值而中断后,不会自动打印调用栈,需要自己手动打印 ftrace 中的 trace 文件查看。
下面是一个简单的示例脚本,在 cyclictest 产生的最大延迟大于 100μs 时,会自动中断测试,并打印产生的最大延迟和函数调用栈:
#!/bin/sh
mount -t tracefs none /sys/kernel/tracing
cd /sys/kernel/tracing
echo wakeup_rt > current_tracer
cyclictest --breaktrace=100 --tracemark
cat trace
总结
本文介绍了为 LicheePi 4A 开发板添加 eMMC 支持的过程,使得内核可以从 eMMC 上的 rootfs 启动,还为 v6.5 版本的内核打入了 PREEMPT_RT 补丁,并解决了 redefinition 编译错误。除此之外,本文还介绍了使用 cyclictest 和 ftrace 追踪延迟的小技巧。
参考资料
猜你喜欢:
- 我要投稿:发表原创技术文章,收获福利、挚友与行业影响力
- 知识星球:独家 Linux 实战经验与技巧,订阅「Linux知识星球」
- 视频频道:泰晓学院,B 站,发布各类 Linux 视频课
- 开源小店:欢迎光临泰晓科技自营店,购物支持泰晓原创
- 技术交流:Linux 用户技术交流微信群,联系微信号:tinylab
支付宝打赏 ¥9.68元 | 微信打赏 ¥9.68元 | |
请作者喝杯咖啡吧 |
Read Album:
- TinyBPT 和面向 buildroot 的二进制包管理服务(1):设计简介与框架
- RISC-V Linux 内核及周边技术动态第 118 期
- RISC-V Linux 内核及周边技术动态第 117 期
- 实时分析工具 rtla timerlat 介绍(二):延迟测试原理
- 实时分析工具 rtla timerlat 介绍(一):交叉编译及使用