我目前正在使用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()
相干?
答案 0 :(得分:4)
好吧,我明白了。
基本上,我对同步API的内核文档的假设和/或理解是完全错误的。也就是说,我在两个关键假设上错了:
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
是数据在页数中的大小。通过以上两项更改,我的代码现在符合我的吞吐量要求。
答案 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 /驱动程序设计。
你可以分享你的修复吗?