當前位置:
首頁 > 最新 > ARM平台,DMA導致的CACHE一致性問題解決

ARM平台,DMA導致的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與內存的數據「不一致性」錯誤。

來個外語的:

The cache.

This entity is essentially "memory state" as the flush architecture views it. In general it has the following properties:

It will always hold copies of data which will be viewed as uptodate by the local processor.

Its proper functioning may be related to the TLB and process/kernel page mappings in some way, that is to say they may depend upon each other.

It may, in a virtually cached configuration, cause aliasing problems if one physical page is mapped at the same time to two virtual pages, and due to to the bits of an address used to index the cache line, the same piece of data can end up residing in the cache twice, allowing inconsistancies to result.

Devices and DMA may or may not be able to see the most up to date copy of a piece of data which resides in the cache of the local processor.

Currently, it is assumed that coherence in a multiprocessor environment is maintained by the cache/memory subsystem. That is to say, when one processor requests a datum on the memory bus and another processor has a more uptodate copy, by whatever means the requestor will get the uptodate copy owned by the other processor.

(NOTE: SMP architectures without hardware cache coherence mechanisms are indeed possible, the current flush architecture does not handle this currently. If at at some point a Linux port to some system where this is an issue occurrs, I will add the necessary hooks. But it will not be pretty.)

以前接觸比較多的幾種內存機制:

帶CACHE的內存有兩種,寫回(writeback)、寫穿(writethrough);或者非CACHE空間。

搞DMA的時候發現非CACHE其實還可以細分兩種,一致(coherent),寫緩存(writecombine)。

其實後面這兩種,網上也找不到標準的翻譯方法,以前書上也沒具體介紹過,純屬自己瞎翻譯。

又一個因為複製粘貼導致的網路上流行結果:

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

千萬別被這句話誤導了,這句是錯誤的。因為,同樣的網頁里以下這段是正確的。

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

而 dma_alloc_writecombine 只禁止 C (Cacheable) 域.

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

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

CB 位的具體含義

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。

效率最高的寫回,其次寫通,再次寫緩衝,最次非CACHE一致性操作。

其實,寫緩衝也是一種非常簡單得CACHE,為何這麼說呢。

我們知道,DDR是以突發讀寫的,一次讀寫匯流排上實際會傳輸一個burst的長度,這個長度一般等於一個cache line的長度。

cache line是32bytes。即使讀1個位元組數據,也會傳輸32位元組,放棄31位元組。

寫緩衝是以CACHE LINE進行的,所以寫效率會高很多。

簡單寫了一個測試程序,驗證在ARM平台上的DMA一致性問題。

dst 地址0xab800000, dst 原始數據0x00,長度8

src 地址0xac200000, src 原始數據0x62,長度8

為保證驗證可靠,源地址是一致性內存,目的地址是寫回內存。

實驗步驟:

1. 讀兩個內存數據,用於後續比較,因為源地址是非CACHE的,所以修改一定會被寫到DDR上。

2. 對目的地址的讀8遍。

3. DMA拷貝源到目的地址。

4. 讀目的地址。

實驗結果說明,目的地址讀取到的值依然是0.

解釋:

第二步驟實際作用是為了在cache中,目的地址不被替換出cache。

我們知道一般OS採用的CACHE替換演算法都是基於局部性原理,當一個數據在連續時間內經常被操作,則對應的cache line就有更大概率被保留。

而DMA拷貝完成前,只要目的地址的CACHE LINE沒有被替換出去,則DMA完成後,CPU會訪問目的地址時,一定是原始數據。

也就是圖中最終結果,目的地址數據依然是原始數據0x00.

此時CPU訪問的是CACHE,但也有可能訪問的是DDR,甚至可能出現正確結果0x62。

1. 訪問CACHE

這是最簡單的了,因為CACHE LINE沒有被替換,CACHE HIT。所以,CPU不知道DDR數據已經變化,返回原始數據。

2. 訪問DDR

同樣好理解,因為如果之前設置了目的地址的數據,這時該cache line會成為dirty狀態。

在DMA完成之後,如果該cache line被替換到DDR上,那麼DDR的數據就又成了原始數據。

目的DDR的變化是 0x00 --> 0x62 (DMA) --> 0x00 (cache writeback).

3. 出現正確結果

這個涉及的內容最多。如果之前沒有設置目的地址的值,只是讀目的地址的值,則該cache line是乾淨的。

在DMA完成之後,該cache line如果被替換,則cache line里的0x00錯誤數據會被丟棄(因為是乾淨的,CACHE認為沒必要寫回)

而CPU在沒有CACHE HIT時,就會從DDR上讀到正確數據0x62.

這同樣說明2個道理,

第一,錯誤的DMA操作,導致了數據一致性的問題,但一定條件下,這個錯誤是不會被感知到的。

第二,即使讀到的100%是正確數據,你也不能從經驗斷定這個程序是對的,但只要有一次錯誤,就可以認為程序是錯誤的了。

解決方案:

先上結果

在DMA拷貝前,進行一次CACHE CLEAN,或CACHE FLUSH。

DMA拷貝完成之後,進行一次CACHE FLUSH。

就能很大概率避免一致性的問題。

這個解決方案不是標準的解決方案,使用一致性內存或寫緩衝內存,是無論何時100%正確的。

而這個方案,是一個很大概率的正確方案,在絕大部分合理場景下,可以以最高效率訪問內存。

其實,這個解決方案,大概率按照第一個實驗的條件3執行的。

為什麼要執行這2個操作呢。

DMA拷貝前,CACHE CLEAN保證了條件3中所述,目的地址的CACHE LINE是乾淨的。最大概率保證DMA傳輸時間內,不會再有寫回動作。

DMA拷貝完成後,CACHE FLUSH保證了,CACHE會重新構建,目的地址的值一定是從DDR上讀取最新數據。

而在拷貝過程中,CPU如果只是讀取目的地址,會直接訪問CACHE,而不訪問DDR。

這個方案使用起來很簡單,但一定要符合以下條件才能取得合理結果(適用於大部分使用DMA的情況):

1. DMA的數據一定是大數據搬移(至少是DCACHE的10倍以上)。因為僅1次CACHE FLUSH就會毀掉你整個DCACHE的數據,至少嵌入式平台也是128KB以上,這些數據訪問DDR重建也是要時間的。 (這一條保證使用DMA的時間效率)

2.在DMA拷貝期間,不會對目的地址進行寫操作。(這一條可以保證數據絕對正確)

GIF

老外英語版,請點擊閱讀原文

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 嵌入式ARM 的精彩文章:

嵌入式未來一段時間還是Linux天下,一位嵌入式er初探Linux kernel經驗

TAG:嵌入式ARM |