cache一致性問題

cache一致性問題

轉自http://blog.csdn.net/dianhuiren/article/details/6896129


這周碰到的一個問題,在解決的過程中得到不少高手的熱心幫助,把一些總結貼出來,歡迎大家指點。寫一個屏驅動的時候,需要把一塊內核中用kmalloc分配的內存映射到應用層中使用。這是一個很simple的需求,很輕鬆的就可以通過mmap搞定:

驅動部分代碼:
1698         if((mmap_addr = kmalloc(PAGE_SIZE, GFP_KERNEL)) == NULL){
1699                 ret = -ENOMEM;
1700                 goto out;
1701         }
1702         SetPageReserved(virt_to_page((mmap_addr)));

....

899         long length = vma->vm_end - vma->vm_start;
900 
901         if (length > PAGE_SIZE)
902                 return -EIO;
903 
904         if ((ret = remap_pfn_range(vma,vma->vm_start,virt_to_phys((void *)mmap_addr) >> PAGE_SHIFT,length,vma->vm_page_prot)) < 0) {
905                 return ret;

....


應用層通過
401         if( ( addr = mmap(NULL, 4096, PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0 )) == (void *)-1)
402         {
403                 perror("mmap error\n");
404                 return -1;
405         }
獲得地址, 然後往內寫入

這是一種通過普通內存設爲保留,當成io內存映射並map出去的一種方式。經過測試,發現大部分時候數據是對的,但偶爾數據會出錯。隨後嘗試不採用remap_pfn_range建立頁表,而是通過缺頁時返回分配頁的動態方式來處理,結果仍然一樣。

我們猜測是由於cache的原因導致的,於是在mmap的時候使其nocache,仍然無法解決。在實在沒有辦法的情況下,我們嘗試採用在內核讀取共享內存時執行flush_all_cache,果然,解決了問題,證明了確實是cache導致的。

那麼爲什麼一開始我們種種嘗試未能成功呢?因爲我們弄反了方向。我們以爲是應用層在讀cache,內核讀內存,實際上,由於採用remap_pfn_range,或者我們在mmap的時候指定了nocache的方式,應用層讀取這片vma的時候是根據其指定的nocache屬性去讀內存,而內核訪問kmalloc的時候卻是讀內存。如何讓內核也讀內存呢,很簡單,通過ioremap_nocache把kmalloc得到的地址再做一次映射,然後給內核用就可以了。

這就是傳說中的Cache Coherence ---緩存一致性。由於cache和內存在某些時候的不一致而導致的不同地址空間分別讀寫導致的問題。並不是所有體系結構都存在這種問題,比如x86.我曾經在x86上用過mmap並且使用良好,因爲x86的體系結構確保了緩存一致性:其總線監聽技術使當某片被cache的內存被其他請求操作時,會被立刻回寫,確保cache與內存的一致性。但這種監聽技術會帶來性能上的損耗,所以arm是由軟件來確保這個一致性的:一些時候,比如進程切換,必須通過flush整個cache獲得正確的內存訪問。

在這裏例子裏,我們是通過內核和應用都nocache的方式來進行內存共享的。那麼雙方能不能通過cache的方式來訪問呢?我們必須知道,有兩種cache:物理cache和邏輯cache,對於armv6以下的arm芯片,採用的是邏輯cache的方式,即cache在mmu之前,而armv6及以上的cpu,cache在mmu之後,cpu送出的要訪問的地址先通過mmu進行虛擬/物理的轉換,再送到cache。這意味着什麼呢?意味着對於我們的cpu(v5),當應用層用其地址空間的地址把共享區從內存加到cache後,內核同樣訪問這片區域的時候,也可以按照其kmalloc分配的地址將同一片內存中的數據加載到cache的另一行中,這就意味着一個內存塊會有2個cache拷貝,也就是傳說中的別名。簡單地說,如果你用arm11,由於使用物理cache,所以不會有問題,而arm9的話,就很麻煩了。

那爲什麼有時候訪問正確,有時候訪問錯誤呢?這就和cache的替換策略有關了。不像x86由於總線監視的原因,可以在相關內存被touch的時候回寫,arm的cache只有當是dirty,並且被cache輪轉策略選中需要換出的時候,纔會被回寫。所以有時候,某些加載共享內存的cache塊沒有被替換,而相應的內存塊又被內核加載到cache形成別名,錯誤就自然產生了。

這就是cache導致的問題。如果雙方採用一樣的cache策略,自然cache就是透明的,但是如果是不一樣的方式,那麼可能就會有問題。具體會有哪些問題,如前所述,就和cpu,體系結構有着密切的關係了。

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