解析Linux DMA mapping机制
说明:
Kernel版本:4.14
ARM64处理器,Contex-A53,双核
使用工具:Source Insight 3.5, Visio
1. 概述
DMA(Direct Memory Access):直接存储器访问;
先看问题的引入:

Non-DMA:CPU直接与设备进行数据交互,CPU的负载会随着数据的读写而增加;
DMA:CPU不参与数据的直接传输,DMA Controller负责Device与Memory之间的数据搬运,并以中断信号的形式通知CPU;
可以看出,使用DMA的最大优点是可以提高CPU的使用率;
2. address mapping
DMA涉及三种地址空间:
CPU虚拟地址:CPU使用的地址空间;
CPU物理地址:CPU使用的虚拟地址通过MMU转换成物理地址;
总线地址:设备使用的地址空间;
CPU与Device看待地址的空间不一样,看几个示例:

A:Host bridge负责将Bus address映射到CPU的物理地址空间,可以通过
ioremap
来使用,比如PCI/PCIe;B:设备使用的总线地址,可以通过IOMMU访问到CPU的物理地址空间,由IOMMU来负责映射;
C:设备使用的总线地址与CPU的物理地址相同,不需要使用IOMMU进行地址转换;
【文章福利】小编推荐自己的Linux内核技术交流群:【749907784】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!(含视频教程、电子书、实战项目及代码)


2.1 cache coherence
DMA的操作,通常与cache相关,先了解一下cache coherence:

cache coherence设备:设备之间的读写不需要关心cache的一致性问题,硬件将确保数据一致,比如连接在ARM CCI端口上的设备就是cache coherence设备;
non-coherence设备:需要额外的软件操作(flush/invalidate)等操作来确保数据一致;
3. DMA mappings
Linux内核中提供了两种dma mapping的接口:Consistent mapping和Stream mapping。
3.1 Consistent DMA mappings
consistent mapping:对应于cache-coherence设备,硬件确保device和CPU都能并行访问数据,并能看到彼此的更新,而不需要软件的flush操作;
通常在驱动init时进行map操作,而在deinit时进行unmap操作;
通常在使用consistent dma mapping时,首先需要通过dma_alloc_coherent
接口来分配一段区域:

dma_alloc_coherent
用于分配coherent内存,并返回对应的虚拟地址;进行内存分配时,存在三种方式:1)优先从设备专用的dma池开始分配;2)无专用dma池,如果是dma-direct访问,通过dma_direct_alloc分配,而底层是依赖于CMA来分配;3)使用IOMMU的设备,则通过iommu的操作函数集来分配;
3.1 device reserved
通常,可以为设备指定专用的dma coherent的区域,有以下的方式:

通过在设备树中添加对应的属性值,驱动中可以调用
of_reserved_mem_device_init
最终完成dma区域的注册;直接通过接口
dma_decleare_coherent_memory
调用来进行注册;
3.2 dma pool
驱动中经常面临buffer的管理,可以使用dma pool机制来处理,大概的原理如下:

dma-pool以页为单位来进行管理分配,可以通过添加多个dma池来使用;
dma-pool子系统的细节描述,需要另起一篇文章了;
3.2 Streaming DMA mappings
streaming mapping:对应于non-coherence设备;
通常在单次DMA传输时进行map,在传输完成后进行unmap(除非调用了
dma_sync_XXX()
函数);non-coherence设备,由于buffer不与其他数据共享cache line,通常会work better;
先看一下数据一致性问题:

dma to device时,需要将cache中的数据flush到memory中;
dma from device时,需要先将cache中的数据invalidate掉,避免CPU读取的是原来的数据;
dma_map_single
函数如下:

map操作时存在两种方式,直接映射或使用iommu来完成映射;
dma_unmap_single
是逆操作:

从上述函数中可以看到,最终都会调用到arch相关的cache操作,这个与体系结构是强相关的,以arm64为例:

最终的代码将调用到汇编中,操作也较简单,不再赘述了;
原文作者:LoyenWang
