dma
说明dma_alloc_coherent 相关的知识网络上有很多,将相关的知识收集汇总以便于后续遇到问题方便查找。这一次对dma_alloc_coherent进行学习的原因是在使用rapidio驱动时申请大于64M的DMA内存时,遇到多次申请无法申请到内存的情况(加大预留DMA内存不能解决)。进而学习一下相关知识看是否有方法能解决相关问题。
怎样让DMA申请大内存遇到的问题DMA无法申请大于32M的内存,MAX_ORDER 这个宏设置的太小,这个宏限制了一次请求所能分配的最大物理页数。如果申请5MB内存,MAX_ORDER 需要不小于12。决方法是修改menuconfig的CONFIG_FORCE_MAX_ZONEORDER定义。 内存物理内存分配alloc_pages 内存管理 能够申请多大的DMA内存,和对应内存个数可以看/proc/buddyinfo。 buddyinfo说明
Linux 内核内存管理 Linux CMA内存使用
深入理解Linux高端内存
mmzone.h中
/* Free memory management - zoned buddy allocator. */#ifndef CONFIG_FORCE_MAX_ZONEORDER#define MAX_ORDER 11#else#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER#endif#define MAX_ORDER_NR_PAGES (1 << (MAX_ORDER - 1)) 注意开启内核的DMA支持CMADMA使用CMA的设置,不开启设置DMA将不能使用CMA内存。在dma_alloc_coherent 调用时使用或上__GFP_HIGHMEM参数可以避免使用Normal 空间。
实际超大内存的使用不建议通过上面的方式处理对于dma framwork来说,当我们使能并且配置了CMA区域时会使用CMA进行内存分配,但是内核依然对于旧的实现方式进行了兼容,可以通过 CONFIG_HAVE_GENERIC_DMA_COHERENT 来进行配置。
对于generic dma coherent的实现方式,依然借用了dts中的reserved memory节点,只不过在其中会定义 no-map 属性,进而这块内存就从系统中剥离出来了,无法被伙伴系统所使用,但是可以在dma核心层通过remap的形式创建页表映射来使用它。
链接中的重点知识理解 空闲页和buddyinfo对应关系在“内存管理”的连接上有内存分配的原理如下说明: 分配器维护空闲页面所组成的块, 这里每一块都是2的方幂个页面, 方幂的指数称为阶. 阶是伙伴系统中一个非常重要的术语. 它描述了内存分配的数量单位. 内存块的长度是2^0,order , 其中order的范围从0到MAX_ORDER zone->free_area[MAX_ORDER]数组中阶作为各个元素的索引, 用于指定对应链表中的连续内存区包含多少个页帧.
数组中第0个元素的阶为0, 它的free_list链表域指向具有包含区为单页(2^0 = 1)的内存页面链表数组中第1个元素的free_list域管理的内存区为两页(2^1 = 2) 第3个管理的内存区为4页, 依次类推. 直到2^MAXORDER-1个页面大小的块buddyinfo说明中有如下图: ZONE的空间配置,但是对于ARM 可能没有ZONE DMA部分。ZONE_DMA,对于32位系统和64位系统表达的意义是不同的,ZONE_DMA32则只对64位系统有意义,对32位系统就等同于ZONE_DMA,没有单独存在的意义。
从上面介绍可以看出Buddy info对着应空闲页的状态。
有一个有意思的现象在使用dma_alloc_coherent申请一致性内存的时候,有时候如果DTS有大量预留内存被分频。第一次会申请64M大内存失败。如果再次运行程序申请又能正常申请,申请失败时报下面的错误。
vmap allocation for size XXXX failed: use vmalloc=XXX to increase size
查看ZONE区域发现有足够的内存空间,怀疑是系统的vmalloc 虚拟地址连续大小资源不足。该资源在系统启动的时候就存在了,在UBOOT阶段可以进行设置。
问题的原因查看虚拟内存大小( cat /proc/meminfo)
$ cat /proc/meminfo.....MemTotal: 1061096 kBMemFree: 987244 kBMemAvailable: 969068 kBBuffers: 0 kBCached: 45716 kBSwapCached: 0 kBActive: 1840 kBInactive: 24 kBActive(anon): 1840 kBInactive(anon): 24 kBActive(file): 0 kBInactive(file): 0 kBUnevictable: 45644 kBMlocked: 0 kBHighTotal: 828928 kBHighFree: 779192 kBLowTotal: 232168 kBLowFree: 208052 kBSwapTotal: 0 kBSwapFree: 0 kBDirty: 0 kBWriteback: 0 kBAnonPages: 1800 kBMapped: 2752 kBShmem: 72 kBSlab: 16740 kBSReclaimable: 9436 kBSUnreclaim: 7304 kBKernelStack: 624 kBPageTables: 144 kBNFS_Unstable: 0 kBBounce: 0 kBWritebackTmp: 0 kBCommitLimit: 530548 kBCommitted_AS: 3984 kBVmallocTotal: 245760 kB VmallocUsed: 0 kBVmallocChunk: 0 kBPercpu: 320 kBCmaTotal: 262144 kBCmaFree: 260280 kB......查看虚拟内存使用情况( cat /proc/meminfo)
$ cat /proc/vmallocinfo .....0x(ptrval)-0x(ptrval) 8298496 simplefb_probe+0x210/0x678 phys=0x70000000 ioremap //DTS 中的显示保留内存 dc_reserved0x(ptrval)-0x(ptrval) 266240 __devm_ioremap+0x4c/0x98 phys=0xfd500000 ioremap0x(ptrval)-0x(ptrval) 4198400 __devm_ioremap+0x4c/0x98 phys=0xf9000000 ioremap0x(ptrval)-0x(ptrval) 274432 deflate_comp_init+0x20/0x9c pages=66 vmalloc0x(ptrval)-0x(ptrval) 1052672 __devm_ioremap+0x4c/0x98 phys=0xf0300000 ioremap0x(ptrval)-0x(ptrval) 1052672 __devm_ioremap+0x4c/0x98 phys=0xf0400000 ioremap0x(ptrval)-0x(ptrval) 1052672 __devm_ioremap+0x4c/0x98 phys=0xf0000000 ioremap......在测试中发现使用的图像接口的接收缓存添加了no-map参数,有这个参数在会导致该段内存在启动的时候被系统使用ioremap将对应地址段占用,用ioremap会消耗虚拟地址范围导致没有虚拟地址分配给其他映射。
.....mipi_buffer: mipi_buffer@0x3EE00000 {compatible = "shared-dma-pool";reg = <0x3EE00000 0x08000000>;no-map;};camera_link_buffer: camera_link_buffer@0x46E00000 {compatible = "shared-dma-pool";reg = <0x46E00000 0x08000000>;no-map;};dc_reserved: dc_mem@0x70000000 {compatible = "shared-dma-pool";reg = <0x70000000 0x2000000>;no-map;};........ 解决办法一点相关的说明
vmalloc.h:201:#define VMALLOC_TOTAL (VMALLOC_END - VMALLOC_START)1.查看/proc/meminfo的vmalloc大小,在uboot启动参数中调大点。
setenv bootargs ‘mem=1G console=ttyAMA0,115200 mtdparts=hi_sfc:1M(boot),8M(kernel),20M(app),2M(nand),1M(param) slave_bhisilicon # 64M console=ttyAMA0,115200 vmalloc=500M’
2.或者修改KENERL 内部的,需要修改KENERL 的内容。
/kernel/arch/arm$ grep -nr "VMALLOC_END"include/asm/pgtable.h:47:#define VMALLOC_END0xff800000ULinclude/asm/pgtable-nommu.h:87:#defineVMALLOC_END0xffffffffULkernel/module.c:55:return __vmalloc_node_range(size, 1, VMALLOC_START, VMALLOC_END,mm/mmu.c:971: (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) {mm/mmu.c:1132:(void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);mm/mmu.c:1149:if (vmalloc_reserve > VMALLOC_END - (PAGE_OFFSET + SZ_32M)) {mm/mmu.c:1150:vmalloc_reserve = VMALLOC_END - (PAGE_OFFSET + SZ_32M);mm/mmu.c:1155:vmalloc_min = (void *)(VMALLOC_END - vmalloc_reserve);mm/init.c:552:MLM(VMALLOC_START, VMALLOC_END),mm/dump.c:30:{ VMALLOC_END,"vmalloc() End" },mm/iomap.c:39: (unsigned long)addr < VMALLOC_END)mm/pageattr.c:60: !in_range(start, size, VMALLOC_START, VMALLOC_END))mm/ioremap.c:123: sizeof(pgd_t) * (pgd_index(VMALLOC_END) -修改mmu.c的240 值为其他值能修改虚拟地址的大小,一般建议是1G->240,内存较大可以对应修改。
static void * __initdata vmalloc_min =(void *)(VMALLOC_END - (240 << 20) - VMALLOC_OFFSET);/* * vmalloc=size forces the vmalloc area to be exactly 'size' * bytes. This can be used to increase (or decrease) the vmalloc * area - the default is 240m.3.将DTS中预留内存中没有必要no-map的去掉no_map参数。
dma_alloc_coherent DMA内存申请学习笔记