我编写了一个linux驱动程序,ioremaps将特定设备的PCI BAR0导出到sysfs二进制属性,允许用户空间直接控制它.
当我尝试在属性之上进行MMAP以直接访问该位内存(来自userland程序)时,问题就出现了.读取成功并返回预期值,但是当我写入该内存时,它似乎缓存在内核和内存之间的某处,而不是传递给GMCH根复合体(因此也就是设备).我想做的是每次访问后都有一个隐含的写内存屏障.
跟进:
我正在玩这个答案的代码,稍微修改一下:
BITS 64
GLOBAL _start
SECTION .text
_start:
mov ecx, 1000000
.loop:
;T is a symbol defined with the CLI (-DT=...)
TIMES T imul eax, eax
lfence
TIMES T imul edx, edx
dec ecx
jnz .loop
mov eax, 60 ;sys_exit
xor edi, edi
syscall
Run Code Online (Sandbox Code Playgroud)
没有lfence我,我得到的结果与答案中的静态分析一致.
当我介绍一个单一 lfence我期望的CPU执行imul edx, edx的序列的第k个平行于迭代imul eax, eax的下一个(的序列K + 1个)迭代.
像这样的东西(调用一个的imul eax, eax序列和d的imul edx, edx一个): …
在最近的英特尔ISA文档中,该lfence指令被定义为序列化指令流(防止指令流无序执行).特别是,该指令的描述包括以下行:
具体来说,LFENCE不会执行,直到所有先前的指令在本地完成,并且在LFENCE完成之前没有后续指令开始执行.
请注意,这适用于所有的指令,不只是内存加载指令,使得lfence 更多的不仅仅是一个存储排序防护.
虽然这现在出现在ISA文档中,但不清楚它是否是"架构",即所有x86实现都遵守,或者它是否特定于Intel.特别是AMD处理器是否也将lfence序列化为指令流?
是否可以对 PCIe BAR 中的 MMIO 区域支持的地址(并通过 UC 或 WC 页表条目映射)发出预取?我目前正在对该地址发出负载,这会导致超线程停滞相当长一段时间。有一个非临时访问提示 via PREFETCHNTA,所以看起来这可能是可能的。
如果可能的话,您是否知道预取值存储在哪里以及在我能够为其发出加载之前什么可能导致它变得无效?例如,如果我针对sfence不相关的内容发出同步指令,这是否会导致预取值失效?
来自英特尔软件开发手册:
“来自不可缓存或 WC 内存的预取将被忽略......应该注意的是,处理器可以自由地推测性地从系统内存区域获取和缓存数据,这些区域被分配了允许推测性读取的内存类型(即 WB、WC和 WT 内存类型)。
MMIO 区域所在的 PCIe BAR 被标记为可预取,因此我不确定这是否意味着预取将与上面手册中的语言一起使用。