ZYNQ AXI-DMA Linux调试记录
AXI DMA 基准测试内存分配失败 —— 调试记录
| 项目 | 内容 |
|---|---|
| 日期 | 2026-08-13 |
| 平台 | Xilinx Zynq-7000(console=ttyPS0,Petalinux,根文件系统 mmcblk0p2) |
| 相关组件 | axidma 内核驱动 + axidma_benchmark 用户态测试程序 |
| 问题类型 | DMA 缓冲区分配失败(内存不足) |
1. 问题现象
加载驱动成功,DMA 通道探测正常:
root@Always@ff:~/usr-app# insmod axidma.ko
axidma: loading out-of-tree module taints kernel.
axidma: axidma_dma.c: axidma_dma_init: 717: DMA: Found 1 transmit channels and 1 receive channels.
axidma: axidma_dma.c: axidma_dma_init: 719: VDMA: Found 0 transmit channels and 0 receive channels.
但运行基准测试时,接收缓冲区分配失败:
root@Always@ff:~/usr-app# ./axidma_benchmark
AXI DMA Benchmark Parameters:
Transmit Buffer Size: 7.91 MiB
Receive Buffer Size: 7.91 MiB
Number of DMA Transfers: 1000 transfers
cma: cma_alloc: alloc failed, req-size: 2025 pages, ret: -12
axidma: axidma_chrdev.c: axidma_mmap: 283: Unable to allocate contiguous DMA memory region of size 8294400.
axidma: axidma_chrdev.c: axidma_mmap: 285: Please make sure that you specified cma=<size> on the kernel command line, and the size is large enough.
Unable to allocate receive buffer from the AXI DMA device: Cannot allocate memory
关键点:发送缓冲区(TX)分配成功,接收缓冲区(RX)分配失败。
2. 定位过程
2.1 确定缓冲区大小来源
axidma_benchmark 的默认传输大小定义在 examples/axidma_benchmark.c:51-53:
#define IMAGE_SIZE (1920 * 1080)
#define DEFAULT_TRANSFER_SIZE ((int)(IMAGE_SIZE * sizeof(int)))
计算:1920 × 1080 × 4 = 8,294,400 字节 = 7.91 MiB,与日志中打印的 7.91 MiB 一致。
2.2 解读内核错误
cma: cma_alloc: alloc failed, req-size: 2025 pages, ret: -12
2025 页 × 4096 = 8,294,400 字节,正好是请求的 7.91 MiB。ret: -12=-ENOMEM(内存不足)。- 该错误来自 CMA(Contiguous Memory Allocator,连续内存分配器)。
2.3 确认分配路径
用户态 axidma_malloc() → mmap() → 驱动的 axidma_mmap() → dma_alloc_coherent()(driver/axidma_chrdev.c:280),最终从内核 CMA 池分配。
由于 TX 成功、RX 失败,说明 CMA 池大小只够装约一个缓冲区,装不下两个。
2.4 确认 CMA 配置
root@Always@ff:~/usr-app# cat /proc/cmdline
console=ttyPS0,115200 earlycon root=/dev/mmcblk0p2 rw rootwait
→ 命令行中没有 cma= 参数,使用内核默认值。
root@Always@ff:~/usr-app# cat /proc/meminfo | grep -i cma
CmaTotal: 16384 kB
CmaFree: 15916 kB
→ CmaTotal = 16384 kB = 16 MiB,印证是内核默认 CMA 大小(CONFIG_CMA_SIZE_MBYTES=16)。
3. 根因分析
- CMA 池总量仅 16 MiB。
- 两个缓冲区各 7.91 MiB,合计约 15.82 MiB,加上对齐开销与碎片,实际需求已逼近甚至超过 16 MiB 上限。
- 第一个(TX)7.91 MiB 分配成功,第二个(RX)再申请 7.91 MiB 时剩余空间不足,触发
-ENOMEM。
结论:内核命令行未指定 cma=,CMA 默认过小,无法同时容纳两个 7.91 MiB 的 DMA 缓冲区。
4. 解决方案
方案 A —— 增大 CMA(推荐,根本解决)
在内核命令行末尾添加 cma=256M:
console=ttyPS0,115200 earlycon root=/dev/mmcblk0p2 rw rootwait cma=256M
快速验证(U-Boot,一次生效):
setenv bootargs 'console=ttyPS0,115200 earlycon root=/dev/mmcblk0p2 rw rootwait cma=256M'
boot
永久生效(二选一):
-
设备树
chosen节点(system-user.dtsi或主 dts):chosen { bootargs = "console=ttyPS0,115200 earlycon root=/dev/mmcblk0p2 rw rootwait cma=256M"; }; -
U-Boot 环境变量(执行上面
setenv后):saveenv改完后重新编译 boot /
image.ub并烧录重启。
大小建议:基准测试两个 7.91 MiB 缓冲区,32 MiB 够用但偏紧,建议 64 MiB 起;若板载 RAM 为 512 MB / 1 GB,
cma=256M无压力。
方案 B —— 减小基准测试缓冲区(无需重启,快速验证)
axidma_benchmark 要求 Tx/Rx 大小同时指定:
./axidma_benchmark -i 4 -o 4 # 各 4 MiB
./axidma_benchmark -b 1048576 -s 1048576 # 各 1 MiB
5. 验证方法
修改后重启,确认:
cat /proc/cmdline | tr ' ' '\n' | grep cma # 应显示 cma=256M
cat /proc/meminfo | grep -i cma # CmaTotal 应为 262144 kB
./axidma_benchmark # 应能跑通并输出吞吐率
6. 附录:相关代码位置
| 文件 | 位置 | 说明 |
|---|---|---|
examples/axidma_benchmark.c |
L51-53 | 默认传输大小 1920×1080×4 = 7.91 MiB |
examples/axidma_benchmark.c |
L258-263 | Tx/Rx 大小必须同时指定的参数校验 |
driver/axidma_chrdev.c |
L280-281 | dma_alloc_coherent() 分配连续 DMA 内存 |
driver/axidma_chrdev.c |
L283-286 | 分配失败时的错误打印(与日志吻合) |

浙公网安备 33010602011771号