22

[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通过 的字段进行 DMA 同步(目前在 )sync_single_for_{cpu,device}dma_map_ops但到目前为止,我还没有找到这些操作的任何定义。

4

2 回答 2

17

我真的很惊讶没有人回答这个问题,所以在这里我们继续一个非 Linux 特定的答案(我对 Linux 内核本身的了解不足,无法更具体)......

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

那么,如何确保来自 DMA 区域的读取始终进入主存储器?

早在我们拥有 DMA 缓存侦听等花哨功能之前的一天,我们过去所做的就是通过一系列分解的阶段来对 DMA 内存进行流水线化处理,如下所示:

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

第 2 阶段:下次设备以新的 DMA 数据中断时,为所有可能访问这些块的 CPU(通常每个 CPU 运行其由本地内存块组成的自己的列表)。将所述段移动到“干净”列表中。

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

第 4 阶段:重复。

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

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

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

尼尔

于 2012-02-27T13:52:04.663 回答
6

也许有点迟到了,但是:

如果禁用缓存侦听,硬件将不再负责缓存一致性。因此,内核需要自己做这件事。在过去的几天里,我花了一些时间审查 [pci_]dma_sync_single_for_{cpu,device} 的 X86 变体。我没有发现任何迹象表明他们会做出任何努力来保持一致性。这似乎与 PCI(e) 规范中默认打开缓存侦听这一事实一致。

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

参考:

于 2013-02-06T13:50:28.007 回答