DMAcaching一致性pipe理

我的问题是:如何在我的设备驱动程序中正确使用[pci_]dma_sync_single_for_{cpu,device}时,如何确定何时禁用caching监听?

我正在研究设备驱动程序,通过PCI Express(DMA)直接写入RAM,并关心pipe理caching一致性。 有一个控制位,我可以设置启动DMA时启用或禁用caching监听DMA,显然是为了性能我想离开caching监听如果可能的话。

在中断例程中,当切换DMA缓冲区时,在适当的时候调用pci_dma_sync_single_for_cpu()..._for_device() ,但是在32位Linux 2.6.18(RHEL 5)上,这些命令是扩展为无的macros。 ..这解释了为什么我的设备在此内核上禁用caching监听时会返回垃圾信息!

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

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

高速缓存侦听只是告诉DMA控制器发送高速缓存失效请求给所有的CPU进行DMA存储。 这明显增加了高速缓存一致性总线的负载,并且由于不是所有的CPU都会与发出探测的DMA控制器进行单跳连接,所以它会随着处理器的增加而特别严重。 因此,“什么时候禁用缓存监听是安全的”这个简单的答案是当被DMA进入的内存不存在于任何CPU缓存中或者其缓存线被标记为无效时。 换句话说,任何从DMA区域读取的尝试总是会导致从主存储器读取。

那么如何确保DMA区域的读取总是会进入主存?

早在我们拥有像DMA缓存侦听这样的奇特功能之前,我们曾经做过的事情就是通过一系列分解阶段来传输DMA内存,如下所示:

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

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

第三阶段:下一个DMA中断(当然,在上一个缓存失效已经完成之前,你肯定不会发生),从“干净”列表中取一个新的区域,并告诉设备下一个DMA应该进入。 回收任何脏块。

阶段4:重复。

尽管这是更多的工作,它有几个主要的优势。 首先,您可以将DMA处理连接到单个CPU(通常是主CPU0)或单个SMP节点,这意味着只有一个CPU /节点需要担心缓存失效。 其次,通过随着时间的推移划分操作并在高速缓存一致性总线上分散负载,给内存子系统更多的机会隐藏内存延迟。 性能的关键一般是尝试使CPU尽可能靠近相关的DMA控制器,并尽可能靠近CPU进入内存。

如果您总是将新的内存移交到用户空间和/或其他CPU中,只需在异步缓存失效管道的前端注入新获取的内存即可。 有些操作系统(对Linux不太确定)有优化的预定义内存的例程,所以操作系统在后台基本上都是零内存,并且保持一个快速的满足缓存 – 这将使你保持新的内存请求低于缓存的数量,因为归零内存是非常缓慢的。 我不知道在过去的十年中使用硬件卸载内存调零的任何平台,所以你必须假设所有的新鲜的内存可能包含有效的缓存行,需要失效。

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

尼尔

也许有点迟到,但是:

如果禁用缓存监听,硬件将不再考虑缓存一致性。 因此,内核需要自己做这个。 在过去的几天中,我花了一些时间回顾了[pci_] dma_sync_single_for_ {cpu,device}的X86变体。 我没有发现他们为保持一致性所做的任何努力。 这似乎与在PCI(e)规范中默认启用缓存侦听的事实一致。

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

参考文献: