You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PCIe驱动中dma_alloc_coherent内存无法被用户态mmap访问的问题

PCIe驱动dma_alloc_coherent内存mmap失效问题排查与解决

针对你遇到的dma_alloc_coherent()分配的内存mmap后用户态读到0xFFFF(IOMMU占位数据)、内核态正常的问题,结合6.1.0-25-amd64环境和FPGA PCIe回环场景,给出以下排查和解决思路:

1. 确认dma_mmap_coherent调用参数正确性

dma_mmap_coherent()是映射一致性内存的标准接口,参数错误会直接导致映射失败。需确保:

  • 传入的struct device*是PCIe设备的&pdev->dev,而非其他设备指针(比如NULL或平台设备指针)
  • 缓冲区的cpu_addr、dma_addr、size完全匹配dma_alloc_coherent()的返回值
  • 虚拟内存区域(vma)的权限和属性设置正确,必须添加VM_SHARED标记,并设置非缓存页属性

示例正确实现:

static int pcie_dev_mmap(struct file *filp, struct vm_area_struct *vma)
{
    struct pcie_dev *dev = filp->private_data;
    size_t map_size = vma->vm_end - vma->vm_start;

    if (map_size > dev->coherent_buf_size)
        return -EINVAL;

    // 设置共享属性与非缓存页保护
    vma->vm_flags |= VM_SHARED;
    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);

    return dma_mmap_coherent(&dev->pdev->dev, vma, dev->coherent_cpu_addr,
                             dev->coherent_dma_addr, dev->coherent_buf_size);
}

2. 排查IOMMU配置影响

6.1内核x86_64默认可能开启IOMMU,此时dma_alloc_coherent()返回的dma_addr是IO虚拟地址(IOVA),而非物理内存的真实物理地址。若驱动未正确关联设备的IOMMU域,dma_mmap_coherent()无法建立正确的映射关系,导致用户态读到占位数据。

排查要点:

  • 执行dmesg | grep -i iommu确认系统是否开启IOMMU
  • 驱动probe阶段必须设置正确的DMA掩码,确保设备支持64位地址:
int ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
if (ret) {
    dev_err(&pdev->dev, "Failed to set DMA mask\n");
    return ret;
}
  • 确保dma_alloc_coherent()调用时传入的是&pdev->dev,让内核关联到正确的IOMMU域

3. 理解kmalloc+dma_map_single的临时映射特性

你用kmalloc()+dma_map_single()配合virt_to_phys()能成功,是因为kmalloc()分配的是内核常规内存,virt_to_phys()获取的是真实物理地址,且你的系统可能关闭了CONFIG_STRICT_DEVMEM,允许用户态直接映射物理页。但这种方式不符合DMA规范:在IOMMU开启时,设备访问的是IOVA,用户态访问的是物理地址,存在缓存一致性风险,仅适合临时测试。

4. 检查内存对齐与页属性

  • dma_alloc_coherent()要求分配的size是PAGE_SIZE的整数倍,若未对齐,dma_mmap_coherent()可能返回错误
  • 用户态映射必须使用非缓存页属性,否则缓存会导致用户态读到过期数据或占位值

5. 内核版本特定问题验证

6.1内核中dma_mmap_coherent()对超大页、IOMMU域的处理有细节变化,可尝试:

  • 缩小缓冲区大小到单个页(4KB)测试,排除超大页映射问题
  • 检查内核配置是否开启CONFIG_DMA_DIRECT_REMAP,该选项会影响一致性内存的映射路径

验证步骤

  1. 在dma_alloc_coherent()调用后打印cpu_addr、dma_addr、virt_to_phys(cpu_addr),若dma_addr与真实物理地址不同,说明IOMMU处于开启状态
  2. 在mmap函数中打印vma->vm_pgoff、map_size,确认偏移和大小参数正确
  3. 检查dma_mmap_coherent()的返回值,若为负数,通过pr_err()打印错误码针对性排查

内容的提问来源于stack exchange,提问作者lazerbeam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 10:24:52