【发布时间】:2016-01-19 18:27:25
【问题描述】:
我有一个基于 Xilinx Zynq 7000 的电路板,其 FPGA 架构中的外设具有 DMA 功能(在 AXI 总线上)。我们开发了一个电路并在 ARM 内核上运行 Linux。在硬件填充后,我们在从用户空间访问 DMA 缓冲区时遇到性能问题。
总结:
我们在启动时预先保留了一部分 DRAM,用作大型 DMA 缓冲区。我们显然使用了错误的 API 来映射这个缓冲区,因为它似乎没有被缓存,而且访问速度很糟糕。
由于性能糟糕,即使将其用作反弹缓冲区也会非常缓慢。 IIUC,ARM 缓存与 DMA 不一致,因此我非常感谢您对如何执行以下操作的一些见解:
- 将 DRAM 区域映射到内核虚拟地址空间,但要确保它是可缓存。
- 确保将其映射到用户空间也不会产生不良影响,即使这需要我们自己的驱动程序提供 mmap 调用。
- 在执行 DMA 之前,显式地使缓存层次结构中的物理内存区域无效,以确保一致性。
更多信息:
在询问之前,我一直在尝试彻底研究这一点。不幸的是,这是一个ARM SoC/FPGA,这方面的信息很少,所以我必须直接询问专家。
因为这是一个 SoC,所以很多东西都是为 u-boot 硬编码的。例如,内核和 ramdisk 在将控制权交给内核之前被加载到 DRAM 中的特定位置。我们利用这一点为 DMA 缓冲区保留了 64MB 的 DRAM 部分(它确实需要那么大,这就是我们预先保留它的原因)。无需担心内存类型冲突或内核踩踏此内存,因为引导参数告诉内核它可以控制 DRAM 的哪个区域。
最初,我们尝试使用 ioremap 将此物理地址范围映射到内核空间,但这似乎将该区域标记为不可缓存,并且访问速度非常糟糕,即使我们尝试使用 memcpy 使其成为反弹缓冲区。我们使用 /dev/mem 也将其映射到用户空间,我将 memcpy 计时为大约 70MB/秒。
根据对该主题的大量搜索,似乎虽然有一半的人希望像这样使用 ioremap(这可能是我们得到这个想法的地方),但 ioremap 不应该用于此目的,并且应该使用与 DMA 相关的 API。不幸的是,似乎 DMA 缓冲区分配是完全动态的,我还没有弄清楚如何告诉它,“这里已经分配了一个物理地址 - 使用它。”
我看过的一个文档就是这个文档,但它过于 x86 和以 PC 为中心: https://www.kernel.org/doc/Documentation/DMA-API-HOWTO.txt
这个问题也出现在我的搜索顶部,但没有真正的答案: get the physical address of a buffer under Linux
查看标准调用,dma_set_mask_and_coherent 和 family 不会采用预定义地址,而是需要 PCI 的设备结构。我没有这样的结构,因为这是一个没有PCI的ARM SoC。我可以手动填充这样的结构,但在我看来,这就像滥用 API,而不是按预期使用它。
顺便说一句:这是一个环形缓冲区,我们将数据块 DMA 到不同的偏移量,但我们与缓存行边界对齐,因此不存在错误共享的风险。
感谢您提供的任何帮助!
更新:如果您以正常方式执行此操作,ARM 上似乎没有可缓存 DMA 缓冲区之类的东西。也许如果我不进行ioremap调用,该区域将不会被标记为不可缓存,但是我必须弄清楚如何在ARM上进行缓存管理,我无法弄清楚。问题之一是用户空间中的 memcpy 似乎真的很糟糕。是否有针对我可以使用的未缓存内存进行优化的 memcpy 实现?也许我可以写一个。我必须弄清楚这个处理器是否有霓虹灯。
【问题讨论】:
-
您使用的是哪个版本的 Linux 内核?您是否尝试过使用连续内存分配器?...
-
@TheCodeArtist "Linux (none) 3.17.0-xilinx-dirty #13 SMP PREEMPT Mon Aug 3 12:10:57 MDT 2015 armv7l GNU/Linux" 另外,我没看过 CMA .如果我可以正确映射此缓冲区,则不需要 CMA,除非完全不可能从预先保留的物理地址指定 DMA 缓冲区。
-
获取linux下缓冲区的物理地址(来自用户空间)stackoverflow.com/a/28987409/1163019。对于 DMA/未缓存的东西 afaik,老实说,我不记得所有的细节,而且我的不同任务没有成功,但我可以从代码中看到我正在尝试“dma_alloc_coherent”->“update vma->vm_page_prot as writethrough " -> dma_mmap_from_coherent -> remap_pfn_range。这个演示文稿也可能有帮助吗? events.linuxfoundation.org/sites/events/files/slides/…
-
@auselen 我不需要获取物理地址。我知道在启动时。我可以使用 /dev/mem 将它映射到用户空间。但是,如果我在内核中使用 ioremap,它会使空间未缓存,从而降低访问速度。 “dma_alloc_coherent” 将仅动态分配相对较小的部分,而我们已经有一个大的连续预分配缓冲区,我们需要 DMA 进入。我会检查演示文稿。谢谢。
-
您的内核是否足够新,可以拥有
memremap()?这可能是最适合这项工作的工具——这里讨论的其他事情是关于从内核内存中分配 DMA 缓冲区。ioremap()在语义上接近正确的方法,即将一些外部区域映射到内核地址空间,但会导致将其映射为设备内存,甚至不是正常的不可缓存。
标签: linux-kernel arm dma zynq soc