Linux DMA访问的一致性
DMA访问的一致性
DMA对内存是直接访问的,而CPU对内存的访问有时会通过cache。不管是CPU还是DMA访问内存,都需要确保cache的一致性。本文只分析从DMA的角度,对内存的访问如何确保cache的一致性。个人理解,通常为保证cache的一致性,在DMA访问内存前后,对cache要有下面必要的操作。
DMA读操作
DMA从外设读取数据到内存时,因读取完成后内存数据改变,因此需要在读取完成后invalidate cache。
DMA写操作
DMA将内存的数据写入外设时,在写之前,有可能cache中最新的数据并没有同步到内存,因此需要在写之前flush cache。
如果硬件上支持DMA访问的一致性(dma-coherent),软件就不需要上述的cache操作。设备寄存器上通常会有DMA SNOOP的选项,来设置CPU cache是否窥探DMA的传输。
Linux一致性DMA映射和流式DMA映射
Linux动态DMA映射一文,简单介绍了DMA映射的两种类型及相关API:一致性DMA映射和流式DMA映射。它们有什么区别呢?
使用一致性DMA映射,CPU和DMA看到的内存时一致的。通常在初始化阶段分配一致性buffer,默认分配的是uncached buffer,不使用cache就不存在一致性问题。如果SoC支持cache-coherent DMA,就可以分配cacheable buffer。
使用流式DMA映射,在dma map和unmap的时候会根据数据方向来对cache进行正确的处理。在unmap之前,CPU一般不能直接访问相应内存。这一点在Linux动态DMA映射一文中有介绍。当然如果SoC支持cache-coherent DMA,dma map和unmap的时候就不必有软件的cache处理了。
当CPU和DMA都需要频繁操作同一块内存的时候,使用一致性DMA映射比较合适。例如对基于描述符的DMA控制器(另一种DMA控制器是基于寄存器的),分配一致性buffer来存储描述符表,CPU可以对描述附表进行管理,DMA可以使用描述符表进行数据传输。(描述符表通常存储一些不连续的的内存区域的DMA总线地址及长度,这些地址和长度是通过散列表映射(dma_map_sg)得到的,详见Linux动态DMA映射一文。DMA拿到描述符表后,就可以一次访问最初散列表scatterlist描述的不连续的内存区了。)