mmap在arm64背后的那个深坑
背景
linux kernel 5.10.100
故障现象
扫描pci设备时,出现设备扫描失败。
故障分析
该程序使用mmap /dev/mem指定的物理地址,在用户态操作寄存器。类似于:
内核调试之devmem直接读写寄存器 - 知乎 (zhihu.com)
A核负责扫描,B核负责利用该寄存器答复A核。AB两个核是inner shareable。A核对某个RC扫描,x16 lane拆分为两个x8,一个做rc x8,一个做ep x8,使用pci链接线连接两个x8.

硬件链接和扫描示意图
扫描失败的初步分析,是由于pci发出对slot的扫描之后,出现了超时,导致bus扫描时认为这个插槽没有设备。
超时的记录为:
[ 329.135578] pci_bus 0001:c2: before scan 0
[ 329.525806] pci_bus 0001:c2: after scan 0
两者的时间相差390ms,而这个时间,刚好就是rc设置的超时时间窗。也就是说rc发生了扫描超时,出现代答。
而事实上,B核在接受到扫描信息时,回复了对应的扫描需求,但是从pcie协议分析仪的角度,每次B核回复的tlp,第一次必然超时,而B核tlp线程回复的再次后面的tlp,RC都能收到。
那么,第一次超时的原因,是什么呢?
从代码分析,B核上的软件使用了mmap /dev/mem的方式来建立映射,写寄存器时,发现第一次写寄存器很慢,消耗的时间刚好和rc超时的时间一致。
pz1抓波形,我们看到了很诡异的那一面,B核在发出写寄存器附近的指令,必然伴随着一个tlbi+dsb指令,而且根据tlbi指令的波形,分析出对应的地址,刚好就是我们写的寄存器的va地址,(一开始波形分析这个VA地址分析有错,误导了一些方向,消耗了一些时间)。

tlbi的va地址,刚好落在对应B核tlp线程的地址空间的写寄存器范围
根据波形分析,A核发出rescan请求1,等待的过程中,B和发出tlbi+dsb,而这个dsb指令在A核的请求1超时之后,才给B核回复,这样看起来就是,A核在等待B核的业务回复,而B核的dsb在等待A核的tranaction完成,形成了指令级别的死锁,注意,这个是指令级别的。必现。
这样引发了两个疑问,
第一个是,为什么mmap /dev/mem在建好页表之后,访问指定地址会发生tlbi?
第二个是,为什么B核的dsb要等A核的transaction超时后才会返回?
问题太过绕口,这两个问题继续排查,都已经得到原因,后来通过配置 kernel相关配置,也就是开启 CONFIG_ARM64_HW_AFDBM 解决了该问题。

1991是tlp线程
问题1就是因为 arm64: Ensure VM_WRITE|VM_SHARED ptes are clean by default · torvalds/linux@aa57157 · GitHub这个补丁之后,注意,这个补丁后来又反向合入到了很多低版本的lts,所以需要根据源码,而不是根据时间判断是否合入这个补丁。
当然这个补丁只是原因的一部分,需要叠加上 TCR_EL1.HD=0这个控制。
类似于 为什么mmap之后访问地址仍然发生了缺页异常? - 知乎 (zhihu.com) ,不同的是不是 CONFIG_ARM64_ERRATUM_1024718导致的。
问题2就是因为 arm64中DSB的语义,实现为 DVM sync属性,则必然等待其他核完成前面指令才会返回,如果两者属于同一个dvm域,则必现。
继续讨论
新讨论请用 GitHub 账号在下方发言——评论会保存在本仓库的 Discussions 里,与站点同寿; 再下方的「历史评论」是原站的只读存档。
评论(7 条,只读存档)
以下为原站的历史评论(只读存档);继续讨论请用上方的评论区。

透苇
markened-frank
安庆
jiyouzhan 主页
肥饶 主页
muto
test