LDD DMA訪問內存

DMA 直接內存訪問,

DMA是設備與內存之間不經過cpu直接傳輸數據的一種機制,CPU讀取設備數據每次需要經過讀取指令,執行指令,讀取數據的過程,所以有一部分時間花費在讀取指令和執行指令的過程;DMA在設備與內存之間傳輸數據時不需要執行指令,而且cpu在DMA傳輸數據的同時可以執行其他程序,極大的提高了計算機讀取設備數據的能力;


DMA讀取數據過程需要的設備有:DMA緩衝區,具有DMA能力的設備,DMAC(DMA控制器)

1,DMA緩衝區,DMA緩衝區是內存中存放DMA數據的內存空間,DMA緩衝區分配最大的困難是:DMA緩衝區需要在物理內存中連續的內存空間,即連續的物理頁幀;

      所以通常分配即使比128K還小的內存時也會分配失敗;

      爲了解決這個問題,通常採取6中措施:在內核引導時通過mem=31M 參數預留物理內存頂端的物理頁幀,

                                                                             通過在主板北條芯片中集成IOMMU單元來實現設備的總線地址虛擬化,設備在虛擬地址空間能夠映射連續的虛擬地址空間,而虛擬地址通過IOMMU映射的物理頁幀可以不連續;

                                                                             分散和聚集操作:

2,總線地址(設備使用的設備地址):

      CPU與總線複用地址線和數據線,所以CPU的地址空間與總線地址空間一致,在x86架構下面(地址總線位寬爲32位)都爲4G,總線地址爲物理地址,總線地址是從設備的角度來看總線的尋址空間;


3,DMA地址映射(物理地址和總線地址映射,這個物理地址是CPU訪問內存時通過地址總線傳輸的物理地址);

      Linux內核假設,CPU使用32位地址總線訪問內存,設備使用32位總線地址進行DMA操作,如果設備尋址位不同,則用dma_set_mask(device *dev,u64 mask)更新設備的尋址能力;

所申請內存的物理地址必須要在設備的dma_mask尋址範圍內(dma_mask表示與設備尋址能力對應的位)。爲了確保由kmalloc申請的內存在dma_mask中,驅動程序需要定義板級相關的標誌位來限制分配的物理內存範圍(比如在x86上,GFP_DMA用於保證申請的內存在可用物理內存的前16Mb空間,可以由ISA設備使用);

      Linux對DMA地址映射的實現分爲兩種情況,兩種情況通過對CPU緩存的處理來劃分,由於在DMA操作期間不訪問CPU緩存,如果對內存數據修改而CPU cache數據未修改,CPU就會產生錯誤的結果,因此Linux內核對映射分爲一下兩種情況:

         1》一致性映射:在DMA操作期間,是CPU cache失效,這樣就CPU讀取數據的時候就會直接從內存加載然後寫入cache,由於從內存直接加載數據,內存的訪問速度沒有cache快,所以就會有性能的損失;

               api接口:void *dma_alloc_coherent(device *dev,size_t  size,dma_addr_t dma_handle); 函數返回虛擬地址,在dma_handle中保存總線地址(DMA訪問內存的物理地址),將dma_handle寫入DMAC基地址寄存器;

                               這個函數負責兩方面操作:緩衝區分配和映射;

                              void dma_free_coherent(device *dev,void *cpu_addr,,size_t size,dma_addr_t dma_handle);

                               這個函數爲緩衝區釋放;

警告:內存一致性操作基於高速緩存行(cache line)的寬度。爲了可以正確操作該API創建的內存映射,該映射區域的起始地址和結束地址都必須是高速緩存行的邊界(防止在一個高速緩存行中有兩個或多個獨立的映射區域)。因爲在編譯時無法知道高速緩存行的大小,所以該API無法確保該需求。因此建議那些對高速緩存行的大小不特別關注的驅動開發者們,在映射虛擬內存時保證起始地址和結束地址都是頁對齊的(頁對齊會保證高速緩存行邊界對齊的)。

         2》流式映射:在DMA操作開始的時候,如果是讀操作,避免讀取到過期的數據,首先flush CPU cache,將CPU cache的數據寫入到內存裏面,然後在從內存裏面讀取最新數據,如果寫操作,則使CPU cache失效,這樣DMA操作完畢,CPU直接從內存讀取最新數據;

               api接口:dma_addr_t dma_map_single(device *dev,void *cpu_addr,size_t size,enum dma_data_direction dir);

                                 enum dma_data_direction 枚舉值:DMA_TO_DEVICE,DMA_FROM_DEVICE,DMA_BIDIRECTIONAL;

                     這個函數只負責映射,因此需要先分配緩衝區,然後將緩衝區void *cpu_addr傳人該函數來映射;

                              void dma_unmap_single(device *dev,dma_addr_t dma_handle,size_t size,enum dma_data_direction dir);

                    這個函數爲取消地址映射;


(需完善)。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章