Linux PCIe DMA驱动程序(Xilinx XDMA)

时间:2018-02-16 08:58:13

标签: linux driver fpga xilinx pci-e

我目前正在使用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),因为您希望能够处理用户空间应用程序忘记驱动程序的瞬态事件,然后可以解决传入的数据。

这里的事情变得很糟糕......循环缓冲区是使用vmalloc32()分配的,来自该分配的页面的映射方式与用户空间缓冲区处于AXI-MM模式的方式相同(即使用{ {1}}界面)。因此,由于循环缓冲区在设备和CPU之间共享,因此每{{}}}次呼叫都需要我拨打pci_map_sg()read(),这绝对会破坏我的性能(我无法跟上使用设备!),因为这适用于整个缓冲区。有趣的是,Xilinx从未在代码中包含这些同步调用,因此我首先知道在编辑测试脚本以在退出之前尝试多个DMA传输并且生成的数据缓冲区已损坏时,我遇到了问题。

结果,我想知道如何解决这个问题。我已经考虑过重写代码来构建我自己使用pci_dma_sync_sg_for_cpu()分配的缓冲区,但这说起来容易做起来难。也就是说,代码的架构是假设在任何地方都使用分散 - 收集列表(在分散 - 收集列表和FPGA理解的内存描述符之间似乎存在一个奇怪的专有映射。)

我应该注意其他任何API调用吗?我是否可以通过某种转换机制使用“单一”变体(即pci_dma_sync_sg_for_device())来同步整个缓冲区?或者,是否有一些函数可以使循环缓冲区分配pci_alloc_consistent()/dma_alloc_coherent()相干?

3 个答案:

答案 0 :(得分:4)

好吧,我明白了。

基本上,我对同步API的内核文档的假设和/或理解是完全错误的。也就是说,我在两个关键假设上错了:

  1. 如果CPU从未写入缓冲区,则无需同步设备。删除此调用会使我的read()吞吐量增加一倍。
  2. 您无需同步整个散点列表。相反,现在在我的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是数据在页数中的大小。
  3. 通过以上两项更改,我的代码现在符合我的吞吐量要求。

答案 1 :(得分:2)

我认为XDMA最初是为x86编写的,在这种情况下,同步功能什么都不做。

除非您修改循环缓冲区,否则您似乎不太可能使用单个同步变体。用一个缓冲区列表替换循环缓冲区对我来说似乎是一个好主意。您预先分配了许多此类缓冲区,并有一个要发送的缓冲区列表和一个供您重复使用的免费列表。

如果您正在使用Zynq FPGA,则可以将DMA引擎连接到ACP端口,以便FPGA内存访问将保持一致。或者,您可以将内存区域映射为未缓存/缓冲而不是缓存。

最后,在我的FPGA应用程序中,我将控制寄存器和缓冲区映射到应用程序进程中,并且只在驱动程序中实现mmap()和poll(),以便为应用程序提供更多的DMA执行灵活性。我通常实现自己的DMA引擎。

答案 2 :(得分:0)

Pete,我是驱动程序代码的原始开发人员(在XMDA X出现之前)。

ringbuffer始终是一个非正统的东西,确实意味着缓存一致系统,默认情况下禁用。它的最初目的是摆脱DMA(重新)启动延迟;即使有完全的异步I / O支持(即使在某些情况下使用零延迟描述符链接),我们也遇到了无法保证这种情况的用例,以及需要真正的硬件环缓冲/循环/循环模式的情况。

Linux中没有等效的ringbuffer API,因此它有点开放式编码。

我很高兴重新考虑IP /驱动程序设计。

你可以分享你的修复吗?