代码之家  ›  专栏  ›  技术社区  ›  Daniel Näslund

存储缓冲区和行填充缓冲区是如何相互作用的?

  •  0
  • Daniel Näslund  · 技术社区  · 4 年前

    我在读MDS攻击报告 RIDL: Rogue In-Flight Data Load 他们讨论了行填充缓冲区如何导致数据泄漏。有 About the RIDL vulnerabilities and the "replaying" of loads 该问题讨论了漏洞的微观架构细节。

    在阅读了这个问题后,我不清楚的一件事是,如果我们已经有一个存储缓冲区,为什么我们需要一个行填充缓冲区。

    John McCalpin讨论了存储缓冲区和行填充缓冲区是如何连接的 How does WC-buffer relate to LFB? 在英特尔论坛上,但这并没有让我更清楚。

    对于存储到WB空间,存储数据将保留在存储缓冲区中,直到存储退役后。一旦退役,数据可以写入L1数据缓存(如果该行存在并且具有写入权限),否则将为存储未命中分配LFB。LFB最终将接收缓存行的“当前”副本,以便将其安装在L1数据缓存中,并将存储数据写入缓存。合并、缓冲、排序和“捷径”的细节尚不清楚。。。。一种与上述合理一致的解释是,LFB充当缓存行大小的缓冲区,在将存储数据发送到L1数据缓存之前,将存储数据合并到缓冲区中。至少我认为这是有道理的,但我可能忘了什么。。。。

    我最近才开始阅读关于无序执行的文章,所以请原谅我的无知。以下是我对存储如何通过存储缓冲区和行填充缓冲区的想法。

    1. 在前端安排存储指令。
    2. 它在存储单元中执行。
    3. 存储请求被放入存储缓冲区(地址和数据)
    4. 无效读取请求从存储缓冲区发送到缓存系统
    5. 如果它错过了L1d缓存,则将请求放入行填充缓冲区
    6. 行填充缓冲区将无效读取请求转发给L2
    7. 某些缓存接收无效读取并发送其缓存行
    8. 存储缓冲区将其值应用于传入的缓存行
    9. 呃?行填充缓冲区将条目标记为无效

    enter image description here

    问题

    1. 如果存储缓冲区已经存在,为什么我们需要Line Fill Buffer来跟踪扩展的存储请求?
    2. 我的描述中事件的顺序正确吗?
    0 回复  |  直到 4 年前
        1
  •  16
  •   Peter Cordes    2 年前

    如果存储缓冲区已经存在,为什么我们需要Line Fill Buffer来跟踪扩展的存储请求?

    存储缓冲区用于按顺序跟踪存储,包括 之前 它们退役,退役后但在提交到L1缓存之前 2. 从概念上讲,存储缓冲区是一个完全本地的东西,它并不真正关心缓存未命中。存储缓冲区处理各种规模的单个存储的“单位”。像英特尔Skylake这样的芯片 store buffers of 50+ entries .

    行填充缓冲区主要处理 二者都 加载并存储 L1缓存中未命中 本质上,它是从L1缓存到存储器子系统其余部分的路径,并以缓存行大小的单位进行处理。如果加载或存储在L1缓存中命中,我们不希望LFB介入 1. .像Skylake这样的英特尔芯片的LFB条目要少得多,可能只有10到12个(测试点为 12 for Skylake ).

    我的描述中事件的顺序正确吗?

    非常接近。以下是我如何更改您的列表:

    1. 存储指令被解码并拆分为存储数据和存储地址uops,它们被重命名、调度并分配了存储缓冲区条目。
    2. 存储uops以任何顺序或同时执行(两个子项可以以任何顺序执行,主要取决于哪个首先满足其依赖关系)。
      1. 存储数据uop将存储数据写入存储缓冲区。
      2. 存储地址uop执行V-P转换,并将地址写入存储缓冲区。
    3. 在所有旧指令都失效的某个时候,存储指令 退休 这意味着指令不再是推测性的,结果可以显示出来。此时,存储仍保留在存储缓冲区中,称为 高级的 商店。
    4. 存储现在等待,直到它位于存储缓冲区的顶部(它是最古老的未提交的存储),此时,如果相关缓存行在L1中处于MESIF Modified或Exclusive状态,它将提交(变得全局可观察)到L1中。(即该核心拥有该线路)
    5. 如果行未处于所需状态(要么完全缺失,即缓存未命中,要么存在但处于非独占状态),则必须从内存子系统获得修改行和行数据的权限(有时):如果尚未分配LFB,则为整行分配LFB。这是一个所谓的 所有权请求 (RFO),这意味着内存层次结构应该 return the line 在适合修改的独占状态下,与仅适合读取的共享状态相反(这会使任何其他私有缓存中存在的行副本无效)。

    将共享转换为独占的RFO仍需等待响应,以确保所有其他缓存已使其副本无效。对这种无效的响应不需要包括数据的副本,因为此缓存已经有一个副本。它仍然可以被称为RFO;重要的部分是在修改线条之前获得所有权。 6.在未命中的情况下,LFB最终会返回该行的全部内容,该行已提交给L1,挂起的存储现在可以提交 3. .

    这是对该过程的粗略近似。某些或所有芯片上的某些细节可能不同,包括不太清楚的细节。

    例如,按照上述顺序,在商店到达商店队列的头部之前,不会提取商店未命中的行。实际上,商店子系统可以实现一种类型的 RFO预取 其中检查存储队列中即将到来的存储,如果L1中不存在这些行,则提前启动请求(在x86上,对L1的实际可见提交仍然必须按顺序发生,或者至少“好像”按顺序发生)。

    因此,请求和LFB使用可能早在步骤3完成时发生(如果RFO预取仅在商店退役后适用),或者如果初级商店需要预取,甚至可能早在2.2完成时发生。

    再举一个例子,步骤6描述了从内存层次结构返回并提交到L1的行,然后存储提交。有可能挂起的存储实际上与返回的数据合并,然后将其写入L1。即使在未命中的情况下,存储也可能离开存储缓冲区,只需在LFB中等待,释放一些存储缓冲区条目。


    1. 在L1缓存中命中的存储的情况下 建议 LFB实际上是涉及的:每个存储在提交到缓存之前实际上都会进入一个组合缓冲区(可能只是一个LFB),这样一系列针对同一缓存行的存储在缓存中组合在一起,只需要访问L1一次。这还没有得到证实,但无论如何,这并不是LFB主要用途的一部分(更明显的是,我们甚至无法真正判断它是否正在发生)。

    2. 在退休之前和退休之前保存存储的缓冲区可能是两个完全不同的结构,具有不同的大小和行为,但在这里我们将它们称为一个结构。

    3. 所描述的场景涉及在存储缓冲区头部等待相关行返回的存储。另一种情况是,存储数据被写入用于请求的LFB中,并且可以释放存储缓冲区条目。这可能允许在未命中过程中处理一些后续存储,但须符合严格的x86订购要求。这可能会增加商店的MLP。

        2
  •  0
  •   Lewis Kelsey    4 年前

    当uop到达分配器时,在PRF+退役RAT方案(SnB以后)中,分配器在需要重命名ROB条目的时候(即当写入架构寄存器时(例如。 rax )执行),它在ROB的尾部指针处为每个uop分配。RAT在PRF中保留一个空闲和正在使用的物理目标寄存器(pdst)列表。RAT返回要使用的空闲物理寄存器号,然后ROB将这些寄存器号放置在相应的条目中(在RRF方案中,分配器向RAT提供要使用的pdst;RAT无法选择,因为要使用的pdf固有地位于ROB的尾部指针处)。RAT还用分配给它的寄存器更新每个架构寄存器指针,即它现在指向包含程序顺序中最新写入数据的寄存器。同时,ROB在预订站(RS)中分配一个条目。如果是存储,它将在RS中放置存储地址uop和存储数据uop。分配器还分配SDB(存储数据缓冲区)/SAB(存储地址缓冲区)条目,并且仅在ROB/RS/RAT/SDB/SAB中的所有所需条目可用时进行分配

    一旦在RS中分配了这些uop,RS就会读取其源操作数的物理寄存器,并将其存储在数据字段中,同时检查EU回写总线上与ROB条目相关的源PR(物理寄存器)和回写数据。然后,RS调度这些uop,以便在它们拥有所有完整的源数据时将其分派到存储地址和存储数据端口。

    然后分派uop——存储地址uop到达AGU,AGU生成地址,将其转换为线性地址,然后将结果写入SAB。我认为在PRF+R-RAT方案中,商店根本不需要PR(这意味着在这个阶段ROB不需要发生回写),但在RRF方案中,ROB条目被迫使用其嵌入的PR,所有内容(ROB/RS/MOB条目)都由其PR编号标识。PRF+R-at方案的一个好处是,ROB以及飞行中的最大uop数量可以在不增加PR数量的情况下扩展(因为会有不需要任何指令的指令),并且如果条目没有标识PR,则所有内容都由ROB条目编号标识。

    存储数据直接通过存储转换器(STC)传输到SDB。一旦它们被分派,它们就可以被释放以供其他uop重用。这可以防止更大的ROB受到RS大小的限制。

    然后,地址被发送到dTLB,然后它将dTLB输出的物理标签存储在L1d缓存的PAB中。

    分配器已经为该ROB条目在SAB/SDB中分配了SBID和相应的条目(STA+STD uops被微写入一个条目),这些条目缓冲了来自RS的AGU/TLB中调度执行的结果。这些存储与具有相同条目号(SBID)的相应条目一起位于SAB/SDB,将它们链接在一起,直到MOB收到退役单元的通知,其中存储已准备好退役,即它们不再是推测性的,并且在CAM匹配到指向SAB/SDB条目中包含的ROB索引/ID的ROB条目检索指针时(在一个可以每个周期退出3个uops,有3个退出指针指向ROB中3个最旧的未退出指令,只有ROB就绪位模式0,0,0,1,1和1,1,1允许退出指针CAM匹配继续进行)。在此阶段,只要SAB/SDB/PAB中的数据有效,它们就可以在ROB中退役(称为“退役/本地完成”),并成为高级存储,并标记有高级位(Ae位表示STA,De位表示STD),然后缓慢地分派到L1d缓存。

    L1d缓存使用SAB中的线性索引对标签数组中包含使用线性索引的数据的集合进行解码,并在下一个循环中使用与SBID具有相同索引值的相应PAB条目将物理标签与集合中的标签进行比较。PAB的全部目的是允许对存储进行早期TLB查找,以隐藏dTLB未命中的开销,而这些存储正在等待成为高级存储,并允许在存储仍处于推测状态时进行推测性页面浏览。如果存储是立即高级的,那么这种早期的TLB查找可能不会发生,它只是被分派,这时L1d缓存将解码标签数组集并并行查找dTLB,并且绕过PAB。请记住,在执行TLB转换之前,它不能从ROB退出,因为可能存在PMH异常代码(页面错误,或者在执行页面遍历时需要设置访问/脏位读取),或者当TLB需要写入它在TLB条目中设置的访问/脏位数时的异常代码。完全有可能的是,存储的TLB查找总是发生在这个阶段,并且不会与集合解码并行执行(与加载不同)。当PAB中的PA变为有效(SAB中设置了有效位)并且ROB中的PA已准备就绪时,存储将变为高级存储。

    然后检查线路的状态。如果是共享线路,则物理地址将发送到RFO中的一致性域(写入时始终为RFO),当它拥有该线路的所有权时,它会将数据写入缓存。如果该行不存在,则为该缓存行分配LFB,并将存储数据存储在其中,然后向L2发送请求,L2将检查L2中该行的状态,并在环形IDI接口上启动读取或RFO。

    当RFO完成时,存储将变得全局可见,LFB中的一个位表示它有权写入该行,这意味着在下一次监听无效或驱逐时(或者在命中的情况下,当数据写入该行时),LFB将被一致地写回。当在未命中的情况下提取行之前将其写入LFB时,它不被认为是全局可见的(与高级不同 荷载 在命中或L1d缓存分配LFB时确实会退出),因为可能存在其他内核发起的其他RFO,这些RFO可能会在当前内核的请求之前到达LLC切片控制器,如果 SFENCE 基于这种“全局可见”的退役版本,当前内核已经退役——至少这为处理器间中断提供了同步保证。全局可见的是,如果负载发生在另一个内核上,存储的数据将被另一个核心读取的时刻,而不是在一段时间后,旧值仍将被其他内核读取的时刻。在分配LFB时(或在命中的情况下写入行时),存储由L1d缓存完成,并从SAB/SDB中退出。当所有以前的商店都从SAB/SDB退役时,这是 store_address_fence (不是 store_address_mfence )及其相关 store_data_fence 可以发送到L1d。它更实用 LFENCE 也对ROB指令流进行串行化,而 SFENCE / MFENCE 不要这样做,因为这可能会导致ROB的全球可见性出现很长时间的延迟,而且与立即退役的高级负载不同,这是不必要的,所以这是有道理的 LFENCE 围栏是否同时选择了串行化指令流。 SFENCE/MFENCE 在所有已分配的LFB变得全局可见之前,不要退役。

    行填充缓冲区可以有三种模式之一:读、写或写组合。我认为写行填充缓冲区的目的是将同一行数据的多个存储组合到LFB中,然后当行到达时,用从L2获取的数据填充无效位。在这个阶段,它可能被视为已完成,因此写入操作会提前一个周期批量完成,而不是等待写入到行中。只要现在保证它会被写回缓存以响应另一个内核的RFO。LFB可能会保持分配状态,直到需要释放为止,这样可以稍微更快地满足对同一行的后续读写。读行缓冲区可以更快地处理读缺失,因为它在行填充缓冲区中立即可用,但将其写入缓存行并从中读取需要更长的时间。当内存为 USWC 类型,并允许立即满足写入并一次性刷新到MMIO设备,而不是具有多个内核->;PCIe事务,并具有多个PCIe事务。WC缓冲区还允许从缓冲区进行推测性读取。通常不允许在UC内存上进行推测性读取,因为读取可能会改变MMIO设备的状态,而且读取/写入需要很长时间,以至于当它完成时,它将不再是推测性的,因此可能不值得额外的流量?LFB可能是VIPT( VIPT/PIPT are the same on intel ,V是intel上的线性地址);我想它可以有物理和虚拟标签来消除进一步的TLB查找,但当物理页面迁移到新的物理地址时,必须协商一些事情。