linux CMA总结

【前言】

有时候对一个词记忆特别深刻的时候,那一定是你被这个词伤害过,我就是被CMA伤害过。。。

那是一个冬天,高通的面试官问我,知道什么是CMA吗,我心头翻涌出无数浪花,脑子也和进了水一样,CMA,我草,这是个啥,China Meteorological Administration 中国气象局?China Meat Association 中国肉类协会。

我心想,高通绝对不会知道我中华文化之深厚,Christian Management Association 基督教管理委员会,他一定想问我是否有信仰,那我就说 merry christmas 或者哈利路亚,这不就表明了我还是了解欧美文化的,结果面试官说:下一个。

我做错了什么。。。

1. 啥是CMA?

CMA: Contiguous Memory Allocator:说人话,linux连续的内存分配模块。

CMA是处于需要连续内存块的其他内核模块(例如DMA mapping framework)和内存管理模块之间的一个中间层模块,主要功能包括:

1、确定CMA内存的区域: 解析DTS或者命令行中的参数。

2、提供cma_alloc和cma_release两个接口函数用于分配和释放CMA pages。

3、记录和跟踪CMA area中各个pages的状态。

4、调用伙伴系统接口,进行真正的内存分配。

2. 为啥需要CMA?

盲猜:应该是有需求,有需求就会有市场!

是谁需要连续的物理内存?连linux强大的虚拟内存都无法满足?还让linux费心专门做了一个功能!我猜想这个需求一定很重要,难道是哈利路亚!

手机中能用到这么大的空间的器件是什么呢?

【猜想】

        第一个能想到的是display和camera,也只有这两个一个长满手机正面,一个充满手机背面的器件可能才有这么大的需求吧?

【验证】

        1300W像素的手机,占用内存:1300W * 3B = 26MB,我去还真是个内存大户,小米的1亿像素得多少,即使用到图像压缩技术,大实在是大!

        buddy系统你一次性只能最大分配4M,太少了吧。。。而且还会频繁调用buddy系统,这样碎片产生就不可避免。

        Page size大于4K的page统称为“huge page”,camera我需要一个26MB的huge page,而且还要连续,如果采用static分配,那么就会这块空间的浪费,其他人用不了,连续的空间哪有那么多。。

【CMA诞生】Michal Nazarewicz 麦克金发哥提出

        对于CMA 内存,当前驱动没有分配使用的时候,这些memory可以内核的被其他的模块使用(当然有一定的要求),而当驱动分配CMA内存后,那些被其他模块使用的内存需要吐出来,形成物理地址连续的大块内存,给具体的驱动来使用。

一句话描述:不是内存的生产者,只是内存的搬运工,解析和管理内存配置。

3. 菜鸡我实现的CMA?

1. 预留专属空间:RAM中预留一片专属的空间给CM(连续内存)器件使用,首先很方便的管理,其次可以和其他正常使用内存的user进行区分。此外,设置一个宏动态配置这片区域的size。

2. 灵活的接口:其他user来调用CMA的时,只需统一的接口,不需要关心其中细节,就可以正常使用CMA内存了。一般做common层。

3. 内存分配和回收算法:

  • 当空间请求时:因为是动态分配,当然面临的问题是如何把连续的空间分配给需要的人,因为不同的人需要的空间大小不同,所以要设计内存分配算法。
  • 当空间释放时:我们又需要内存的回收算法,让这些空间继续可以被其他人使用,而且还需要碎片少,连续性强。
  • 当空间不足的时:我们又需要将不常使用的内存分配给及时调用的user。

4. 大神实现的CMA?

大神实现的CMA:

 1. Reserved Area(memory type与reserve type对应)

  • Golbal CMA area(cmd line):初始化的时候,调用dma_contiguous_reserve函数,将指定的memory region保留给Global CMA area使用
  • per device CMA area:Reserved memory node(DTS)。 

2. 设计CMA数据结构:

struct cma cma_areas[MAX_CMA_AREAS];

struct cma {
    unsigned long   base_pfn; //CMA区域物理地址的起始页帧号
    unsigned long   count;   //该cma area内存有多少个page
    unsigned long   *bitmap; //0:表示内存free,1表示内存已经分配,位图,用于描述页的分配情况
    unsigned int order_per_bit;//0:1个page分配和释放 1:按2个page组成block来分配和释放
    struct mutex    lock;
};

PS:
1. base_pfn + count :该CMA area在内存在的位置
2. order_per_bit + count: bitmap指针指向内存的大小
3. MAX_CMA_AREAS: 1 + CONFIG_CMA_AREAS

3. Global CMA area创建:

        1.初始化定义:RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup;

        2.内核配置:CMA_SIZE_MBYTES和CMA_SIZE_PERCENTAGE

        3.命令行参数:cma=nn[MG]@[start[MG][-end[MG]]]

Per device CMA area操作:

1.DTS定义:调用:cma_init_reserved_mem。reserved memory节点必须有reusable属性,不能有no-map的属性。no-map属性的往往是专用于某个设备驱动,在驱动中会进行io remap,如果OS已经对这段地址进行了mapping,而驱动又一次mapping,这样就有不同的虚拟地址mapping到同一个物理地址上去。

reserved-memory {
        /* global autoconfigured region for contiguous allocations */
        linux,cma {
                compatible = "shared-dma-pool";
                reusable;
                size = <0 0x28000000>;
                alloc-ranges = <0 0xa0000000 0 0x40000000>;
                linux,cma-default;
        };
};

linux,cma {                                      
        compatible = "xxx";          
        reusable;                                  //默认属性
        size = <0x00800000>; /* 8M */              //通常size为8M的倍数
        alloc-ranges = <0x69000000 0x00800000>;    //起始地址和长度
        linux,cma-default;                         //默认使用这段区域
};

2. 注册过程和各自具体的驱动相关,都会用到dma_declare_contiguous。

4. 内存管理子系统进行初始化,CMA如何生效:

a. memblock确定系统的内存布局:memory type与reserved type的划分。

setup_arch--->
setup_machine_fdt--->
early_init_dt_scan--->
early_init_dt_scan_nodes--->
memblock_add

b. reserve block的建立与初始化:

DTS优先级高于命令行:

//built dts
setup_arch--->
arm64_memblock_init--->
early_init_fdt_scan_reserved_mem--->
__fdt_scan_reserved_mem--->
memblock_reserve

//init dts
setup_arch--->
arm64_memblock_init--->
early_init_fdt_scan_reserved_mem--->
fdt_init_reserved_mem--->
__reserved_mem_init_node

//golbal area CMD line
setup_arch--->
arm64_memblock_init--->
dma_contiguous_reserve

5. CMA的两种调用方法:

一种是CMA area是固定位置的,即参数给出了确定的起始地址和大小,这种情况比较简单,直接调用memblock_reserve就OK了。

另外一种情况是动态分配的,这时候,需要调用memblock的内存分配接口memblock_alloc_range来为CMA area分配内存。

6. free memory与reserved memory的伙伴系统处理:

// free memory handle
start_kernel--->
mm_init--->
mem_init--->
free_all_bootmem--->
free_low_memory_core_early--->
__free_memory_core

//reserved memory不会进入buddy system

//CMA初始化:在初始化的过程中,CMA area的内存会全部导入伙伴系统
为了避免这块reserved的内存在不用时候的浪费,
内存管理模块会将CMA区域添加到Buddy System中,用于可移动页面的分配和管理。
core_initcall(cma_init_reserved_areas);

7. CAM工作流程:

伙伴系统中页属性的概念Migrate type:MIGRATE_CMA,只有可移动的页面可以从MIGRATE_CMA的pageblock中分配。non-movable的内存是不可以从CMA区域分配的,保证了之后需要迁移时,可以将movable内存搬走。

如果MIGRATE_MOVABLE 页框链表没有页框后,将第一个访问MIGRATE_CMA 页框链表,在MIGRATE_CMA上分配物理页框。从上面可以看出,只有MIGRATE_MOVABLE才会fallback MIGRATE_CMA链表,分配CMA区域内存。

CMA分配 
CMA是通过cma_alloc分配的。cma_alloc->alloc_contig_range(..., MIGRATE_CMA,...),向刚才释放给buddy system的MIGRATE_CMA类型页面,重新“收集”过来。

__init cma_activate_area(struct cma *cma)
-->  cma->bitmap = kzalloc(bitmap_size, GFP_KERNEL); ----分配内存
-->  zone = page_zone(pfn_to_page(pfn)); ---找到page对应的memory zone

1. CMA area有一个bitmap来管理各个page的状态,这里bitmap_size给出了bitmap需要多少的内存。i变量表示该CMA area有多少个pageblock。

2. 遍历该CMA area中的所有的pageblock。

3. 获得所需pageblock:确保CMA area中的所有page都是在一个memory zone内,同时累加了pfn,从而得到下一个pageblock的初始page frame number。

4. 导入伙伴系统进行管理:将该pageblock导入到伙伴系统,并且将migrate type设定为MIGRATE_CMA。

5. 申请内存:cma_alloc用来从指定的CMA area上分配count个连续的page frame,按照align对齐,从bitmap上搜索free page的过程,一旦搜索到,就调用alloc_contig_range向伙伴系统申请内存。

6. 释放内存:分配连续内存的逆过程,除了bitmap的操作之外,最重要的就是调用free_contig_range,将指定的pages返回伙伴系统。

PS:

需要注意的是,CMA内存分配过程是一个比较“重”的操作,可能涉及页面迁移、页面回收等操作,因此不适合用于atomic context。因为CMA在迁移的过程中需要等待当前页面中的数据回写到磁盘之后,才会进一步的规整为连续内存供gpu/display使用,从而出现卡顿的现象。

#define CMA_NAME "video_cma@69000000"
struct page *cma_page;
static struct device *cma_dev;

np = of_find_node_by_path("/reserved-memory/video_cma@69000000");
struct cma *cma = find_cma_by_name(CMA_NAME);
cma_dev->cma_area = cma;

cma_page = dma_alloc_from_contiguous(cma_dev, alloc->count, 8, GFP_KERNEL);
alloc->vaddr = (unsigned long)page_address(alloc->cma_page);

ret = dma_release_from_contiguous(cma_dev, alloc->cma_page, alloc->count);


//Linux cmd
cat /proc/meminfo
CmaTotal: 16384 kB
CmaFree: 14592 kB

              total        used        free      shared  buff/cache   available
Mem:          31911        5546         560        1177       25805       24729
Swap:          2047         628        1419

在内存不断使用的过程中,会产生碎片,日益使用对内存消耗大的设备比如camera,video,将很难分配到足够大的内存。cma的技术提前分配一块专用内存,在不用的时候释放到伙伴系统中,在使用的时候通过页迁移腾出这块内存。

cma也是一种内存调节技术之一。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值