DMA缓存一致性管理

时间:2011-08-20 14:14:11

标签: linux linux-kernel kernel dma

我的问题是:当我在设备驱动程序中正确使用[pci_]dma_sync_single_for_{cpu,device}时,如何确定何时可以安全地禁用缓存侦听?

我正在研究一种通过PCI Express(DMA)直接写入RAM的设备的设备驱动程序,并担心管理缓存一致性。在DMA启动DMA以启用或禁用缓存侦听时,我可以设置一个控制位,显然对于性能,我希望尽可能禁用缓存监听。

在中断例程中,我根据需要调用pci_dma_sync_single_for_cpu()..._for_device(),当切换DMA缓冲区时,但是在32位Linux 2.6.18(RHEL 5)上,事实证明这些命令是宏,扩展为空...这解释了为什么我的设备在此内核上禁用缓存侦听时会返回垃圾!

我已经浏览了内核源代码的历史,似乎直到2.6.25只有64位x86才有DMA同步的钩子。从2.6.26开始,似乎有一个通用的统一间接机制,用于通过include/asm-generic/dma-mapping-common.h的字段sync_single_for_{cpu,device}进行DMA同步(当前位于dma_map_ops),但到目前为止,我找不到任何定义这些行动。

2 个答案:

答案 0 :(得分:16)

我真的很惊讶没有人回答这个问题,所以我们在这里继续讨论一个非Linux特定的答案(我对Linux内核本身的知识不够具体)...

缓存侦听只是告诉DMA控制器向所有CPU发送缓存失效请求,以便将DMA内存发送到。这显然增加了缓存一致性总线的负载,并且它与其他处理器的关系特别严重,因为并非所有CPU都会与发出监听的DMA控制器进行单跳连接。因此,“当禁用缓存侦听是安全的”时,简单的答案是当任何CPU缓存中不存在被DMA存入的存储器或其缓存行被标记为无效时。换句话说,任何从DMAed区域读取的尝试都将始终导致从主存储器读取。

那么如何确保从DMA区域读取总是进入主存储器?

早在我们拥有DMA缓存侦听等奇特功能的前一天,我们过去常常做的是通过一系列分解阶段来管理DMA内存,如下所示:

阶段1:将“脏”DMA存储区添加到“脏且需要清理”的DMA存储器列表中。

第2阶段:下次设备使用新的DMA数据中断时,为可能访问这些块的所有CPU的“脏和需要清理”列表中的DMA段发出异步本地CPU缓存无效(通常每个CPU运行自己的由本地内存块组成的列表。将所述段移动到“干净”列表中。

阶段3:下一个DMA中断(当然你肯定在前一个缓存无效完成之前不会发生),从“清理”列表中获取一个新区域并告诉设备它的下一个DMA应该进入那。回收任何脏块。

第4阶段:重复。

尽管这是更多的工作,但它有几个主要优点。首先,您可以将DMA处理固定到单个CPU(通常是主CPU0)或单个SMP节点,这意味着只有一个CPU /节点需要担心缓存失效。其次,通过将操作随时间隔开并在缓存一致性总线上分散负载,您可以为内存子系统提供更多机会来隐藏内存延迟。性能的关键通常是尝试在CPU上尽可能靠近相关DMA控制器进行任何DMA,并尽可能靠近该CPU进入内存。

如果总是将新DMA内存交给用户空间和/或其他CPU,只需将新获取的内存注入异步缓存无效管道的前面。一些操作系统(不确定Linux)有一个优化的例程来预先排序归零内存,所以操作系统基本上在后台存储内存并保持快速满足缓存 - 它会支付你保持新的内存请求低于缓存量,因为归零内存非常慢。我不知道过去十年中使用硬件卸载内存归零的任何平台,因此您必须假设所有新内存都可能包含需要无效的有效缓存行。

我很欣赏这只能回答你问题的一半,但总比没有好。祝你好运!

尼尔

答案 1 :(得分:6)

可能有点过期,但是:

如果禁用缓存侦听,硬件将不再处理缓存一致性。因此,内核需要自己完成。在过去的几天里,我花了一些时间来审查[pci_] dma_sync_single_for_ {cpu,device}的X86变体。我没有发现他们为维持一致性所做的任何努力。这似乎与默认情况下在PCI(e)规范中打开缓存侦听一致。

因此,如果您要关闭缓存侦听,则必须在驱动程序中自己保持一致性。可能通过调用clflush_cache_range()(X86)或类似的东西?

参考文献: