摄像头、显示、部分网卡和编解码器都喜欢“物理连续”的大块内存。内核里这块通常走 CMA(Contiguous Memory Allocator):启动时划出一片区域,平时可以给可迁移页用,外设要 DMA 大缓冲时再挤出来。账算不好,常见两种后果:预留过大,用户态和文件系统一看可用内存少了一截,启动后期直接卡死或 OOM;预留过小,open 摄像头或申请 4K 缓冲时 dma_alloc_coherent / dma_alloc_attrs 失败,驱动返回 -ENOMEM,上层只看到“设备打不开”。

先把三笔数分开算。第一笔是硬件真正需要的缓冲:分辨率 × 像素格式字节数 × 队列深度,再加上对齐和 ISP/旋转可能要的中间帧。1080p NV12、队列 4 深,单路大约 8MB 量级;4K、多平面、多路同时开,会一下冲到几十上百 MB。第二笔是驱动和 IOMMU 路径的额外开销:有的平台还要描述符表、scatterlist 线性化失败后的回退缓冲。第三笔是系统自己要活下去的内存:page cache、网络、文件系统日志、GUI 或业务进程。CMA 不是“越大越稳”,它是从可移动内存里划走的额度。

设备树里常见 cma 或 reserved-memory 下的 shared-dma-pool,也有内核参数 cma=64M 这种一刀切。板级差异大时,更稳妥的是 reserved-memory 精确划区间,并在驱动里用 of_reserved_mem 绑定,而不是全局 CMA 把所有外设搅在一起。调试时看 /proc/meminfo 的 CmaTotal/CmaFree,再看 dmesg 里 cma: alloc failed 时的 order 和 available。分配失败先问两句:是不是同时有两路高分辨率在抢同一片池?是不是启动早期别的驱动已经把可迁移页钉死,CMA 挤不出来?

有 IOMMU/SMMU 的平台,不一定非要物理连续,但很多历史驱动仍然按连续缓冲写。这时盲目加大 CMA,只是在掩盖驱动没有走 sg 或不再映射的问题。量产建议是:按最坏同时打开的传感器组合算出峰值,留 20%–30% 余量,剩下的还给系统;用一次压测同时打开预览、录像和拍照,看 CmaFree 的谷值和是否出现 alloc failed。预留值和业务场景写进板级说明,后来的人就不用再靠猜。