为什么需要CMA?
CMA的设计思路
要实现大块连续物理地址内存的分配,首先面临的问题就是:大块连续的物理内存从哪里找?
这里有两个方案:
方案一:在系统开机时,从整机内存中划分一块出来作为 CMA 区域的预留。
方案二:当多媒体业务启动时,再清理出一块物理地址连续的空闲内存。
显然,第一个方案对多媒体业务是最可靠、最友好的。但是缺点也很明显,即在非多媒体业务场景,预留的 CMA 区域无法得到利用,整机内存利用率变低了。第二个方案对整机友好,因为没有 CMA 预留,整机内存利用率相比方案一更高。但是由于碎片化问题,随着系统运行获取连续物理内存越来越耗时,影响多媒体业务的性能,甚至当内存分配失败时,会导致业务不可用。
综上所述,问题本质上是整机内存利用率与 CMA 业务性能之间的平衡。那么,CMA 如果解决上述问题呢?它采取了折中的办法:
• 开机时,系统预留出 CMA 区域。
• 在 CMA 业务不使用时,允许其他业务有条件地使用 CMA 区域,条件是申请页面的属性必须是可迁移的。
• 当 CMA 业务使用时,把其他业务的页面迁移出 CMA 区域,以满足 CMA 业务的需求。
CMA是如何工作的?
了解了 CMA 的产生背景和设计思路后,下面来看看 CMA 具体是如何工作的。
1.前置知识
在了解 CMA 如何工作之前,我们需要先了解一点有关迁移类型和 pageblock 的知识。
在 buddy 系统中管理着多种迁移类型(migrate type)的内存页面,不同的迁移类型有各自的用途。举例来说, MIGRATE_MOVABLE 表示保存在其页面的数据是可以被迁移的,这种迁移类型在磁盘缓存、进程页等方面贡献很大。MIGRATE_UNMOVABLE 表示该页面不能被迁移。
pageblock 是一个物理上连续的内存区域,它包含多个页面。例如,在 ARM64 中,一个 pageblock 的大小是 4MB,一个页面的大小为 4KB,每个 pageblock 管理着 1024 个页面。
页面的迁移类型与其所在的 pageblock 的迁移类型一致。当 buddy 系统接收到内存分配请求时,会根据请求标识从对应迁移类型的 pageblock 中分配页面。
当对应迁移类型的 pageblock 中页面不够分配时,buddy 系统会从其他的 pageblock 中获取页面。在一定条件下,这可能会改变一个 pageblock 的迁移类型。比如,请求一个不可移动的页面时,如果系统从 MIGRATE_MOVABLE 迁移类型的 pageblock 中分配页面,会导致这个 pageblock 的迁移类型变成 MIGRATE_UNMOVABLE。这个过程被称为 fallback。
对于 CMA 区域的内存页面,它并不希望被改变迁移类型,因此 CMA 引入了一个新的 MIGRATE_CMA 迁移类型。这种迁移类型具有一个重要的性质:不是所有的内存申请都能到该区域分配内存,只有可移动的页面才能从 MIGRATE_CMA 类型的 pageblock 中分配。
掌握了以上知识之后,下面我们来看看 CMA 的三个主要工作流程:创建 CMA 区域、分配 CMA 内存和释放 CMA 区域内存。
2. 创建CMA区域
创建 CMA 区域的流程如下:
(1)在启动阶段, CMA 通知 memblock 保留一部分内存。
(2)CMA 将这部分内存设置为 MIGRATE_CMA 迁移类型,并其返还给 buddy 系统。
系统中可能存在多个 CMA 区域。这些被预留的 CMA 区域由 buddy 系统管理。buddy 系统可以利用这部分页面用于满足可移动页面的分配请求,这样既保证了预留内存是物理连续的,又提高了整机内存利用率。
3. 分配CMA区域内存
CMA 业务通过 cma_alloc() 接口申请 CMA 区域内存。CMA 区域内存的分配过程如下:
(1)根据分配请求选择要分配的页面范围。
(2)将涉及该页面范围的 pageblock 从 buddy 系统中隔离。由于 buddy 系统不会从 MIGRATE_ISOLATE 迁移类型的 pageblock 分配页面,所以 CMA 将 pageblock 的迁移类型由 MIGRATE_CMA 变更为 MIGRATE_ISOLATE,从而实现隔离。如图 1 所示。

(3)在隔离后,将分配范围内已使用的页面进行迁移处理。迁移过程就是将页面内容复制到其他内存区域,并更新对该页面的引用。
(4)迁移完成后,pageblock 的迁移类型从 MIGRATE_ISOLATE 恢复为 MIGRATE_CMA,最后将这些页面返回给调用者。如图 2 所示。

4. 释放CMA区域内存
CMA 业务通过 cma_release () 接口释放 CMA 区域内存。释放过程就是简单迭代这些页面,将它们一一放回 buddy 系统。
代码流程讲解
上面为大家介绍了 CMA 的工作原理,接下来我们来看看 CMA 的代码是如何实现的。
(备注:这部分只是讲解主流程,并未面面俱到展开。具体细节,读者可以自行查阅代码。)
1. CMA管理结构
首先让我们来看一下 CMA 区域在代码中是怎么表示的。
CMA 区域抽象为 struct cma 结构:
struct cma {unsigned long base_pfn; // 区域的起始页帧号unsigned long count; // 区域所包含的总页面数量unsigned long *bitmap; // 位图,用于描述区域的分配单元的分配情况unsigned int order_per_bit; // 表示位图中一个bit所代表的页面数量(2^order_per_bit)char name[CMA_MAX_NAME]; // 区域名称};extern struct cma cma_areas[MAX_CMA_AREAS]; // CMA区域数组extern unsigned cma_area_count; // CMA区域总个数
CMA 区域通过 bitmap 进行管理,每个 bit 表示 2^order_per_bit 个页面。bit 为 0 则表示空闲,为 1 表示已占用。
系统中存在多个 CMA 区域, cma_areas[MAX_CMA_AREAS] 表示 CMA 区域数组,cma_area_count 表示 CMA 区域总个数。
2. 创建CMA区域
简单介绍完 CMA 的管理结构,下面我们来看创建过程。
CMA 区域的创建常用两种方式:
start_kernel()`-|setup_arch()`-|arm_memblock_init()`-|dma_contiguous_reserve()`-|dma_contiguous_reserve_area()`-|cma_declare_contiguous()`-|cma_declare_contiguous_nid()`-|cma_init_reserved_mem()
2. 使用 DTS 方式
这里以共享 CMA 区域(cma-default)创建流程为例:
首先,使用 DTS(Device Tree Source)描述当前要创建的 CMA 区域。
Reserved-memory {#address-cells = <1>;#size-cells = <1>;ranges;/* global autoconfigured region for contiguous allocations */linux,cma {compatible = "shared-dma-pool";reusable;size = <0x04000000>;alignment = <0x2000>;alloc-ranges = <0x00000000 0x20000000>;linux,cma-default;};};
RESERVEDMEM_OF_DECLARE(dma, "shared-dma-pool", rmem_dma_setup);rmem_cma_setup()`-|cma_init_reserved_mem()
core_initcall(cma_init_reserved_areas);cma_init_reserved_areas()`-|for (i = 0; i < cma_area_count; i++)`-|cma_activate_area()`-|for (pfn = base_pfn; pfn < base_pfn + cma->count; pfn += pageblock_nr_pages)`-|init_cma_reserved_pageblock()`-|set_pageblock_migratetype(page, MIGRATE_CMA);|__free_pages(page, pageblock_order);
3.分配CMA区域内存
cma_alloc()`-|for (;;)`-|alloc_contig_range()`-|start_isolate_page_range()|__alloc_contig_migrate_range()|isolate_freepages_range()|undo_isolate_page_range()
cma_release()`-|free_contig_range(pfn, count);|cma_clear_bitmap(cma, pfn, count);
OpenHarmony对CMA的增强

问题2:部分Movable内存无法迁移导致cma_alloc失败

结束语
本文参考资料:
https://lwn.net/Articles/396707/
https://lwn.net/Articles/486301/
https://lwn.net/Articles/447405/
https://lwn.net/Articles/684611/
【转自OpenHarmony】




