我目前正在使用 Xilinx XDMA 驱动程序(请参阅此处的源代码:XDMA Source),并试图让它运行(在您询问之前:我已经联系了我的技术支持联系人和 Xilinx 论坛到处都是有同样问题的人)。但是,我可能在 Xilinx 的代码中发现了一个障碍,这对我来说可能是一个交易破坏者。我希望有一些我没有考虑的事情。
首先,驱动程序有两种主要模式,AXI-Memory Mapped (AXI-MM) 和 AXI-Streaming (AXI-ST)。对于我的特定应用,我需要 AXI-ST,因为数据将持续从设备中流出。
驱动程序是为利用分散-聚集列表而编写的。在 AXI-MM 模式下,这是有效的,因为读取是相当随机的事件(即,没有数据流出设备,用户空间应用程序只是在需要时请求数据)。因此,建立 DMA 传输,传输数据,然后拆除传输。这是 get_user_pages()
、pci_map_sg()
和 pci_unmap_sg()
的组合。
对于 AXI-ST,事情变得很奇怪,源代码远非正统。驱动程序分配一个循环缓冲区,数据将源源不断地流入其中。这个缓冲区的大小通常有点大(我的设置为 32MB 左右),因为您希望能够处理用户空间应用程序忘记驱动程序的 transient 事件,然后可以稍后处理传入数据。
这就是事情变得不稳定的地方......循环缓冲区是使用 vmalloc32()
分配的,并且来自该分配的页面以与用户空间缓冲区在 AXI-MM 模式下相同的方式映射(即,使用 pci_map_sg()
接口(interface))。结果,因为循环缓冲区在设备和 CPU 之间共享,所以每次 read()
调用都需要我调用 pci_dma_sync_sg_for_cpu()
和 pci_dma_sync_sg_for_device()
,这绝对会破坏我的性能(我跟不上设备!),因为它适用于整个缓冲区。有趣的是,Xilinx 从未在他们的代码中包含这些同步调用,所以当我编辑他们的测试脚本以在退出前尝试多次 DMA 传输并且结果数据缓冲区已损坏时,我第一次知道我遇到了问题。
因此,我想知道如何解决这个问题。我考虑过重写代码以构建我自己的使用 pci_alloc_consistent()/dma_alloc_coherent()
分配的缓冲区,但这说起来容易做起来难。也就是说,代码被设计为假设在任何地方都使用分散-聚集列表(在分散-聚集列表和 FPGA 理解的内存描述符之间似乎有一个奇怪的专有映射)。
还有其他任何我应该注意的 API 调用吗?我可以通过某种翻译机制使用“单一”变体(即 pci dma_sync_single_for_cpu()
)来不同步整个缓冲区吗?或者,是否有一些函数可以使使用 vmalloc()
分配的循环缓冲区保持一致?
最佳答案
好吧,我明白了。
基本上,我对有关同步 API 的内核文档的假设和/或理解是完全错误的。也就是说,我在两个关键假设上错了:
- 如果缓冲区从未被 CPU 写入,则您不需要为设备同步。删除此调用使我的
read()
吞吐量翻了一番。 - 您不需要同步整个散点列表。相反,现在在我的
read()
调用中,我弄清楚哪些页面将受到copy_to_user()
调用的影响(即,将要复制的内容循环缓冲区)并且只同步那些我关心的页面。基本上,我可以调用类似pci_dma_sync_sg_for_cpu(lro->pci_dev, &transfer->sgm->sgl[sgl_index], pages_to_sync, DMA_FROM_DEVICE)
的东西,其中sgl_index
是我计算复制将开始,pages_to_sync
是数据在页数中的大小。
通过以上两项更改,我的代码现在可以满足我的吞吐量要求。
关于Linux PCIe DMA 驱动程序 (Xilinx XDMA),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48823171/