CMA 连续内存机制与实现详解
源码路径:
rk3588/kernel-6.1/mm/cma.c、cma.h、cma_debug.c、cma_sysfs.c
内核版本:Linux 6.1(RK3588)
文档目录:linuxDoc/mm/
目录
一、原理
1.1 问题背景
DMA、摄像头、显示、VPU 等硬件常要求 物理地址连续 的缓冲区。伙伴分配器在高 order 或碎片化严重时难以提供大块连续物理页。
1.2 CMA 思路
在启动早期 预留 一片物理连续区域:
- 平时:页作为 MIGRATE_CMA 类型进入 buddy,可被 可迁移 的普通分配使用
- 驱动需要时:从 CMA 池分配,通过 迁移(compaction) 腾出连续物理块并 pin 住
兼顾 系统内存利用率 与 驱动连续内存需求。
1.3 与 reserved-memory 区别
| 机制 | 平时是否进 buddy | 用途 |
|---|---|---|
| memblock reserve | 否 | 固件/驱动私有,内核不管理 |
| CMA | 是(可迁移页) | 动态借出,需要时收回 |
DT 中 linux,cma 与独立 reserved-memory 可并存。
二、实现方式
2.1 初始化
- DT /
cma=启动参数指定区域名、大小、对齐 cma_init_reserved_memblock()在 memblock 阶段 reservecma_init_reserved_areas():buddy 初始化后,将区域页标为 MIGRATE_CMA,建立 bitmap 跟踪 CMA 内分配
struct cma 字段:base_pfn、count、bitmap、name、order_per_bit。
2.2 分配:cma_alloc()
1 | struct page *cma_alloc(struct cma *cma, unsigned long count, |
要点:
- bitmap 防止重复分配同一段
alloc_contig_range(compaction.c):isolate + migrate 腾出[pfn, pfn+count)- 失败 -EBUSY 时换偏移重试
2.3 释放:cma_release()
清除 bitmap,free_contig_range 将页归还 CMA migratetype 的 buddy。
2.4 驱动 API
| API | 说明 |
|---|---|
dma_alloc_from_contiguous |
DMA 框架封装 |
cma_alloc / cma_release |
内核直接调用 |
devm_cma_alloc |
设备 managed |
2.5 与 page_alloc 协作
- 普通 GFP 不应长期占用 CMA 页;
is_migrate_cma()检查 NR_FREE_CMA_PAGES在 vmstat 中统计- 分配 CMA 时可能触发 全局 compact
三、关键数据结构与接口
| 符号 | 说明 |
|---|---|
cma_areas[] |
全局 CMA 区域数组 |
cma_get_base/size/name |
查询接口 |
cma_alloc / cma_release |
分配释放 |
alloc_contig_range |
底层连续化(compaction.c) |
四、RK3588 平台说明
4.1 典型配置
- defconfig
CONFIG_CMA=y - DT
reserved-memory下linux,cma默认池 - 显示/ISP/VPU 可能还有 命名 CMA 或固定 reserve
4.2 编解码场景
- drm_gem_cma、vb2 dma-contig 等从 CMA 取 buffer
- 多路编码同时分配大块 →
CmaFree下降、compact 频繁 → 延迟抖动 - 排查:
/proc/meminfo的 CmaTotal/CmaAllocated/CmaFree
4.3 失败处理
- 增大 DT CMA 或
cma=512M等启动参数 - 减少用户态 hoarding dmabuf
- 检查是否有驱动泄漏未
cma_release
五、调试与观测
| 手段 | 说明 |
|---|---|
/proc/meminfo |
Cma* 行 |
CONFIG_CMA_DEBUGFS |
debugfs 位图 |
CONFIG_CMA_SYSFS |
/sys/kernel/mm/cma/ |
| trace | trace/events/cma.h |
附录:源码索引
| 主题 | 文件 | 约略行号 |
|---|---|---|
| cma_alloc | cma.c | 438 |
| cma 初始化 | cma.c | 搜索 init_reserved |
正在加载留言…