DMA-BUF Heaps 与 CMA:RK3588 连续内存模型怎么理解
在摄像头、RGA、NPU 和编码器之间共享图像时,经常会同时看到 DMA-BUF、DMA-BUF Heaps 与 CMA。它们不是三套并列的内存方案,而是位于不同层次。
三个概念的关系
- DMA-BUF:一个跨驱动、跨设备、跨进程共享 buffer 的通用句柄机制,用户态通常拿到一个 fd。
- DMA-BUF Heaps:用户态申请 DMA-BUF 的标准接口,设备节点通常位于
/dev/dma_heap/。 - CMA:内核用于获得较大块物理连续内存的后端机制之一。
因此,更准确的描述是:
text
应用通过 /dev/dma_heap/<heap> 申请
↓
DMA_HEAP_IOCTL_ALLOC 返回 dma-buf fd
↓
heap 选择自己的分配后端(其中可能使用 CMA)
↓
fd 被导入 V4L2 / RGA / OpenCL / MPPDMA-BUF Heaps 是分配接口,CMA 是可能的内核后端。 把二者说成“先用 DMA-BUF,再用 CMA”会混淆调用层次。
一个典型的申请与导入流程
cpp
int heap_fd = open("/dev/dma_heap/system-uncached", O_RDWR | O_CLOEXEC);
dma_heap_allocation_data data{};
data.len = buffer_size;
data.fd_flags = O_RDWR | O_CLOEXEC;
ioctl(heap_fd, DMA_HEAP_IOCTL_ALLOC, &data);
int dma_fd = data.fd;
// 将 dma_fd 放进 v4l2_buffer 的 planes,再以 V4L2_MEMORY_DMABUF 入队后续 RGA、OpenCL 或 MPP 如果支持 dma-buf import,就可以继续使用同一个 fd 描述的内存,而不必先复制到普通堆内存。
CmaFree: 0 kB 不一定等于 CMA 耗尽
板端排查时,一个常见误判是看到 /proc/meminfo 里的 CmaFree 为 0,就直接得出“连续内存已耗尽”。这个结论必须结合内核配置。
某些系统启用了类似 CONFIG_CMA_INACTIVE 的策略,CMA 页在空闲时可能以不同方式进入普通内存管理,此时 CmaFree 或 nr_free_cma 不能单独代表可分配余量。
更有价值的证据包括:
- CMA 分配成功/失败计数;
/sys/kernel/debug/dma_buf/bufinfo中的 owner 与 size;- camera、NPU、ISP、编码器重启时是否出现 allocation failure;
- 峰值负载或双 buffer pool 重叠时是否失败;
- 普通
MemAvailable是否被过大的保留区长期挤压。
如何估算生产容量
先做 buffer 预算,而不是只看一张静态内存截图:
text
总预算 = Σ(宽 × 高 × 每像素字节 × buffer 数)
+ stride/alignment
+ ISP/RGA/NPU/MPP 内部池
+ 重启或切换期间的短时重叠
+ 安全余量NV12 通常按约 1.5 byte/pixel 估算,但实际还要加 stride 和对齐。多相机系统还要考虑采集队列、矫正输出、算法输入和编码队列是否同时驻留。
容量选择的目标不是“越大越安全”。CMA 太小会在峰值或重启重叠时失败,太大则可能长期压缩普通 RAM。最终选择应来自真实负载下的 owner 分布、allocation failure 和重启测试。
板端检查顺序
- 确认使用的
/dev/dma_heap/<heap>; - 确认 V4L2 是否使用
V4L2_MEMORY_DMABUF; - 查看 DMA-BUF owner 和总量;
- 记录分配失败计数;
- 在完整负载下执行服务重启;
- 对照稳定态、峰值和重启重叠三个窗口判断余量。
真正需要回答的不是“CmaFree 为什么是 0”,而是:当前主链在最差运行窗口里,是否还能可靠获得它需要的 buffer。