Mic*_*ael 21 linux kernel linux-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似乎是DMA同步(目前通用的统一间接机制include/asm-generic/dma-mapping-common.h通过字段)sync_single_for_{cpu,device}的dma_map_ops,但到目前为止,我一直没能找到这些操作的任何定义.
Nia*_*las 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)有一个优化的例程来预先排序归零内存,所以操作系统基本上在后台存储内存并保持快速满足缓存 - 它会支付你保持新的内存请求低于缓存量,因为归零内存非常慢.我不知道在过去十年中使用硬件卸载内存清零产生的任何平台,因此您必须假设所有新鲜内存可能包含需要无效的有效缓存行.
我很欣赏这只能回答你问题的一半,但总比没有好.祝好运!
尼尔
也许有点过期,但是:
如果禁用缓存侦听,硬件将不再处理缓存一致性.因此,内核需要自己完成.在过去的几天里,我花了一些时间来审查[pci_] dma_sync_single_for_ {cpu,device}的X86变体.我没有发现他们为维持一致性所做的任何努力.这似乎与PCI(e)规范中默认启用缓存侦听的事实一致.
因此,如果要关闭缓存侦听,则必须在驱动程序中自行保持一致性.可能通过调用clflush_cache_range()(X86)或类似的?
参考文献: