是否可以为PCIe栏中由MMIO区域支持的地址(并通过UC或WC页表条目进行映射)预取?我目前正在为这个地址发出一个负载,这会导致超线程延迟相当一段时间。通过PREFETCHNTA
有一个非时态访问提示,所以这似乎是可能的。
如果可能的话,您知道预取值存储在哪里吗?在我能够为它发出负载之前,有什么可能导致它失效?例如,如果我为一些无关的东西发出同步指令(如sfence
),这会导致预取值失效吗?
来自英特尔软件开发手册:
“忽略来自不可缓存或WC内存的预取。应该指出,处理器可以自由地从分配给内存类型的系统内存区域(即WB、WC和WT内存类型)中获取和缓存数据。”
MMIO区域所在的PCIe条被标记为预取,因此我不确定这是否意味着,鉴于上面的手册中的语言,预取将使用它。
发布于 2022-11-20 00:24:03
我要感谢彼得·科德斯、约翰·D·McCalpin、尼尔·纳图、克里斯蒂安·路德洛夫和大卫·马齐埃,感谢他们帮助我解决这个问题!
为了预取,您需要能够将MMIO读取存储在CPU缓存层次结构中。当您使用UC或WC页表项时,您不能这样做。但是,如果使用WT页表条目,则可以使用缓存层次结构。
唯一要注意的是,当您使用WT页表条目时,以前使用陈旧数据进行的MMIO读取可能在缓存中徘徊。您必须在软件中实现一致性协议,以从缓存中清除陈旧的缓存线,并通过MMIO读取读取最新数据。这在我的例子中是没有问题的,因为我控制了PCIe设备上发生的事情,所以我知道什么时候应该冲洗。但是,您可能不知道什么时候应该在所有场景中进行刷新,这可能会使这种方法对您毫无帮助。
以下是我如何设置我的系统:
ioremap_wt()
(如果条形图已经映射到内核中,则使用ioremap_change_attr()
)。lspci -vv
中看到)和PCIe栏大小更新命令。大小是以字节为单位的十六进制值。echo "base=$ADDRESS size=$SIZE type=write-through" >| /proc/mtrr
prefetch()
函数来帮助发出预取.。
clflush
刷新陈旧的缓存行。Linux有clflush()
函数来帮助解决这个问题.这个场景中关于clflush
的一个注意事项:由于内存类型是WT,所以每个存储都会同时进入缓存中的缓存行和MMIO。因此,从CPU的角度来看,缓存中缓存行的内容总是与MMIO的内容相匹配。因此,clflush
只会使缓存中的缓存行失效--它也不会将陈旧的缓存行写入MMIO。
clflush
之后发出预取。但是,下面的代码不正确:clflush(address);
prefetch(address);
此代码不正确,因为根据https://c9x.me/x86/html/file_module_x86_id_252.html,预取可以在clflush
之前重新排序。因此,预取可以在clflush
之前发出,而当clflush
发生时,预取可能会失效。
要解决这个问题,根据链接,您应该在cpuid
和预取之间发出clflush
:
int eax, ebx, ecx, edx;
clflush(address);
cpuid(0, &eax, &ebx, &ecx, &edx);
prefetch(address);
彼得·科德斯( Peter )表示,在上面发布一个lfence
,而不是cpuid
,就足够了。
https://stackoverflow.com/questions/74409615
复制相似问题