文章目录
1、dma_alloc_coherent用法
通过dma_alloc_coherent接口可以申请连续的大块内存。
dma_addr_t dma_handle;
cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, gfp);
入参:
dev: ``struct device *``,可设为NULL
size: 想要申请的内存长度,单位为bytes
gfp: 分配内存时的标志位,如GFP_KERNEL等,在此不多介绍
出参
cpu_addr: 内存虚拟其实地址
dma_handle: 实际物理地址
2、问题
使用这个接口最大只能申请到4M的内存,申请超过4M时,报
swiotlb buffer is full (sz: 16777216 bytes)
错误。
3、解决方法
方法一,走CMA空间配置
3.1 内核配置
CONFIG_CMA
CONFIG_CMA
在内核配置
CONFIG_CMA=y
,dma分配默认会走cma,空间会大点。
cat /proc/meminfo |grep Cma 可查看设备上cma空间大小。
3.2 修改cma起始地址
按照3.1设置后,有时仍然报swiotlb错误.
参考
一个大佬的回答
,总结如下:
检查内核打印,给cma分配的地址是否在4G以上,dma_allocat_coherent() 要求地址低于掩码[(0x1 << 32)-1] = 0xFFFFFFFF。如果地址大于这个,它将回退到 swiotlb 缓冲区。
因此需要指定cma空间分配的地址,方法见3.3
3.3 设置cma空间(大小和地址等)
3.3.1 内核配置
配置
CONFIG_CMA=y
后,cma空间应默认为64M.
如果需要修改,先配置
CONFIG_DMA_CMA=y
打开DMA Contiguous Memory Allocator, 再配置
CONFIG_CMA_SIZE_MBYTES
大小就行。
相关配置:
CONFIG_DMA_CMA | |
CONFIG_CMA_AREAS | 配置 CMA 分配器区域数 |
CONFIG_CMA_SIZE_MBYTES | 配置 CMA 的大小 |
CONFIG_CMA_SIZE_SEL_MBYTES | 按 MB 配置 CMA 区域大小 |
CONFIG_CMA_SIZE_SEL_PERCENTAGE | 按物理内存的百分比配置 CMA 区域大小 |
CONFIG_CMA_SIZE_SEL_MIN | 优先选择最小的 CMA 体积 |
CONFIG_CMA_SIZE_SEL_MAX | 优先选择最大的 CMA 体积 |
CONFIG_CMA_ALIGNMENT CMA | 对齐大小 |
优点:
配置简单便捷。
缺点:
不能指定cma区域的地址。
3.3.2 cmdline配置
可通过cmdline配置cma空间属性。用法说明:
cma=nn[MG]@[start[MG][-end[MG]]]
[ARM,X86,KNL]
Sets the size of kernel global memory area for
contiguous memory allocations and optionally the
placement constraint by the physical address range of
memory allocations. A value of 0 disables CMA
altogether. For more information, see
include/linux/dma-contiguous.h
例如:
cma=128M /* 设置一个128M的cma空间 */
cma=128M@0x8000000 /* 设置一个128M的cma空间并指定起始地址为0x8000000 */
cma=256M@0-4G /* 在0-4G范围内设置一个256M的cma空间 */
cma=256M@0-4G
可解决上述dma_allocat_coherent要求地址在0~4G范围内的问题。
优点:
可配置大小,也可指定地址。
可通过uboot传参,也可内核指定CMDLINE。
3.3.3 dts配置
在dts中添加cma节点,可指定cma大小和空间。
方法二,修改内核代码
4M这个限制是本身应该是分的普通内存的限制, 例如把配置加一个CONFIG_FORCE_MAX_ZONEORDER=16把最大限制改成128M也能解决。
23 /* Free memory management - zoned buddy allocator. */
24 #ifndef CONFIG_FORCE_MAX_ZONEORDER
25 #define MAX_ORDER 11
26 #else
27 #define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
28 #endif
29 #define MAX_ORDER_NR_PAGES (1 << (MAX_ORDER - 1))
"./include/linux/mmzone.h"
参考
For the problem when using swiotlb
dma_alloc_coherent failed on x86_64 but works on i686