dma_alloc_coherent 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支持CMA

DMA使用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 kB
MemFree:          987244 kB
MemAvailable:     969068 kB
Buffers:               0 kB
Cached:            45716 kB
SwapCached:            0 kB
Active:             1840 kB
Inactive:             24 kB
Active(anon):       1840 kB
Inactive(anon):       24 kB
Active(file):          0 kB
Inactive(file):        0 kB
Unevictable:       45644 kB
Mlocked:               0 kB
HighTotal:        828928 kB
HighFree:         779192 kB
LowTotal:         232168 kB
LowFree:          208052 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:          1800 kB
Mapped:             2752 kB
Shmem:                72 kB
Slab:              16740 kB
SReclaimable:       9436 kB
SUnreclaim:         7304 kB
KernelStack:         624 kB
PageTables:          144 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:      530548 kB
Committed_AS:       3984 kB
VmallocTotal:     245760 kB  
VmallocUsed:           0 kB
VmallocChunk:          0 kB
Percpu:              320 kB
CmaTotal:         262144 kB
CmaFree:          260280 kB
......

查看虚拟内存使用情况( cat /proc/meminfo)

$  cat /proc/vmallocinfo 
.....
0x(ptrval)-0x(ptrval) 8298496 simplefb_probe+0x210/0x678 phys=0x70000000 ioremap   //DTS 中的显示保留内存 dc_reserved
0x(ptrval)-0x(ptrval)  266240 __devm_ioremap+0x4c/0x98 phys=0xfd500000 ioremap
0x(ptrval)-0x(ptrval) 4198400 __devm_ioremap+0x4c/0x98 phys=0xf9000000 ioremap
0x(ptrval)-0x(ptrval)  274432 deflate_comp_init+0x20/0x9c pages=66 vmalloc
0x(ptrval)-0x(ptrval) 1052672 __devm_ioremap+0x4c/0x98 phys=0xf0300000 ioremap
0x(ptrval)-0x(ptrval) 1052672 __devm_ioremap+0x4c/0x98 phys=0xf0400000 ioremap
0x(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_END		0xff800000UL
include/asm/pgtable-nommu.h:87:#define	VMALLOC_END	0xffffffffUL
kernel/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参数。

  • 15
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值