DMA及cache一致性的学习心得
在framebuffer的prob函數中,用到了這樣一個函數,下面分析下它的作用
/*
*????s3c_fb_map_video_memory():
*???分配DRAM的緩存區給frame?buffer。
*???這個緩存區是一個non-cached,non-buffered的。
*???這片內存區域允許調色板和像素在寫入時不刷新cache緩存。
*???一旦這片區域重新映射,那么所有用來訪問video?memory的虛擬內存將會
*???對應另外一片新的區域,即另外一片物理地址
*/
int?__init?s3c_fb_map_video_memory(struct?s3c_fb_info?*fbi)
{
......
fbi->map_size_f1?=?PAGE_ALIGN(fbi->fb.fix.smem_len?+?PAGE_SIZE);
fbi->map_cpu_f1?=?dma_alloc_writecombine(fbi->dev,?fbi->map_size_f1,
&fbi->map_dma_f1,?GFP_KERNEL);
......
}
首先說說6410的DMA虛擬地址和物理地址的映射。2.6.29中,比2.6.24有一些出入
2.6.29內核中,在arch/arm/mm/dma-mapping.c?中實現了DMA映射的函數。其中?
#define?CONSISTENT_END????(0xffe00000)
#define?CONSISTENT_BASE????(CONSISTENT_END?-?CONSISTENT_DMA_SIZE)
CONSISTENT_ENT?是DMA虛擬地址的結束地址
CONSISTENT_ENT?是DMA虛擬地址的起始地址
而CONSISTENT_DMA_SIZE定義在/arch/arm/include/asm/memory.h?
/*
*?Size?of?DMA-consistent?memory?region.??Must?be?multiple?of?2M,
*?between?2MB?and?14MB?inclusive.
*/
#ifndef?CONSISTENT_DMA_SIZE
#define?CONSISTENT_DMA_SIZE?(SZ_8M?+?SZ_4M)?//+?chachi?-?SZ_2M????????
#endif
DMA的大小必須是2M的整數倍。且在2M和14M之間
原來定義的大小是2M,網上看到有些blog說不夠用,會報錯。考慮到將來如果移植攝像頭和音頻的話,將會占用不少DMA緩存,因此這里改到12M
由于在編寫驅動的時候,出了很多錯誤,所以走了不少彎路,也學到不少東西。因此在研究?dma_writecombine函數的時,有必要先了解一下cache的一致性問題。
參考書:《Linux設備驅動開發詳解》、《Linux設備驅動程序》第三版?
cache的一致性
先理解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
兩者的區別在于:
查看兩者的源代碼?
/*
*?Allocate?DMA-coherent?memory?space?and?return?both?the?kernel?remapped
*?virtual?and?bus?address?for?that?space.
*/
void?*
dma_alloc_coherent(struct?device?*dev,?size_t?size,?dma_addr_t?*handle,?gfp_t?gfp)
{
void?*memory;
if?(dma_alloc_from_coherent(dev,?size,?handle,?&memory))
return?memory;
if?(arch_is_coherent())?{
void?*virt;
virt?=?kmalloc(size,?gfp);
if?(!virt)
return?NULL;
*handle?=??virt_to_dma(dev,?virt);
return?virt;
}
return?__dma_alloc(dev,?size,?handle,?gfp,
pgprot_noncached(pgprot_kernel));
}
/*
*?Allocate?a?writecombining?region,?in?much?the?same?way?as
*?dma_alloc_coherent?above.
*/
void?*
dma_alloc_writecombine(struct?device?*dev,?size_t?size,?dma_addr_t?*handle,?gfp_t?gfp)
{
return?__dma_alloc(dev,?size,?handle,?gfp,
pgprot_writecombine(pgprot_kernel));
}
#define?pgprot_noncached(prot)??__pgprot(pgprot_val(prot)?&?~(L_PTE_CACHEABLE?|?L_PTE_BUFFERABLE))
#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是總線地址。對任意一個操作都將改變寫緩沖區內容。
1、頁對齊內存大小:dma_map_size?=?PAGE_ALIGN(MY_DATA_SIZE?+?PAGE_SIZE);
MY_DATA_SIZE是你想分配的大小.
2、調用
A?=?dma_alloc_writecombine(B,C,D,GFP_KERNEL);
含義:
A:?內存的虛擬起始地址,在內核要用此地址來操作所分配的內存
B:?struct?device指針,可以平臺初始化里指定,主要是dma_mask之類,可參考framebuffer
C:?實際分配大小,傳入dma_map_size即可
D:?返回的內存物理地址,dma就可以用。
所以,A和D是一一對應的,只不過,A是虛擬地址,而D是物理地址。對任意一個操作都將改變緩沖區內容。當然要注意操作環境。
參見S3C2410?LCD驅動中的函數s3c2410fb_map_video_memory(): unsigned?map_size?=?PAGE_ALIGN(info->fix.smem_len); info->screen_base?=?dma_alloc_writecombine(fbi->dev,?map_size,&map_dma,?GFP_KERNEL);總結
以上是生活随笔為你收集整理的DMA及cache一致性的学习心得的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 招行附属卡还不起/还不上怎么办
- 下一篇: 存本取息是什么意思?这么存更划算!