3. 分配失败常见原因概览
在嵌入式 Linux 系统中,dma_alloc_coherent() 的失败往往不是单一原因造成的。理解常见失败原因的分类和特征,是快速定位问题的第一步。本章从内存资源、设备约束、IOMMU 配置和内存碎片四个维度,系统梳理分配失败的典型场景。
3.1 内存不足
系统物理内存总量不足是最直观的原因。当系统长时间运行、内存泄漏或并发分配请求过多时,dma_alloc_coherent() 可能因无法获得连续物理页而失败。尤其在高负载的嵌入式设备中,ZONE_DMA 或 ZONE_NORMAL 的可用页数可能迅速下降。
/proc/meminfo 中的 MemFree、DMAFree 和 MemAvailable 字段可以快速评估系统内存余量。若 MemFree 低于请求大小的 2 倍,失败风险显著增加。
3.2 CMA 区域耗尽
CMA(Contiguous Memory Allocator)是嵌入式系统中为大块连续 DMA 内存预留的专用区域。CMA 区域耗尽是最常见的失败原因之一,尤其在多媒体、摄像头、GPU 等需要大块连续内存的场景中。
| CMA 状态 | 典型现象 | 排查命令 |
|---|---|---|
| CMA size 过小 | 分配 4MB 以上连续内存时频繁失败 | cat /proc/cmainfo |
| CMA 被其他驱动占用 | 分配失败但系统总内存充足 | cat /sys/kernel/debug/cma/usage |
| CMA 碎片化 | 分配小尺寸成功,大尺寸失败 | cat /sys/kernel/debug/cma/bitmap |
3.3 设备 DMA 掩码限制
每个 DMA 设备通过 dma_mask 和 coherent_dma_mask 声明其可访问的物理地址范围。若掩码设置过小,内核将无法在指定地址范围内找到连续内存。
常见掩码配置错误包括:
- dma_mask 未初始化: 默认值为 0,导致内核认为设备无法访问任何 DMA 地址。
- coherent_dma_mask 小于实际需求: 例如设备需要 64MB 连续内存,但掩码只允许 32MB 范围。
- dma_set_mask_and_coherent() 调用失败: 驱动未检查返回值,导致掩码未生效。
// 正确的掩码设置示例
if (dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32))) {
dev_err(dev, "No suitable DMA mask available\n");
return -ENODEV;
}
3.4 IOMMU/SMMU 配置错误
IOMMU(I/O Memory Management Unit)或 SMMU(System MMU)负责将设备 DMA 地址转换为物理地址。配置错误会导致地址映射失败,进而使 dma_alloc_coherent() 返回错误。
dmesg 中是否有 IOMMU 相关的错误信息,如 iommu_map failed、domain attach failed 等。同时确认设备树中 iommus 属性是否正确指向对应的 IOMMU 实例。
典型 IOMMU 配置错误包括:
- IOMMU 未使能: 内核编译时未开启
CONFIG_IOMMU_SUPPORT或CONFIG_ARM_SMMU。 - Stream ID 冲突: 多个设备共享同一个 SMMU stream,导致地址空间重叠。
- 页表级别不足: 请求的连续内存大小超过 IOMMU 页表支持的映射范围。
3.5 内存碎片化
即使系统总内存充足,长期运行后的内存碎片化也可能导致 dma_alloc_coherent() 无法找到足够大的连续物理页块。这在嵌入式设备中尤为突出,因为其内存通常较小且分配/释放频繁。
- 使用
/proc/buddyinfo观察各 order 的可用页数,若高阶 order(如 order-4 以上)长期为 0,说明碎片化严重。 - 启用
CONFIG_COMPACTION内核选项,允许内存压缩合并碎片。 - 在设备树中增大 CMA 区域大小,或使用
cma=内核启动参数手动指定。 - 考虑使用
dma_alloc_noncoherent()配合一致性缓存维护,减少对大块连续内存的依赖。
3.6 原因对比速查表
| 失败原因 | 典型错误日志 | 快速诊断命令 | 优先级 |
|---|---|---|---|
| 内存不足 | page allocation failure: order:4 |
free -m |
高 |
| CMA 耗尽 | cma_alloc: alloc failed |
cat /sys/kernel/debug/cma/usage |
高 |
| DMA 掩码限制 | swiotlb buffer is full |
cat /sys/kernel/debug/dma-api/dma_mask |
中 |
| IOMMU 错误 | iommu_map failed |
dmesg | grep iommu |
中 |
| 内存碎片化 | page allocation failure: order:5 |
cat /proc/buddyinfo |
低(长期问题) |
在实际定位过程中,建议按照 内存不足 → CMA 状态 → DMA 掩码 → IOMMU 配置 → 碎片化 的顺序逐一排查,结合 dmesg 日志和 /proc 文件系统信息,可以快速缩小问题范围。