【问题标题】:Need help mapping pre-reserved **cacheable** DMA buffer on Xilinx/ARM SoC (Zynq 7000)需要帮助在 Xilinx/ARM SoC (Zynq 7000) 上映射预先保留的 **cacheable** DMA 缓冲区
【发布时间】:2016-01-19 18:27:25
【问题描述】:

我有一个基于 Xilinx Zynq 7000 的电路板,其 FPGA 架构中的外设具有 DMA 功能(在 AXI 总线上)。我们开发了一个电路并在 ARM 内核上运行 Linux。在硬件填充后,我们在从用户空间访问 DMA 缓冲区时遇到性能问题。

总结:

我们在启动时预先保留了一部分 DRAM,用作大型 DMA 缓冲区。我们显然使用了错误的 API 来映射这个缓冲区,因为它似乎没有被缓存,而且访问速度很糟糕。

由于性能糟糕,即使将其用作反弹缓冲区也会非常缓慢。 IIUC,ARM 缓存与 DMA 不一致,因此我非常感谢您对如何执行以下操作的一些见解:

  1. 将 DRAM 区域映射到内核虚拟地址空间,但要确保它是可缓存
  2. 确保将其映射到用户空间也不会产生不良影响,即使这需要我们自己的驱动程序提供 mmap 调用。
  3. 在执行 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


【解决方案1】:

您是否尝试过使用mmap() 方法将您的缓冲区重新映射为可缓存(通过remap_pfn_range())实现您自己的字符设备?

【讨论】:

    【解决方案2】:

    如果你想缓存映射,我相信你需要一个实现 mmap() 的驱动程序。

    为此,我们使用两个设备驱动程序:portalmem 和 zynqportal。在Connectal Project, 中,我们将用户空间软件和 FPGA 逻辑之间的连接称为“门户”。这些驱动程序需要 dma-buf,自 Linux 内核版本 3.8.x 以来,它对我们来说一直很稳定。

    portalmem 驱动程序提供一个 ioctl 来分配一个引用计数的内存块并返回与该内存关联的文件描述符。此驱动程序实现dma-buf sharing。它还实现了 mmap() 以便用户空间应用程序可以访问内存。

    在分配时,应用程序可以选择内存的缓存或非缓存映射。在 x86 上,映射始终被缓存。我们的 mmap() 实现目前从line 173 of the portalmem driver 开始。如果映射未缓存,它会使用 pgprot_writecombine() 修改 vma->vm_page_prot,启用写入缓冲但禁用缓存。

    portalmem 驱动程序还提供了一个 ioctl 来使数据缓存行无效并选择性地写回数据缓存行。

    portalmem 驱动程序不了解 FPGA。为此,我们使用了zynqportal 驱动程序,它提供了一个用于将转换表传输到 FPGA 的 ioctl,以便我们可以使用 FPGA 上的逻辑连续地址并将它们转换为实际的 DMA 地址。 Portalmem 使用的分配方案旨在生成紧凑的转换表。

    我们使用与pcieportal 相同的portalmem 驱动程序来连接PCI Express FPGA,无需更改用户软件。

    【讨论】:

    • 在 x86 上,这一切都很容易。对于 ARM,我一直在研究如何通过 AXI 协议实现缓存一致性,但我还没有弄清楚如何分配或映射可缓存的 DMA 缓冲区。有些建议(例如 memremap)是我有机会时必须测试的东西。
    • 对于 ARM,这取决于 AXI 如何连接到内存基础架构。例如,在 Zynq 上,有 5 个可编程的 AXI 端口可用于访问处理器的 DRAM。其中 4 个没有被处理器缓存窥探,因此缓存不一致。第 5 个 (ACP) 被窥探,因此可能是缓存一致的。内存区域需要映射到可缓存到用户进程中。
    【解决方案3】:

    Zynq 有 neon 指令,使用 neon 指令的 memcpy 汇编代码实现,使用在缓存边界上对齐(32 字节)将达到 300 MB/s 或更高的速率。

    【讨论】:

      【解决方案4】:

      我在udmabuf 上为此苦苦挣扎了一段时间,发现答案很简单,只需将dma_coherent; 添加到设备树中的条目中即可。我从这个简单的步骤中看到了访问时间的显着加快 - 尽管我仍然需要添加代码来使设备从/到设备的所有权转移时无效/刷新。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-14
        • 2016-09-12
        • 2010-11-18
        • 1970-01-01
        • 2017-07-14
        • 1970-01-01
        • 1970-01-01
        • 2011-01-06
        相关资源
        最近更新 更多