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

评论(0) 浏览(97)

Linux读写锁逻辑解析

作者:OPPO内核团队 发布于:2023-5-29 20:57 分类:内核同步机制

除了mutex,在linux内核中,还有一个经常用到的睡眠锁就是rw semaphore(后文简称为rwsem),它到底和mutex有什么不同呢?为何会有rw semaphore?无他,仅仅是为了增加内核的并发,从而增加性能而已。Mutex严格的限制只有一个thread可以进入临界区,但是实际应用中,有些场景对共享资源的访问可以严格区分读和写的,并且是读多写少,这时候,其实多个读的thread同时进入临界区是OK的,使用mutex则限制一个线程进入临界区,从而导致性能的下降。

本文会描述linux5.15.81中读写锁的数据结构和逻辑过程。

阅读全文>>

标签: rwsem

评论(1) 浏览(4215)

futex基础问答

作者:OPPO内核团队 发布于:2022-10-9 7:20 分类:内核同步机制

futex是Fast Userspace muTEX的缩写,该机制是由Rusty Russell、Hubertus Franke和Mathew Kirkwood在2.5.7版本的内核中引入,虽然名字中有互斥锁(mutex)的含义,但实际它是一种用于用户空间应用程序的通用同步工具(基于futex可以在userspace实现互斥锁、读写锁、condition variable等同步机制)。

阅读全文>>

标签: futex

评论(2) 浏览(6425)

如何使能500个virtio_blk设备

作者:安庆 发布于:2022-8-12 16:12 分类:Linux内核分析

一例virtio_blk设备中断占用分析

背景:这个是在客户的centos8.4的环境上复现的,dpu是目前很多云服务器上的网卡标配了,在云豹的dpu产品测试中,dpu实现的virtio_blk设备在申请中断时报错,在排查这个错误的过程中,觉得某些部分还比较有趣,故记录之。本身涉及的背景知识有:irq,msi,irq_domain,affinity,virtio_blk,irqbalance。下面列一下我们是怎么排查并解决这个问题的。

阅读全文>>

评论(0) 浏览(3841)

Linux内核同步机制之(九):Queued spinlock

作者:OPPO内核团队 发布于:2022-6-29 6:33 分类:内核同步机制

本站之前已经有了一篇关于spinlock的文档,在之前的文章中有对自旋锁进行简单的介绍,同时给出了API汇整和应用场景。不过该文章中的自旋锁描述是基于比较老的内核版本,那时候的自旋锁还是ticket base锁,而目前最新内核中的自旋锁已经进化成queued spinlock,因此需要一篇新的自旋锁文档来跟上时代。此外,本文将不再描述基本的API和应用场景,主要的篇幅将集中在具体的自旋锁实现上。顺便说一句,同时准备一份linux5.10源码是打开本文的正确方式。

由于自旋锁可以在各种上下文中使用,因此本文中的thread是执行线索的意思,表示进程上下文、hardirq上下文、softirq上下文等多种执行线索,而不是调度器中线程的意思。

阅读全文>>

标签: spinlock queued

评论(3) 浏览(7091)

Linux内核同步机制之(八):mutex

作者:OPPO内核团队 发布于:2022-5-10 5:55 分类:内核同步机制

在linux内核中,互斥量(mutex,即mutual exclusion)是一种保证串行化的睡眠锁机制。和spinlock的语义类似,都是允许一个执行线索进入临界区,不同的是当无法获得锁的时候,spinlock原地自旋,而mutex则是选择挂起当前线程,进入阻塞状态。本文代码来自Linux 5.10.61,文章中的代码片段有删减,阅读本文时可以参考源代码。

阅读全文>>

标签: mutex

评论(2) 浏览(9837)

schedutil governor情景分析

作者:OPPO内核团队 发布于:2022-4-26 6:29 分类:进程管理

这是一篇分析schedutil governor(后文称之sugov)代码逻辑的文章。通过详细的代码级别的分析,希望能够帮助读者理解sugov的代码精妙之处。本文主要分四个章节:第一章简单重复了sugov相关的软件结构和基本算法,让读者对整个sugov在系统所处的位置和基本的逻辑控制有所了解。第二章对sugov使用的数据结构给出了详细的解释。第三章对sugov和cpufreq core的基本数据流和控制流进行分析。第四章描述了sugov本身的调频逻辑。

本文出现的内核代码来自Linux5.10.61,为了减少篇幅,我们会引用缩减版本的代码(仅包含主要逻辑),如果有兴趣,读者可以配合原始代码阅读本文。

阅读全文>>

标签: governor schedutil

评论(3) 浏览(8044)

PELT算法浅析

作者:OPPO内核团队 发布于:2022-4-7 7:09 分类:进程管理

Linux是一个通用操作系统的内核,她的目标是星辰大海,上到网络服务器,下至嵌入式设备都能运行良好。做一款好的linux进程调度器是一项非常具有挑战性的任务,因为设计约束太多了:

---它必须是公平的

---快速响应

---系统的throughput要高

---功耗要小

3.8版本之前的内核CFS调度器在计算CPU load的时候采用的是跟踪每个运行队列上的负载(per-rq load tracking)。这种粗略的负载跟踪算法显然无法为调度算法提供足够的支撑。为了完美的满足上面的所有需求,Linux调度器在3.8版中引入了PELT(Per-entity load tracking)算法。本文将为您分析PELT的设计概念和具体的实现。

本文出现的内核代码来自Linux5.10.61,如果有兴趣,读者可以配合代码阅读本文。

阅读全文>>

标签: PELT

评论(9) 浏览(7350)

load_balance函数代码详解

作者:OPPO内核团队 发布于:2022-2-16 7:29 分类:进程管理

我们描述CFS任务负载均衡的系列文章一共三篇,第一篇是框架部分,第二篇描述了task placement和active upmigration两个典型的负载均衡场景,第三篇是负载均衡的情景分析,包括tick balance、nohz idle balance和new idle balance。在负载均衡情景分析文档最后,我们给出了结论:tick balancing、nohz idle balancing、new idle balancing都是万法归宗,汇聚到load_balance函数来完成具体的负载均衡工作。本文就是第三篇负载均衡情景分析的附加篇,重点给大家展示load_balance函数的精妙。

本文出现的内核代码来自Linux5.10.61,为了减少篇幅,我们对引用的代码进行了删减(例如去掉了NUMA的代码,毕竟手机平台上我们暂时不关注这个特性),如果有兴趣,读者可以配合完整的源代码代码阅读本文。

阅读全文>>

标签: load_balance

评论(1) 浏览(6621)

CPU 多核指令 —— WFE 原理

作者:heaven 发布于:2022-2-11 1:34 分类:ARMv8A Arch

今天我想分享一个跟多核锁原理相关的东西,由于我搞 arm 居多,所以目前只研究了 arm 架构下的 WFE 指令,分享出来,如果有表述不精准或者错误的地方还请大家指出,非常感谢。研究这个原因也是只是想搞清楚所以然和来龙去脉,以后写代码可以更游刃有余。

阅读全文>>

标签: ARM wfe wfi

评论(2) 浏览(6559)

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