Rockchip RK3588 PCIe驱动开发:arm64 DMA映射操作指南 - 详解

Rockchip RK3588 PCIe驱动开发实战:arm64 DMA映射避坑与性能调优指南

你有没有遇到过这样的情况?

在RK3588上接了一块PCIe NVMe SSD,系统能识别,但跑 dd 命令时速度只有理论值的一半;
或者写了个FPGA采集卡驱动,DMA传输后读回来的数据全是0xAA——明明设备已经发了有效数据;
更糟的是,偶尔还会触发内核Oops,定位到 dma_map_single 返回了一个非法地址……

别急,这些“玄学”问题背后,几乎都指向同一个元凶: DMA映射配置不当

尤其是在Rockchip RK3588这类集成了SMMU的arm64平台中,传统的“物理地址直连”思维早已失效。本文将带你穿透Linux内核、IOMMU和硬件协同的迷雾,用一线调试经验告诉你: 为什么你的DMA不工作?怎么让它既稳定又高效?


一、从一次失败的DMA说起:你以为的“物理地址”,其实是个虚拟IO地址

我们先来看一个真实案例。

有位开发者在RK3588板子上接入一块自研的PCIe数据采集卡,驱动逻辑看似无懈可击:

void *buf = kmalloc(4096, GFP_KERNEL);
dma_addr_t phys = virt_to_phys(buf);  // 错!大错特错!
pcie_write_reg(pdev, DMA_ADDR_REG, phys);

结果呢?设备压根收不到数据, dmesg 里还飘着一行警告:

[    5.123456] dma-direct remap: failed to allocate at 0x00000000f0000000

问题出在哪?

关键就在于这句 virt_to_phys(buf) —— 它拿到的是 系统物理地址(PA) ,但在启用了SMMU的RK3588平台上,PCIe设备看到的根本不是这个地址!

实际流程是这样的:

CPU虚拟地址 (VA)
     ↓ (mmu)
CPU物理地址 (PA)
     ↓ (iommu/smmu)
IO虚拟地址 (IOVA) ← PCIe设备唯一可见的“地址”
     ↓ (页表翻译)
最终物理地址 (IPA → PA)

也就是说, 你必须通过内核提供的DMA API来获取那个设备真正能访问的IOVA ,而不是自己硬算。

✅ 正确做法:永远使用 dma_map_single() dma_alloc_coherent() 获取 dma_addr_t ,这才是设备该用的地址。


二、两种DMA映射模式:什么时候用Coherent?什么时候用Streaming?

Linux为arm64提供了两类DMA映射方式,选错一个,轻则性能下降50%,重则数据错乱。

1. 一致性DMA映射(Consistent Mapping)

适用于:控制寄存器共享内存、小块频繁交互缓冲区(≤4KB)

特点:
- 分配时自动保证cache一致性
- 内存通常来自CMA区域,分配稍慢
- 映射长期有效,适合常驻场景

典型调用:

void *vaddr;
dma_addr_t dma_handle;
vaddr = dma_alloc_coherent(&pdev-&g
posted @ 2026-03-05 14:26  gccbuaa  阅读(102)  评论(0)    收藏  举报