如何为可缓存的 PCIe BAR 做 mmap
Posted
技术标签:
【中文标题】如何为可缓存的 PCIe BAR 做 mmap【英文标题】:how to do mmap for cacheable PCIe BAR 【发布时间】:2012-06-28 22:52:22 【问题描述】:我正在尝试为 PCIe BAR 编写具有自定义 mmap()
函数的驱动程序,目的是使此 BAR 可缓存在处理器缓存中。我知道这不是实现最高带宽的最佳方式,而且写入的顺序是不可预测的(在这种情况下也不是问题)。
这类似于How would one prevent MMAP from caching values?中描述的内容
处理器是 Sandy Bridge i7,PCIe 设备是 Altera Stratix IV dev。板。
首先,我尝试在 CentOS 5 (2.6.18) 上执行此操作。我更改了 MTRR 设置以确保 BAR 不在不可缓存的 MTRR 内,并使用 io_remap_pfn_range()
并清除了 _PAGE_PCD
和 _PAGE_PWT
位。读取按预期工作:读取返回正确的值并且第二次读取到同一地址并不一定会导致读取转到 PCIe(在 FPGA 中检查了读取计数器)。但是,写入导致系统冻结,然后重新启动,而日志或屏幕上没有任何消息。
其次,我尝试在支持 PAT 的 CentOS 6 (2.6.32) 上执行此操作。结果是一样的:读取工作正常,写入导致系统冻结并重新启动。有趣的是,非临时/写入组合的完整高速缓存行写入 (AVX/SSE) 按预期工作,即它们总是转到 FPGA 并且 FPGA 观察到完整的高速缓存行写入,然后读取返回正确的值。但是,简单的 64 位写入仍然会导致系统冻结/重启。
我还尝试在驱动程序代码中 ioremap_cache()
然后 iowrite32()
。结果是一样的。
我认为这是一个硬件问题,但如果有人能分享任何关于正在发生的事情的想法,我将不胜感激。
编辑:我能够在 CentOS 6 上捕获 MCE 消息:机器检查异常:5 Bank 5:be2000000003110a。
我还在 2-socket Sandy Bridge (Romley) 上尝试了相同的代码:读取和非临时写入行为是相同的,简单写入不会导致 MCE/崩溃但对系统状态没有影响,即内存中的值不变。
另外,我在旧的 2-socket Nehalem 系统上尝试了相同的代码:简单的写入也会导致 MCE,尽管代码不同。
【问题讨论】:
【参考方案1】:我不知道有任何 x86 硬件支持 MMIO 地址的 WriteBack (WB) 内存类型,您几乎肯定会看到这种不兼容的结果。 我已经在我的博客http://blogs.utexas.edu/jdm4372/2013/05/29/ 和http://blogs.utexas.edu/jdm4372/2013/05/30/ 上发布了关于这个主题的讨论
在这些帖子中,我讨论了一种适用于某些处理器的方法——两次映射 MMIO 范围——一次用于使用写入组合 (WC) 内存类型从处理器到 FPGA 的存储操作,一次用于读取使用写保护 (WP) 或直写 (WT) 类型从处理器到 FPGA。当您在“只写”区域中写入该行的别名时,您需要通过在“只读”区域中的缓存行上使用 CLFLUSH 手动保持一致性。您还需要手动维护 FPGA 内存中值变化的一致性,因为 IO 设备无法为 MMIO 地址生成缓存失效事务。
几年前,当我在 AMD 工作时,我的团队就这样做了,现在我正试图弄清楚如何使用更新的 Linux 内核和英特尔处理器来做到这一点。 Linux 不直接支持具有预定义映射功能的 WP 或 WT 内存类型,因此需要进行一些黑客攻击....覆盖一个区域的 MTRR 相当容易,但我很难找到正确的位置( s) 在我需要更改的 remap_pfn_range() 函数的后代中,以便在该范围的 PAT 条目中设置 WP 或 WT 属性。
这种方法可能比其他(预定义)类型的 IO 设备更适合 FPGA,因为 FPGA 的可编程性允许灵活定义 PCI BAR 以在这种双映射模式下运行并与保持高速缓存一致性的处理器端驱动程序。
【讨论】:
以上是关于如何为可缓存的 PCIe BAR 做 mmap的主要内容,如果未能解决你的问题,请参考以下文章
如何为 Blazor Web Assembly 实现缓存清除