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

永久生效(二选一):

  1. 设备树 chosen 节点(system-user.dtsi 或主 dts):

    chosen {
        bootargs = "console=ttyPS0,115200 earlycon root=/dev/mmcblk0p2 rw rootwait cma=256M";
    };
    
  2. 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 分配失败时的错误打印(与日志吻合)
posted @ 2026-08-13 13:45  AlwaysComb  阅读(40)  评论(0)    收藏  举报