天天看点

浅析错误: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.

继续阅读