转载于公众号LoyenWang
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进行地址转换;
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.1 device reserved
通常,可以为设备指定专用的dma coherent的区域,有以下的方式:
- 通过在设备树中添加对应的属性值,驱动中可以调用
of_reserved_mem_device_init
最终完成dma区域的注册; - 直接通过接口
dma_decleare_coherent_memory
调用来进行注册;
3.1.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为例:
最终的代码将调用到汇编中,操作也较简单,不再赘述了;
评论 (0)