mmap在arm64背后的那个深坑

作者:安庆 发布于:2024-2-28 20:13 分类:Linux内核分析

背景

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域,则必现。

评论:

markened-frank
2024-04-12 19:14
我记得DSB的语义是等待本核的前面的操作完成,并不能等待其他核的操作完成吧
安庆
2024-04-24 14:41
@markened-frank:是的,但是它前面是tlbi啊
透苇
2024-04-02 17:37
终于看到更新了,赞

发表评论:

Copyright @ 2013-2015 蜗窝科技 All rights reserved. Powered by emlog