内存管理-13-CMA内存-1-初探
基于msm-5.4
关键词 cma_alloc/cma_release MIGRATE_ISOLATE MIGRATE_CMA
一、概述
1. CMA简介
CMA(Contiguous Memory Alloctor)主要用于分配大块连续的物理内存。伙伴系统能分配的最大连续物理内存块是2^10,即一个pageblock大小4MB。为了提高内存的使用率,嵌入式平台上一般使用CMA.。
在伙伴系统的 free_area[] 链表中有一个 MIGRATE_CMA 类型,当不需要分配大块连续物理内存时,会将CMA内存通过伙伴系统提供给其它模块使用。
和DMA功能类似,CMA也是预先预留出一块连续的物理内存,给多媒体等需要大块连续物理内存的模块使用,如果不使用,CMA内存区也不会空闲着,通过伙伴系统的 free_page()/alloc_pages() 接口释放到伙伴系统,给其它模块使用。
当需要大块连续物理内存时,才会从CMA中分配,当CMA内存不足时,会将其释放到伙伴系统中的内存再拿回来,若属于CMA的一些页面已经被其它程序使用了,那么还要做一个迁移操作,腾出连续的区域。
简化理解,CMA区域就是对DMA区域的改进,在不使用时不让其空闲着,释放给伙伴系统给其它程序使用,当需要使用时再收回来,一方面满足了分配大块连续物理内存的需求,另一方面提高了内存的使用率。
2. CMA对外接口
cma_alloc(): 从CMA区域分配一大块连续的物理内存,若此内存已经被伙伴系统给别人使用了,还要回收回来用作CMA内存使用。
cma_release(): 当使用完CMA内存时,调用此函数将内存返还给CMA,CMA会调用伙伴系统的 free_page() 将这块内存释放到伙伴系统中,供其它模块使用。
实际上驱动很少通过 cma_alloc()/cma_release() 接口去分配CMA内存了,主要是通过DMA接口间接调用到CMA接口。
二、CMA实现
1. 数据结构
//mm/cma.h struct cma { unsigned long base_pfn; //此CMA区域起始页帧号 unsigned long count; //物理页的个数 unsigned long *bitmap; //表示此CMA中物理页的使用情况,每一个bit位表示一个物理页是否被分出去了还是空闲的。 unsigned int order_per_bit; //和上面bitmap配合使用,若为0则每个bit代表一个物理页,若为1则表示每个bit代表两个物理页 struct mutex lock; #ifdef CONFIG_CMA_DEBUGFS struct hlist_head mem_head; spinlock_t mem_head_lock; #endif const char *name; }; //mm/cma.c struct cma cma_areas[MAX_CMA_AREAS]; //17 CONFIG_CMA_AREAS+1 unsigned cma_area_count;
内核允许系统上有多个CMA区域,因此定义了一个数组 cma_areas[] 来保存所有的CMA区域,最大支持的个数可配置。
假设 order_per_bit=1 的,每个bit代表2个物理页,为0则每个bit代表1个物理页。
CMA初始化的时候,也会将CMA内存以pageblock 4MB的大小,插入到伙伴系统的 MIGRATE_CMA 类型的空闲链表中。只有用户向伙伴系统申请的内存是可移动的,才可能会去CMA链表上去拿内存。
伙伴系统在初始化的时候将内存分为4MB大小的pageblock,每个pageblock内存块都有物理页的类型,MIGRATE_UNMOVABLE、MIGRATE_MOVABLE 等。
假设DMA模块现在要去申请两个pageblock大小的连续物理内存,系统会找到CMA内存在伙伴系统中的位置,将其对应的pageblock设置为 MIGRATE_ISOLATE 类型(此时其上所有物理页都变为这个类型),表示此pageblock已经被当做CMA内存拿去使用了,伙伴系统不要分配它了。
2. CMA初始化
初始化有两种方式,一个是通过内核配置,另一个是通过设备树dts文件。
设备树中 reserved-memory 节点也是受伙伴系统管理的,但是不会通过伙伴系统的接口给用户使用了。保留下来这块内存用于专门的用途。compatible = "shared-dma-pool" 指定的可以用作CMA区域,内核根据这个字眼去解析然后初始化成一个struct cma结构。
/ { reserved-memory { #address-cells = <0x02>; #size-cells = <0x02>; ranges; phandle = <0x2ea>; linux,cma { compatible = "shared-dma-pool"; alloc-ranges = <0x00 0x00 0x00 0xffffffff>; reusable; alignment = <0x00 0x400000>; size = <0x00 0x2000000>; //32M linux,cma-default; phandle = <0x2eb>; }; qseecom_region { compatible = "shared-dma-pool"; alloc-ranges = <0x00 0x00 0x00 0xffffffff>; no-map; alignment = <0x00 0x400000>; size = <0x00 0x1400000>; //20M phandle = <0x02>; }; qseecom_ta_region { compatible = "shared-dma-pool"; alloc-ranges = <0x00 0x00 0x00 0xffffffff>; no-map; alignment = <0x00 0x400000>; size = <0x00 0x1000000>; //16M phandle = <0x03>; }; mem_dump_region { compatible = "shared-dma-pool"; alloc-ranges = <0x00 0x00 0x00 0xffffffff>; reusable; size = <0x00 0x1000000>; //16M phandle = <0x05>; }; cnss_wlan_region { compatible = "shared-dma-pool"; alloc-ranges = <0x00 0x00 0x00 0xffffffff>; reusable; alignment = <0x00 0x400000>; size = <0x00 0x6000000>; //96M phandle = <0x18b>; }; pmem_shared_region { reg = <0x01 0x66500000 0x00 0x51400000>; label = "pmem_shared_mem"; phandle = <0x2ec>; }; ramoops@800000000 { compatible = "ramoops"; reg = <0x08 0x00 0x00 0x200000>; record-size = <0x40000>; console-size = <0x40000>; pmsg-size = <0x40000>; }; }; };
注:设备树见 https://www.cnblogs.com/hellokitty2/p/18284414
内核启动log打印:
# cat kernel.txt | grep OF: <6>[ 0.000000] (0)[0:swapper]OF: reserved mem: initialized node qseecom_region, compatible id shared-dma-pool <6>[ 0.000000] (0)[0:swapper]OF: reserved mem: initialized node qseecom_ta_region, compatible id shared-dma-pool <6>[ 0.000000] (0)[0:swapper]OF: reserved mem: initialized node mem_dump_region, compatible id shared-dma-pool <6>[ 0.000000] (0)[0:swapper]OF: reserved mem: initialized node cnss_wlan_region, compatible id shared-dma-pool <6>[ 0.000000] (0)[0:swapper]OF: reserved mem: initialized node linux,cma, compatible id shared-dma-pool <6>[ 0.000000] (0)[0:swapper]Memory: 13692116K/14227228K available (18344K kernel code, 3350K rwdata, 12772K rodata, 4224K init, 2509K bss, 387656K reserved, 147456K cma-reserved)
注:147456K=144MB,和 cat /proc/pagetypeinfo 对应的上。设备树前面加起来是180MB,对应不上 #####。
三、CMA相关接口
/sys # find ./ -name "*cma*" ./kernel/debug/tracing/events/cma ./kernel/debug/tracing/events/cma/cma_alloc_busy_retry ./kernel/debug/tracing/events/cma/cma_alloc ./kernel/debug/tracing/events/cma/cma_alloc_start ./kernel/debug/tracing/events/cma/cma_release /sys/firmware/devicetree/base/reserved-memory # ls -l drwxr-xr-x 2 root root 0 2024-07-02 20:26 cnss_wlan_region drwxr-xr-x 2 root root 0 2024-07-02 20:26 linux,cma drwxr-xr-x 2 root root 0 2024-07-02 20:26 mem_dump_region drwxr-xr-x 2 root root 0 2024-07-02 20:26 pmem_shared_region drwxr-xr-x 2 root root 0 2024-07-02 20:26 qseecom_region drwxr-xr-x 2 root root 0 2024-07-02 20:26 qseecom_ta_region drwxr-xr-x 2 root root 0 2024-07-02 20:26 ramoops@800000000
注:还有/sys/kernel/debug 路径下的。
/sys/kernel/debug/cma # ls cma-cnss_wlan_r cma-linux,cma cma-mem_dump_re /sys/kernel/debug/cma/cma-cnss_wlan_r # ls alloc base_pfn bitmap count free maxchunk order_per_bit used
四、CMA内存释放
1. 概述
CMA内存也属于reversed类型的内存的一部分,但是其特殊之处在于它也会释放进伙伴系统。
reserved-dma-pool "linux,dma-default"
no-map
设备树中的这些属性告诉系统这块区域是预留DMA专用区域,不需要映射。
shared-dma-pool "linux,cma-default"
reusable
告诉系统这部分区域可以重新利用,可以交给伙伴系统去管理,使用时不够的话再回收回来就可以了。
2. 释放调用路径
start_kernel setup_arch setup_machine_fdt early_init_dt_scan early_init_dt_scan_memory //memblock.memory初始化 arm64_memblock_init early_init_fdt_scan_reserved_mem //memblock.reserved初始化 mm_init mem_init memblock_free_all //释放到伙伴系统 arch_call_rest_init rest_init kernel_init kernel_init_freeable do_basic_setup do_initcalls core_initcall cma_init_reserved_areas //cma内存释放 free_initmem //这里释放.init段内存
cma_init_reserved_areas() 用于将CMA区域的内存释放给伙伴系统。看其实现:
static int __init cma_init_reserved_areas(void) //cma.c { /* 一个循环,将所有的CMA区域都释放到伙伴系统中 */ for (i = 0; i < cma_area_count; i++) cma_activate_area(&cma_areas[i]); } static void __init cma_activate_area(struct cma *cma) { unsigned long base_pfn = cma->base_pfn, pfn = base_pfn; unsigned i = cma->count >> pageblock_order; struct zone *zone; /* 给CMA创建一个bitmap,记录page的使用情况。因为后续还可能要回收回来,因此需要知道哪些被伙伴系统分配出去了 */ cma->bitmap = bitmap_zalloc(cma_bitmap_maxno(cma), GFP_KERNEL); zone = page_zone(pfn_to_page(pfn)); /* 以pageblock为单位释放到伙伴系统中,此CMA区域有count个page就循环count>>pageblock_order次 */ do { unsigned j; base_pfn = pfn; for (j = pageblock_nr_pages; j; --j, pfn++) { if (page_zone(pfn_to_page(pfn)) != zone) goto not_in_zone; } init_cma_reserved_pageblock(pfn_to_page(base_pfn)); } while (--i); mutex_init(&cma->lock); #ifdef CONFIG_CMA_DEBUGFS INIT_HLIST_HEAD(&cma->mem_head); spin_lock_init(&cma->mem_head_lock); #endif return; } void __init init_cma_reserved_pageblock(struct page *page) { unsigned i = pageblock_nr_pages; struct page *p = page; do { /* 清除页面的reserved标记 */ __ClearPageReserved(p); set_page_count(p, 0); } while (++p, --i); /* 设置页面迁移类型 MIGRATE_CMA */ set_pageblock_migratetype(page, MIGRATE_CMA); /* 这是大页的释放,我们走else流程 */ if (pageblock_order >= MAX_ORDER) { ... } else { /* 设置page的引用计数 page->_refcount */ set_page_refcounted(page); /* 根据order将这个page添加到伙伴系统对应链表上 */ __free_pages(page, pageblock_order); } /* 更新伙伴系统中page的数量 */ adjust_managed_page_count(page, pageblock_nr_pages); }
posted on 2024-07-02 21:00 Hello-World3 阅读(433) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!