LFENCE在AMD处理器上是否具有序列化功能?

9
在最近的Intel ISA文档中,lfence指令被定义为序列化指令流(防止它跨越乱序执行)。特别地,在描述该指令时包括了以下内容:

lfence不会执行,直到所有先前的指令都已本地完成,且在lfence完成之前没有后续指令开始执行。

请注意,这适用于所有指令,而不仅仅是内存加载指令,使得lfence比内存排序栅还要更加强大。
尽管此说明现已出现在ISA文档中,但仍不清楚它是否是“架构级”的,即要被所有x86实现遵守,还是只有Intel专用。特别地,AMD处理器是否也将lfence视为序列化指令流?

1
lfence 在英特尔上不是“串行化”的。该术语具有技术含义,包括完全刷新存储缓冲区。例如,cpuidiret 是串行化的。lfence 仅串行化指令流/乱序核心,而不是整个管道,包括存储缓冲区。我通常说它是“部分串行化”或类似的东西。 - Peter Cordes
@PeterCordes - 请注意,我在问题的第一次使用该术语时写下了“_串行化_指令流”。我不同意英特尔在其手册中始终使用_串行化_。他们确实相当一致地使用_串行化指令_来处理诸如cpuid之类的事情,但他们还将_串行化_单独用于其他事物,包括不是_串行化指令_的事物。在我引用的那句话之前,lfence部分的句子使用“串行化操作”一词来引用lfence - BeeOnRope
@PeterCordes - 是的,但这只是一个标签。我不觉得它会分散注意力。事实上,我认为它至少与主题有关:lfence 至少被呈现为内存屏障,并且确实是一种内存屏障,而这种 OoO 阻塞副作用实际上是其原始主要功能的实现设计的结果。如果你对 lfence 作为屏障感兴趣,那么你很可能关心性能,并且也许关心这种 OoO 阻塞行为。相反地,你提到 lfence 的 OoO 行为几乎每次在上下文中出现该指令时都会发生。 - BeeOnRope
@PeterCordes - 是的,这就是我为什么说“原始”主要标签。我还试图在“主要”周围加上引号,以反映今天最有趣的用途与内存排序无关,但是我在评论中达到了字符限制。问题是,这个标签正在取代“isa”标签,后者很少使用,在这里也没什么用处。 - BeeOnRope
@Brendan - 这只对英特尔起作用,因为现有芯片上恰好具有这种行为。对于 AMD 来说情况更加复杂,lfence 可能会或可能不会序列化,这取决于 MSR 中设置的值。 - BeeOnRope
显示剩余7条评论
2个回答

9

AMD在其手册中一直将LFENCE的实现描述为一个加载序列化指令。

作为一种屏障,强制内存顺序(序列化)在LFENCE之前的加载指令和在LFENCE之后的加载指令之间执行。

LFENCE最初的使用案例是对WC内存类型加载进行排序。然而,在发现了漏洞后,AMD在2018年1月发布了一份名为“管理AMD处理器推测的软件技术”的文件。这是唯一一份提到MSR C001_1029 [1]的文档(其他位于C001_1029的位数在某些AMD文档中讨论,但不是第1位)。当设置C001_1029 [1]为1时,LFENCE将作为调度序列化指令(比仅仅是加载序列化更昂贵)。由于该MSR在大多数旧版AMD处理器上可用,因此似乎它一直得到支持。也许是因为他们认为他们未来需要与英特尔处理器保持兼容性,以便使LFENCE的行为相同。

fence指令、序列化指令和具有序列化属性的指令的排序规则有例外情况。这些例外情况在英特尔和AMD处理器之间略有不同。我现在能想到的一个例子是CLFLUSH指令。因此,当AMD和Intel谈论具有序列化属性的指令时,它们的意思略有不同。

有一件事对我来说并不清楚,那就是来自harlod答案的下面这部分引用:

AMD家族0Fh/11h处理器始终支持LFENCE作为序列化,但不支持此MSR。

这个声明含糊不清,因为它并没有清楚地说明AMD家族0Fh和11h上的LFENCE是否完全序列化(在AMD术语中)或调度序列化(在AMD术语中)。但很可能只是调度序列化。AMD特定的手册中没有提到LFENCE或MSR C001_1029。


自Linux内核v4.15-rc8以来,AMD处理器上使用了LFENCE的序列化属性。更改由两个提交组成12。定义了以下宏:

+#define MSR_F10H_DECFG         0xc0011029
+#define MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT    1

第一个宏指定了MSR地址,第二个宏指定了偏移量。以下代码已添加在init_amd中(一些注释是我的):
/* LFENCE always requires SSE2 */
if (cpu_has(c, X86_FEATURE_XMM2)) {
    unsigned long long val;
    int ret;

    /* The AMD CPU supports LFENCE, but there are three cases to be considered:
     * 1- MSR C001_1029[1] must be set to enable the dispatch 
     *    serializing behavior of LFENCE. This can only be done 
     *    if and only if the MSR is supported.
     * 2- The MSR is not supported (AMD 0Fh/11h). LFENCE is by 
     *    default at least dispatch serializing. Nothing needs to 
     *    be done.
     * 3- The MSR is supported, but we are running under a hypervisor
     *    that does not support writing that MSR (because perhaps
     *    the hypervisor has not been updated yet). In this case, resort
     *    to the slower MFENCE for serializing RDTSC and use a Spectre
     *    mitigation that does not require LFENCE (i.e., generic retpoline).


    /*
     * A serializing LFENCE has less overhead than MFENCE, so
     * use it for execution serialization.  On families which
     * don't have that MSR, LFENCE is already serializing.
     * msr_set_bit() uses the safe accessors, too, even if the MSR
     * is not present.
     */
    msr_set_bit(MSR_F10H_DECFG,
            MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT);

    /*
     * Verify that the MSR write was successful (could be running
     * under a hypervisor) and only then assume that LFENCE is
     * serializing.
     */
    ret = rdmsrl_safe(MSR_F10H_DECFG, &val);
    if (!ret && (val & MSR_F10H_DECFG_LFENCE_SERIALIZE)) {
        /* A serializing LFENCE stops RDTSC speculation */
        set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);
        /* X86_FEATURE_LFENCE_RDTSC is used later to choose a Spectre
           mitigation */
    } else {
        /* MFENCE stops RDTSC speculation */
        set_cpu_cap(c, X86_FEATURE_MFENCE_RDTSC);
    }
}

自v5.4-rc1版本以来,MSR写入验证代码已被删除。因此,代码变为:
    msr_set_bit(MSR_F10H_DECFG,
            MSR_F10H_DECFG_LFENCE_SERIALIZE_BIT);
    set_cpu_cap(c, X86_FEATURE_LFENCE_RDTSC);

这个更改的原因在提交信息中有讨论。(总的来说,它大多数情况下都不需要,并且可能无效。)
该文档还表示:

所有AMD 10h/12h/14h/15h/16h/17h处理器都支持此MSR。 CPUID函数1 EDX位26(SSE2)表示LFENCE支持。 AMD 0Fh/11h系列处理器始终支持LFENCE作为序列化,但不支持此MSR。

但似乎尚未更新任何AMD手册以提到对C001_1029[1]的支持。
AMD在该文件中说了以下内容:

AMD计划支持这个MSR并为所有未来的处理器提供这一位的访问权限。

这意味着在未来的AMD处理器上(截至2018年1月),C001_1029[1]应被视为架构性的。

6

有一个MSR来配置这种行为:

描述:在处理器中设置一个MSR,使LFENCE成为一个调度序列化指令,然后在代码流中使用LFENCE来序列化调度(LFENCE比也适用于调度序列化的RDTSCP更快)。通过设置MSR C001_1029 [1] = 1可以启用LFENCE的这种模式。

效果:当MSR位被设置时遇到LFENCE时,调度将停止,直到LFENCE指令成为机器上最老的指令。

适用性:所有AMD家族10h / 12h / 14h / 15h / 16h / 17h处理器都支持此MSR。 CPUID函数1 EDX位26(SSE2)表示支持LFENCE。 AMD家族0Fh / 11h处理器始终支持LFENCE作为序列化,但不支持此MSR。 AMD计划支持此MSR并为所有未来处理器提供对此位的访问。

(source)


这个寄存器是最近的发明,还是在旧的AMD模型上也存在?如果它是一个旧的东西,似乎很奇怪,因为它可能是为了Spectre缓解而添加的。 - BeeOnRope
2
@BeeOnRope AMD家族10h是K10,0Fh(没有MSR但有序列化LFENCE)是K8,据我所知,任何更旧的东西甚至还没有SSE2,因此根本没有LFENCE。 - harold
Windows/Linux/*BSD都启用了Spectre漏洞缓解的MSR吗?因此,如果我们可以假定内核已更新,那么可移植使用lfence; rdtsc现在基本上是安全的。 - Peter Cordes
1
请注意,英特尔的手册并不保证 rdtscp 停止后续指令的执行,它只承诺在所有早期指令完成之前不会对时间进行采样。 因此,在定时间隔的开始处使用它是没有用的。 另请参考这种情况,在计时间隔底部使用 lfence;rdtsc;lfence 能够获得更一致的结果:clflush to invalidate cache line via C function - Peter Cordes
更新:我不确定在英特尔CPU上,rdtscp是否真的像英特尔的论文规范中那样弱。它可能基本上会解码为uops,就像lfence;rdtsc加上设置ECX一样。我似乎记得有人在评论中提到过,他们从未见过后续指令能够提前执行的情况。 - Peter Cordes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接