Linux PCIe驱动DMA Read正常但DMA Write失败排查求助
PCIe板卡Linux DMA Write流程故障排查
架构
Linux主机 <> PCIe接口 <> 外部PCIe板卡
DMA操作流程
- 通过IOCTL将用户空间缓冲区传递给驱动(缓冲区包含DMA Write的实际数据和DMA Read的目标位置)
- 将用户缓冲区转换为SG list
- 为DMA映射SG list
- 通过MBOX(寄存器机制)将DMA地址传递给板卡,由板卡侧DMA控制器执行操作
当前实现核心代码
用户空间缓冲区通过copy_from_user()复制到内核空间,核心处理代码:
descriptor = dma_alloc_coherent(&pdev->dev, sizeof(struct dma_descriptor), &dma_handle, GFP_KERNEL); start = (unsigned long)buffer; len = do_dma->length; offset = start & ~PAGE_MASK; num_pages = (offset + len + PAGE_SIZE - 1) / PAGE_SIZE; pages = kvmalloc_array(num_pages, sizeof(struct page *), GFP_KERNEL); pin_user_pages(start, num_pages, FOLL_WRITE | FOLL_LONGTERM | FOLL_FORCE, pages); sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); sg_alloc_table_from_pages(sgt, pages, num_pages, offset, length, GFP_KERNEL); dma_map_sgtable(&rtc_dev->pdev->dev, sgt, DMA_BIDIRECTIONAL, 0);
随后将SG list元素复制到struct dma_descriptor中传递给板卡。
故障现象
- DMA Read流程正常,可接收板卡发送的数据
- DMA Write流程异常:数据传递给板卡后,板卡DMA控制器持续处于忙碌状态,未触发完成中断;板卡在Windows环境下可正常工作,排除板卡软件问题
- 怀疑问题出在
pin_user_pages()的页面权限上,已尝试FOLL_WRITE | FOLL_FORCE | FOLL_LONGTERM等标志组合,未解决
排查方向建议
1. 页面权限与缓存一致性检查
- 确认
pin_user_pages()的标志匹配DMA方向:DMA Write是主机向板卡传输数据,板卡DMA需要读取用户页面,当前使用的FOLL_WRITE仅允许内核写入页面,需尝试替换为FOLL_READ | FOLL_LONGTERM,确保页面具备被外部设备读取的权限 - 手动执行缓存同步:在DMA启动前调用
dma_sync_sgtable_for_device(&pdev->dev, sgt, DMA_TO_DEVICE),确保用户空间的数据已刷入物理内存,避免板卡DMA读取到缓存中的无效数据
2. SG列表与DMA地址正确性验证
- 打印SG列表中每个元素的
dma_address和长度,对比板卡侧接收的地址是否一致,排查MBOX传递过程中的地址截断、字节序(32位/64位)匹配问题 - 检查
sg_alloc_table_from_pages的length参数:代码中前面定义的变量是len,但调用时用了length,确认是否为笔误,长度计算错误会导致DMA访问超出映射范围,引发控制器挂起
3. DMA映射类型与方向匹配
- 将
dma_map_sgtable的方向参数从DMA_BIDIRECTIONAL改为DMA_TO_DEVICE,明确匹配主机到板卡的传输方向,避免不必要的双向缓存同步操作,修改后代码:dma_map_sgtable(&rtc_dev->pdev->dev, sgt, DMA_TO_DEVICE, 0);
4. 页面pin的生命周期与释放检查
- 确认
pin_user_pages的返回值是否等于num_pages,排查是否存在部分页面pin失败的情况 - 确保DMA完成后调用
unpin_user_pages释放页面,同时检查FOLL_LONGTERM是否真的禁止了页面swap(可通过/proc/vmstat观察pgout指标确认)
5. 板卡DMA控制器的地址兼容性
- 确认板卡是否支持IOMMU映射地址:如果系统启用了IOMMU,
dma_map_sgtable返回的是IOMMU虚拟地址,若板卡需要直接物理地址,需在驱动中禁用IOMMU,或通过dma_set_mask_and_coherent设置正确的地址掩码
6. 错误处理完善
- 补充所有API的返回值检查:
kvmalloc_array、pin_user_pages、sg_alloc_table_from_pages、dma_map_sgtable的返回值是否正常,避免因资源分配失败导致的隐式错误
内容的提问来源于stack exchange,提问作者Akash Chandra
相关产品推荐
相关产品推荐

