说明:
- 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的使用率;
资料直通车:Linux内核源码技术学习路线+视频教程内核源码
学习直通车:Linux内核源码内存调优文件系统进程管理设备驱动/网络协议栈
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进行地址转换;
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.2 device reserved
通常,可以为设备指定专用的dma coherent的区域,有以下的方式:
- 通过在设备树中添加对应的属性值,驱动中可以调用of_reserved_mem_device_init最终完成dma区域的注册;
- 直接通过接口dma_decleare_coherent_memory调用来进行注册;
3.3 dma pool
驱动中经常面临buffer的管理,可以使用dma pool机制来处理,大概的原理如下:
- dma-pool以页为单位来进行管理分配,可以通过添加多个dma池来使用;
- dma-pool子系统的细节描述,需要另起一篇文章了;
3.4 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为例:
- 最终的代码将调用到汇编中,操作也较简单,不再赘述了;