DMA及cache一致性的學習心得 --dma_alloc_writecombine


來源:http://xmxohy.blog.163.com/blog/static/53469584201082734630713/

 

先理解cache的作用
CPU在訪問內存時,首先判斷所要訪問的內容是否在Cache中,如果在,就稱為“命中(hit)”,此時CPU直接從Cache中調用該內容;否則,就 稱為“ 不命中”,CPU只好去內存中調用所需的子程序或指令了。CPU不但可以直接從Cache中讀出內容,也可以直接往其中寫入內容。由於Cache的存取速 率相當快,使得CPU的利用率大大提高,進而使整個系統的性能得以提升。

Cache的一致性就是直Cache中的數據,與對應的內存中的數據是一致的。

DMA是直接操作總線地址的,這里先當作物理地址來看待吧(系統總線地址和物理地址只是觀察內存的角度不同)。如果cache緩存的內存區域不包括DMA分配到的區域,那么就沒有一致性的問題。但是如果cache緩存包括了DMA目的地址的話,會出現什么什么問題呢?

問題出在,經過DMA操作,cache緩存對應的內存數據已經被修改了,而CPU本身不知道(DMA傳輸是不通過CPU的),它仍然認為cache中的數 據就是內存中的數據,以后訪問Cache映射的內存時,它仍然使用舊的Cache數據。這樣就發生Cache與內存的數據“不一致性”錯誤。

題外話:好像2.6.29內核中,6410的總線地址和物理地址是一樣的,因為我在查看vir_to_bus函數的時候,發現在/arch/arm/linux/asm/memory.h中這樣定義: 
#ifndef __virt_to_bus
#define __virt_to_bus    __virt_to_phys
#define __bus_to_virt    __phys_to_virt
#endif
而且用source Insight搜索了一遍,沒有發現6410相關的代碼中,重新定義__vit_to_bus,因此擅自認為2.6內核中,6410的總線地址就是物理地址。希望高手指點。

順便提一下,總線地址是從設備角度上看到的內存,物理地址是CPU的角度看到的未經過轉換的內存(經過轉換的是虛擬地址)

由上面可以看出,DMA如果使用cache,那么一定要考慮cache的一致性。解決DMA導致的一致性的方法最簡單的就是禁止DMA目標地址范圍內的cache功能。但是這樣就會犧牲性能。

因此在DMA是否使用cache的問題上,可以根據DMA緩沖區期望保留的的時間長短來決策。DAM的映射就分為:一致性DMA映射和流式DMA映射。

一致性DMA映射申請的緩存區能夠使用cache,並且保持cache一致性。一致性映射具有很長的生命周期,在這段時間內占用的映射寄存器,即使不使用也不會釋放。生命周期為該驅動的生命周期

流式DMA映射實現比較復雜,因為沒具體了解,就不說明了。只知道種方式的生命周期比較短,而且禁用cache。一些硬件對流式映射有優化。建立流式DMA映射,需要告訴內核數據的流動方向。


因為LCD隨時都在使用,因此在Frame buffer驅動中,使用一致性DMA映射
上面的代碼中用到 dma_alloc_writecombine函數,另外還有一個一致性DMA映射函數dma_alloc_coherent

兩者的區別在於:
查看兩者的源代碼 

[cpp]  view plain copy
  1. /* 
  2. * Allocate DMA-coherent memory space and return both the kernel remapped 
  3. * virtual and bus address for that space. 
  4. */  
  5. void *  
  6. dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)  
  7. {  
  8. void *memory;  
  9.   
  10. if (dma_alloc_from_coherent(dev, size, handle, &memory))  
  11. return memory;  
  12.   
  13. if (arch_is_coherent()) {  
  14. void *virt;  
  15.   
  16. virt = kmalloc(size, gfp);  
  17. if (!virt)  
  18. return NULL;  
  19. *handle =  virt_to_dma(dev, virt);  
  20.   
  21. return virt;  
  22. }  
  23.   
  24. return __dma_alloc(dev, size, handle, gfp,  
  25. pgprot_noncached(pgprot_kernel));  
  26. }  
  27.   
  28. /* 
  29. * Allocate a writecombining region, in much the same way as 
  30. * dma_alloc_coherent above. 
  31. */  
  32. void *  
  33. dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp)  
  34. {  
  35. return __dma_alloc(dev, size, handle, gfp,  
  36. pgprot_writecombine(pgprot_kernel));  
  37. }  
  38.   
  39.   
  40. #define pgprot_noncached(prot)  __pgprot(pgprot_val(prot) &~(L_PTE_CACHEABLE | L_PTE_BUFFERABLE))  
  41. #define pgprot_writecombine(prot) __pgprot(pgprot_val(prot) &~L_PTE_CACHEABLE)  

再結合網上的資料(不過我感覺那文章寫的有些問題,我修改了一下),由上面代碼可以看出,兩個函數都調用了__dma_alloc函數,區別只在於最后一個參數。

dma_alloc_coherent 在 arm 平台上會禁止頁表項中的 C (Cacheable) 域以及 B (Bufferable)域。而 dma_alloc_writecombine 只禁止 C (Cacheable) 域.

 C 代表是否使用高速緩沖存儲器, 而 B 代表是否使用寫緩沖區。

這樣,dma_alloc_writecombine 分配出來的內存不使用緩存,但是會使用寫緩沖區。而 dma_alloc_coherent  則二者都不使用。

C B 位的具體含義
0 0 無cache,無寫緩沖;任何對memory的讀寫都反映到總線上。對 memory 的操作過程中CPU需要等待。
0 1 無cache,有寫緩沖;讀操作直接反映到總線上;寫操作,CPU將數據寫入到寫緩沖后繼續運行,由寫緩沖進行寫回操作。
1 0 有cache,寫通模式;讀操作首先考慮cache hit;寫操作時直接將數據寫入寫緩沖,如果同時出現cache hit,那么也更新cache。
1 1 有cache,寫回模式;讀操作首先考慮cache hit;寫操作也首先考慮cache hit。

這樣,兩者的區別就很清楚了。 

A = dma_alloc_writecombine(struct device *dev, size_t size,dma_addr_t *handle, gfp_t gfp);

含義:
A          : 內存的虛擬起始地址,在內核要用此地址來操作所分配的內存
dev      : 可以平台初始化里指定,主要是用到dma_mask之類參數,可參考framebuffer
size      : 實際分配大小,傳入dma_map_size即可
handle: 返回的內存物理地址,dma就可以用。

A和hanle是一一對應的,A是虛擬地址,而handle是總線地址。對任意一個操作都將改變寫緩沖區內容。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM