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描述的不連續的內存區了。)