CMA的必要性
Continuous Memory Allocator
系统长时间运行后,可能碎片化,很难找到连续物理页。连续内存分配器CMA使得这种情况下分配大的连续内存成为可能。
当应用层申请大的内存时的解决方法
1,一种解决方案是为设备保留一块大的内存区,缺点是当设备驱动不使用时,内核其他模块也不能使用这块内存;
2,连续内存分配试图解决这个问题,保留一块大的内存区域。当设备驱动不使用的时候,内核其他模块可以使用,要求:只有申请可移动类型的页时可以借用;当设备驱动需要使用的时候,把已经分配的页迁移到其他地方,形成物理连续的大内存块。
CMA的使用方法
配置宏CONFIG_CMA,启动连续内存分配器;
配置宏CONFIG_CMA_AREAS,指定CMA区域的最大数量,默认值为7;
配置宏CONFIG_DMA_CMA运行设备驱动分配内存的连续内存分配器;
CMA区域分配全局CMA区域和设备私有CMA区域。全局CMA区域是所有设备共享的,设备私有CMA区域是由指定的一个或多个设备驱动使用。
配置CMA区域有3种方法
1)通过内核参数cma配置全局cma区域大小:
使用内核参数cma=nn[MG]@[start[MG]][-end[MG]]设置全局CMA区域的大小和物理地址范围
2)通过配置宏配置全局CMA区域的大小
CONFIG_CMA_SIZE_SEL_MBYTES表示兆字节;0表示禁用
CONFIG_CMA_SIZE_SEL_PERCENTAGE 表示指定物理内存容量的百分比,默认使用指定兆字节数的方式;0表示禁用
以上两种方式都可以使用内存传递参数启用CMA
3)通过设备树源文件/reserved-memory配置CMA区域
如果子节点的属性“compatible”的值是shared-dma-pool 表示全局CMA区域,否则表示设备私有CMA区域
设备树源码解析
reserved-memory {
#address-cells= <1>;
#size-cells= <1>;
ranges ;
linux , cma {
compatible= " shared-dma-pool" ;
reusable;
size= <Ox4000000>;
alignment= <Ox2000>;
linux , cma-defaul 七;
} ;
display_reserved: framebuffer@78000000 {
reg= <Ox78000000 Ox800000>;
) ;
multimedia reserved : multimedia@77000000
compatible= "acme, multimedia-memory" ;
reg= <Ox77000000 Ox4000000>;
} ;
) ;
分配三个cma区域:
1)节点名称linux,cma 大小64MB
2)帧缓冲设备专用CMA framebuffer,大小8M
3)多媒体专用CMA区域,节点multimedia-memory,大小64M
技术原理
连续内存分配器是 DMA映射框架的辅助框架,设备驱动程序不能直接使用连续分配器;
- 连续内存分配器是在页分配器的基础上实现的,提供接口cma_alloc用来从CMA区域分配页,cma_release用来释放CMA区域分配的也。
-
在连续内存分配器的基础上实现了DMA映射框架专用的连续内存分配器,简称DMA专用连续内存分配器。
dma_alloc_from_contiguous 用来从CMA分配
dma_release_from_contiguous 释放dma映射框架从DMA专用连续内存分配器分配或释放,为设备驱动程序提供的接口
dma_alloc_coherent ,dma_alloc_noncoherent 用来分配内存
dma_free_conerent ,dma_free_nocoherent用来释放内存设备驱动程序调用DMA映射框架提供的函数来分配或释放。
可配置多个CMA区域,内核定义一个数组来管理CMA区域struct cma cma_areas[MAX_CMA_AREAS];
unsigned cma_area_count;
页分配器为CMA区域的物理页定义了迁移类型MIGRATE_CMA1、创建CMA区域
内存管理子系统初始化,解析设备树二进制文件得到物理内存布局,使用memblock保存布局信息。
memblock的memory保存物理块的物理地址范围,
reserved类型保存预留内存块的物理地址范围
CMA区域属于保留内存块通过设备树配置CMA区域,解析如下:
setup_arch->arm64_memblock_init
early_init_fdt_scan_reaseved_mem
2、把CAM区域释放给页分配器memblock是内核初始化的时候使用的内存分配器,内核初始化完成后使用伙伴分配器管理物理页。内核初始化后,把空闲的内存释放给伙伴分配器,不会把保留的内存释放给伙伴分配器。CMA是保留内存。但需要把CMA区域的物理页交给伙伴分配器管理。
3、从CMA区域借用页
当设备驱动程序不使用CMA区域的时候,内核的其他模块可以借用CMA区域的物理页,页分配器只允许可移动类型从CMA类型
借用物理页。
1)从迁移类型分配页
2)如果分配失败,从备用类型借用物理页
如果指定迁移类型是可移动类型,首先从CMA类型借用物理页
从备用类型列表中的每个迁移类型借用物理页4、从CMA区域分配内存
当设备驱动程序需要使用CMA区域的时候,如果CMA区域的物理页已经被页分配器分配出去,需要把物理页迁移到其他地方。1)在CMA区域的位图中查找一个足够大的空闲页块
2)在位图中把物理页的分配状态设置为已分配
3)调用函数alloc_contig_range把页分配出去的物理页迁移到其他地方;
4)如果迁移失败,回到1),找下一个足够大的空闲页块尝试分配,知道分配成功或者尝试完所有的空闲页块;
5、释放CMA区域的内存
1) 检测物理页是否是CMA区域;
2)把物理页释放给页分配器;
3)在CMA位图中把物理页状态标记为空闲;
学习链接:kernel学习链接
参考
https://course.0voice.com/v1/course/intro?courseId=2&agentId=0