Skip to content

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 / MPP

DMA-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 页在空闲时可能以不同方式进入普通内存管理,此时 CmaFreenr_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 和重启测试。

板端检查顺序

  1. 确认使用的 /dev/dma_heap/<heap>
  2. 确认 V4L2 是否使用 V4L2_MEMORY_DMABUF
  3. 查看 DMA-BUF owner 和总量;
  4. 记录分配失败计数;
  5. 在完整负载下执行服务重启;
  6. 对照稳定态、峰值和重启重叠三个窗口判断余量。

真正需要回答的不是“CmaFree 为什么是 0”,而是:当前主链在最差运行窗口里,是否还能可靠获得它需要的 buffer。