Linux記憶體管理 DMA和一致性快取

2021-08-16 05:47:38 字數 2688 閱讀 7559

cpu寫記憶體的時候有兩種方式:

1. write through: cpu直接寫記憶體,不經過cache。

2. write back: cpu只寫到cache中。cache的硬體使用lru演算法將cache裡面的內容替換到記憶體。通常是這種方式。

dma可以完成從記憶體到外設直接進行資料搬移。但dma不能訪問cpu的cache,cpu在讀記憶體的時候,如果cache命中則只是在cache去讀,而不是從記憶體讀,寫記憶體的時候,也可能實際上沒有寫到記憶體,而只是直接寫到了cache。

這樣一來,如果dma從將資料從外設寫到記憶體,cpu中cache中的資料(如果有的話)就是舊資料了,這時cpu在讀記憶體的時候命中cache了,就是讀到了舊資料;cpu寫資料到記憶體時,如果只是先寫到了cache,則記憶體裡的資料就是舊資料了。這兩種情況(兩個方向)都存在cache一致性問題。例如,網絡卡發包的時候,cpu將資料寫到cache,而網絡卡的dma從記憶體裡去讀資料,就傳送了錯誤的資料。

主要靠兩類apis:

1.一致性dma快取(coherent dma buffers)

dma需要的記憶體由核心去申請,核心可能需要對這段記憶體重新做一遍對映,特點是對映的時候標記這些頁是不帶cache的,這個特性也是存放在頁表裡面的。

上面說「可能」需要重新做對映,如果核心在highmem對映區申請記憶體並將這個位址通過vmap對映到vmalloc區域,則需要修改相應頁表項並將頁面設定為非cache的,而如果核心從lowmem申請記憶體,我們知道這部分是已經線性對映好了,因此不需要修改頁表,只需修改相應頁表項為非cache即可。

還有乙個介面dma_cache_sync(),可以手動去做cache同步,上面說dma_alloc_coherent()分配的是uncached記憶體,但有時給dma用的記憶體是其他模組已經分配好的,例如協議棧發包時,最終要把skb的位址和長度交給dma,除了將skb位址轉換為實體地址外,還要將cpu cache寫回(因為cache裡可能是新的,記憶體裡是舊的)。

貼出一種實現:

void dma_cache_sync(struct device *dev, void *vaddr, size_t size,

enum dma_data_direction direction)

}

呼叫這個函式的時刻就是上面描述的情況:因為記憶體是可cache的,因此在dma讀記憶體(記憶體到裝置方向)時,由於cache中可能有新的資料,因此要先將cache中的資料寫回到記憶體;在dma寫記憶體(裝置到記憶體方向)時,cache中可能還有資料沒有寫回,為了防止cache資料覆蓋dma要寫的內容,要先將cache無效。注意這個函式的vaddr引數接收的是虛擬位址

例如在發包時將協議棧的skb放進ring buffer之前,要做一次dma_to_device的flush。對應的,在收包後為ring buffer中已被使用的skb資料buffer重新分配記憶體後,要做一次dma_from_device的flush(invalidate的時候要注意cache align)。

還有一種針對可cache的記憶體做一致性的方式,就是流式dma對映。

, 相關介面為 dma_map_sg(), dma_unmap_sg(),dma_map_single(),dma_unmap_single()。

一致性快取的方式是核心專門申請好一塊記憶體給dma用。而有時驅動並沒這樣做,而是讓dma引擎直接在上層傳下來的記憶體裡做事情。例如從協議棧裡發下來的乙個包,想通過網絡卡傳送出去。

但是協議棧並不知道這個包要往**走,因此分配記憶體的時候並沒有特殊對待,這個包所在的記憶體通常都是可以cache的。

這時,內存在給dma使用之前,就要呼叫一次dma_map_sg()或dma_map_single(),取決於你的dma引擎是否支援聚集雜湊(dma scatter-gather),支援就用dma_map_sg(),不支援就用dma_map_single()。dma用完之後要呼叫對應的unmap介面。

由於協議棧下來的包的資料有可能還在cache裡面,呼叫dma_map_single()後,cpu就會做一次cache的flush,將cache的資料刷到記憶體,這樣dma去讀記憶體就讀到新的資料了。

注意,在map的時候要指定乙個引數,來指明資料的方向是從外設到記憶體還是從記憶體到外設:

從記憶體到外設:cpu會做cache的flush操作,將cache中新的資料刷到記憶體。

從外設到記憶體:cpu將cache置無效,這樣cpu讀的時候不命中,就會從記憶體去讀新的資料。

還要注意,這幾個介面都是一次性的,每次運算元據都要呼叫一次map和unmap。並且在map期間,cpu不能去操作這段記憶體,因此如果cpu去寫,就又不一致了。

同樣的,dma_map_sg()和dma_map_single()的後端實現也都是和硬體特性相關。

其他方式

上面說的是常規dma,有些soc可以用硬體做cpu和外設的cache coherence,例如在soc中整合了叫做「cache coherent interconnect」的硬體,它可以做到讓dma踏到cpu的cache或者幫忙做cache的重新整理。這樣的話,dma_alloc_coherent()申請的記憶體就沒必要是非cache的了。

dma記憶體一致性

核心的dma一般在平台初始化的時候已經分配好了。但是對於一些有內部dma的硬體ip,比如usb ip video加速ip,他們可能由ip廠商封裝好的,沒辦法繫結到cpu端,這時候在核心使用dma就要注意了,因為dma只認識實體地址哦。當然,辦法還是有的,look 以下來自 這兩天在做 dma 相關開...

DMA和cache一致性問題

cache原理 cpu快取 cache memory 是位於cpu與記憶體之間的臨時儲存器,它的容量比記憶體小的多但是交換速度卻比記憶體要快得多。快取的出現主要是為了解決cpu運算速度與記憶體 讀寫速度不匹配的矛盾,因為cpu運算速度要比記憶體讀寫速度快很多,這樣會使cpu花費很長時間等待資料到來或...

DMA和cache一致性問題

cache原理 cpu快取 cache memory 是位於cpu與記憶體之間的臨時儲存器,它的容量比記憶體小的多但是交換速度卻比記憶體要快得多。快取的出現主要是為了解決cpu運算速度與記憶體 讀寫速度不匹配的矛盾,因為cpu運算速度要比記憶體讀寫速度快很多,這樣會使cpu花費很長時間等待資料到來或...