天天看點

淺析錯誤:software IO TLB: coherent allocation failed for device

問題背景:i2c controller 在 probe 的時候,使用 dma_alloc_coherent 申請一塊可持續使用的 dma 傳輸 buffer。平台使用 DRAM 大小為 6GB,未出現錯誤。

平台 DRAM 大小從 6GB 擴充到 8GB,dma_alloc_coherent 報錯:"software IO TLB: coherent allocation failed for device "。

調查結果:

簡單的原因是,當設定dma_set_mask(&pdev->dev, DMA_BIT_MASK(33))時,dma_allocat_coherent()要求從CMA擷取的位址是0x0~0x1FFFFFFFF[(0x1 << 33)-1](8GB-1)範圍内。另CMA的區域範圍總是在記憶體的高位址範圍。

當實體記憶體大小低于8GB,比如6GB,整個位址空間包括soc 位址空間(0x0~0x3FFFFFFF) + 片外位址空間(0x10000000~0x1BFFFFFFF),CMA的區域範圍總是在記憶體的高位址範圍,從CMA擷取的位址擷取到的最大位址是0x1BFFFFFFF,就是在0x0~0x1FFFFFFFF範圍内的,這個時候全部操作合理合法,不會報錯;

而如果實體記憶體高于或等于8GB,假設是8GB,整個位址空間包括soc 位址空間(0x0~0x3FFFFFFF) + 片外位址空間(0x10000000~0x23FFFFFFF),CMA的區域範圍又總是在記憶體的高位址範圍,那麼從CMA擷取的位址就有可能高于0x1FFFFFFFF,這時就會導緻swiotlb_tbl_map_single報錯“swiotlb: coherent allocation failed”。

以上隻是簡單原因,很多細節還需要繼續學習追查,比如CMA/DMA/slab/swio等記憶體管理的關系。

參考連結:

宋寶華:那些年你誤會的Linux DMA(關于Linux DMA ZONE和API最透徹的一篇)

絕大多數的SoC目前都支援和使用CMA技術,并且多數情況下,DMA coherent APIs以CMA區域為申請的後端,這個時候,dma alloc coherent本質上用__alloc_from_contiguous()從CMA區域擷取記憶體,申請出來的記憶體顯然是實體連續的。這一點,在裝置樹dts裡面就可以輕松配置,要麼配置一個自己特定的cma區域,要麼從“linux,cma-default”指定的預設的CMA池子裡面取記憶體

stackoverflow : dma_alloc_coherent failed on x86_64 but works on i686

After more investigation, I think you may have the same rootcause as me, just list it out for your reference. The CMA allocation will start from the highest memory blocks, so if you have more that 3G memory, the last physical memory will be above 0xFFFFFFFF, that means the CMA's base address is above 4GB, but dma_allocat_coherent() requires the address is below the mask [(0x1 << 32)-1] = 0xFFFFFFFF, if the alloacted dma end address is bigger than 0xFFFFFFFF, it will fallback to the swiotlb buffer and then you will see the error you described. Please see the below memory map, the last 2G is above 4GB memory space.

the appropriate solution is to specify the range or end address of the CMA region to be below 4GB.

繼續閱讀