解析Linux DMA mapping機制
說明:
Kernel版本:4.14
ARM64處理器,Contex-A53,雙核
使用工具:Source Insight 3.5, Visio
1. 概述
DMA(Direct Memory Access):直接存儲器訪問;
先看問題的引入:

Non-DMA:CPU直接與設(shè)備進行數(shù)據(jù)交互,CPU的負載會隨著數(shù)據(jù)的讀寫而增加;
DMA:CPU不參與數(shù)據(jù)的直接傳輸,DMA Controller負責(zé)Device與Memory之間的數(shù)據(jù)搬運,并以中斷信號的形式通知CPU;
可以看出,使用DMA的最大優(yōu)點是可以提高CPU的使用率;
2. address mapping
DMA涉及三種地址空間:
CPU虛擬地址:CPU使用的地址空間;
CPU物理地址:CPU使用的虛擬地址通過MMU轉(zhuǎn)換成物理地址;
總線地址:設(shè)備使用的地址空間;
CPU與Device看待地址的空間不一樣,看幾個示例:

A:Host bridge負責(zé)將Bus address映射到CPU的物理地址空間,可以通過
ioremap
?來使用,比如PCI/PCIe;B:設(shè)備使用的總線地址,可以通過IOMMU訪問到CPU的物理地址空間,由IOMMU來負責(zé)映射;
C:設(shè)備使用的總線地址與CPU的物理地址相同,不需要使用IOMMU進行地址轉(zhuǎn)換;
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦!?。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)? ?


2.1 cache coherence
DMA的操作,通常與cache相關(guān),先了解一下cache coherence:

cache coherence設(shè)備:設(shè)備之間的讀寫不需要關(guān)心cache的一致性問題,硬件將確保數(shù)據(jù)一致,比如連接在ARM CCI端口上的設(shè)備就是cache coherence設(shè)備;
non-coherence設(shè)備:需要額外的軟件操作(flush/invalidate)等操作來確保數(shù)據(jù)一致;
3. DMA mappings
Linux內(nèi)核中提供了兩種dma mapping的接口:Consistent mapping和Stream mapping。
3.1 Consistent DMA mappings
consistent mapping:對應(yīng)于cache-coherence設(shè)備,硬件確保device和CPU都能并行訪問數(shù)據(jù),并能看到彼此的更新,而不需要軟件的flush操作;
通常在驅(qū)動init時進行map操作,而在deinit時進行unmap操作;
通常在使用consistent dma mapping時,首先需要通過dma_alloc_coherent
接口來分配一段區(qū)域:

dma_alloc_coherent
用于分配coherent內(nèi)存,并返回對應(yīng)的虛擬地址;進行內(nèi)存分配時,存在三種方式:1)優(yōu)先從設(shè)備專用的dma池開始分配;2)無專用dma池,如果是dma-direct訪問,通過dma_direct_alloc分配,而底層是依賴于CMA來分配;3)使用IOMMU的設(shè)備,則通過iommu的操作函數(shù)集來分配;
3.1 device reserved
通常,可以為設(shè)備指定專用的dma coherent的區(qū)域,有以下的方式:

通過在設(shè)備樹中添加對應(yīng)的屬性值,驅(qū)動中可以調(diào)用
of_reserved_mem_device_init
最終完成dma區(qū)域的注冊;直接通過接口
dma_decleare_coherent_memory
調(diào)用來進行注冊;
3.2 dma pool
驅(qū)動中經(jīng)常面臨buffer的管理,可以使用dma pool機制來處理,大概的原理如下:

dma-pool以頁為單位來進行管理分配,可以通過添加多個dma池來使用;
dma-pool子系統(tǒng)的細節(jié)描述,需要另起一篇文章了;
3.2 Streaming DMA mappings
streaming mapping:對應(yīng)于non-coherence設(shè)備;
通常在單次DMA傳輸時進行map,在傳輸完成后進行unmap(除非調(diào)用了
dma_sync_XXX()
函數(shù));non-coherence設(shè)備,由于buffer不與其他數(shù)據(jù)共享cache line,通常會work better;
先看一下數(shù)據(jù)一致性問題:

dma to device時,需要將cache中的數(shù)據(jù)flush到memory中;
dma from device時,需要先將cache中的數(shù)據(jù)invalidate掉,避免CPU讀取的是原來的數(shù)據(jù);
dma_map_single
函數(shù)如下:

map操作時存在兩種方式,直接映射或使用iommu來完成映射;
dma_unmap_single
是逆操作:

從上述函數(shù)中可以看到,最終都會調(diào)用到arch相關(guān)的cache操作,這個與體系結(jié)構(gòu)是強相關(guān)的,以arm64為例:

最終的代碼將調(diào)用到匯編中,操作也較簡單,不再贅述了;
原文作者:LoyenWang
