多CPU服务器上FPGA PCIe设备DMA请求致Linux挂死问题排查
FPGA PCIe DMA 双CPU服务器挂死问题排查
问题描述
我正在开发基于FPGA的PCI Express(PCIe)设备,同时编写适配Linux系统的内核驱动。设备支持通过DMA模式向Linux主机内存写入数据,流程如下:
- 驱动分配128KB连续内存区域并获取对应的DMA(总线)地址;
- 驱动将该DMA地址写入FPGA PCIe设备;
- FPGA PCIe设备生成以该DMA地址为起始的Memory Write PCIe事务(驱动已设置Bus Master位)。
该方案在单CPU Ubuntu桌面PC上运行正常,但在搭载两颗物理Intel CPU的超微服务器上,设备触发Memory Write事务时系统直接挂死。
驱动相关代码
// 采用64位DMA寻址 if (!dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64))) printk(KERN_WARNING "%s: using 64bit DMA addressing\n", DRIVER_NAME); ... // 内存分配与映射 struct page *dma_pages; dma_pages = alloc_pages_node(NUMA_NO_NODE, GFP_ATOMIC | __GFP_NOWARN | __GFP_COMP | __GFP_MEMALLOC, mem_order); virt_addr = (unsigned long)page_address(dma_pages); // 此DMA地址将传入FPGA dma_addr = dma_map_single(&fpga_dev->pdev->dev, virt_addr, size, DMA_BIDIRECTIONAL);
已尝试的排查操作
- 确认PCIe链路正常,CPU可读写FPGA寄存器并向FPGA传输数据;
- 内核启动时禁用IOMMU(添加启动参数
iommu=off intel_iommu=off amd_iommu=off); - BIOS中禁用Intel VT-d;
- 尝试NUMA相关配置(禁用、启用,在不同节点分配内存);
- 更换PCIe插槽及对应CPU,当前插槽曾正常运行另一PCIe网卡。
后续排查思路
1. DMA地址有效性校验
- 打印
dma_addr的具体值,确认是64位有效地址,且未超出服务器PCIe设备的寻址范围; - 用
dma_mapping_error()检查dma_map_single()的返回值,确认映射是否成功,避免无效地址传入FPGA; - 尝试使用
dma_alloc_coherent()替代alloc_pages_node()+dma_map_single()的组合,该函数会直接分配并映射适合DMA的内存,减少手动操作的错误。
2. PCIe事务属性检查
- 确认FPGA生成的Memory Write事务的地址宽度是否为64位,部分双CPU服务器可能不支持32位DMA跨节点访问;
- 检查事务的**请求者ID(Requestor ID)**是否正确,双CPU系统中PCIe设备可能挂接在特定CPU的PCIe根桥下,错误的Requestor ID会导致地址路由失败;
- 验证事务的字节使能(Byte Enable)、**突发长度(Burst Length)**是否符合PCIe规范,部分服务器对非规范的突发事务兼容性较差。
3. 内存属性与缓存一致性
- 确认分配的内存是否为**非缓存(Uncached)或写合并(Write-Combining)**类型,双CPU系统中缓存一致性协议对DMA内存的要求更严格;
- 使用
set_memory_wc()将分配的内存设置为写合并属性,避免缓存导致的数据不一致或系统挂死; - 检查
dma_map_single()的方向参数,若仅需FPGA写入主机内存,使用DMA_FROM_DEVICE替代DMA_BIDIRECTIONAL,减少不必要的缓存同步操作。
4. 系统日志与硬件调试
- 启用内核调试日志(如
debug、pci=verbose启动参数),查看挂死前是否有内核报错或警告; - 使用FPGA的调试工具(如逻辑分析仪、JTAG)捕获PCIe事务,确认事务是否正确发出,以及主机是否响应;
- 尝试降低FPGA的DMA传输速率(如减小突发长度、降低时钟频率),排查是否因事务速率过高导致系统总线过载。
单CPU桌面与双CPU服务器的核心差异
1. NUMA架构差异
双CPU服务器通常采用NUMA(非统一内存访问)架构,每个CPU拥有独立的本地内存和PCIe控制器,内存访问存在本地/远程节点的差异;单CPU桌面为UMA(统一内存访问)架构,所有内存和PCIe设备共享同一总线。若DMA内存分配在非PCIe设备所在CPU的远程节点,可能导致地址路由失败或触发系统挂死。
2. PCIe拓扑与路由差异
双CPU服务器的PCIe插槽可能分别挂接在不同CPU的根桥下,设备的Requestor ID需要正确关联到对应CPU的内存控制器;单CPU桌面的所有PCIe设备共享同一根桥,路由逻辑更简单。部分双CPU服务器的PCIe链路支持更严格的事务规范,对非标准事务的容错性更低。
3. IOMMU与内存保护差异
双CPU服务器的IOMMU/VT-d功能通常更复杂,即使禁用后仍可能存在残留的内存保护机制;单CPU桌面的IOMMU功能相对简单,禁用后基本不会影响DMA操作。
4. 缓存一致性协议差异
双CPU系统采用更复杂的缓存一致性协议(如Intel QPI/UPI),DMA内存的缓存属性设置错误可能导致缓存同步失败,进而触发系统挂死;单CPU系统的缓存一致性逻辑更简单,对DMA内存的限制较少。
内容的提问来源于stack exchange,提问作者ya_urock
相关产品推荐
相关产品推荐

