4

我正在开发我的无锁数据结构库的下一个版本,在 ARM 上使用 LL/SC。

对于我的 LL/SC 用例,我需要将它与 LDREX 和 STREX 之间的单个 STR 一起使用。(而不是用它来模拟 CAS。)

现在,我已经编写了代码,并且可以正常工作。然而,我担心的是它可能并不总是有效。如果您访问与 LL/SC 目标相同的高速缓存行,我已经在 PowerPC 上阅读过,您会破坏 LL/SC。

所以我在想,如果我的 STR 目标与我的 LL/SC 目标在同一缓存行上,那么 pow,我已经死了。

现在,LL/SC 目标和 STR 目标始终位于不同的 malloc() 中,因此它们直接位于同一缓存行中的机会可能很小(我可以通过填充 LL/SC 目标来保证这一点,因此它开始于缓存线边界并填充该缓存线)。

但是,如果 STR 目标在内存中的正确(错误!)位置,则可能存在错误共享。

查看 LDREX/STREX 文档,这描述了“物理地址”方面的独占访问。这意味着寄存器宽度粒度,而不是高速缓存行宽度粒度。

这就是我的问题——LDREX/STREX 是否对使用寄存器宽度粒度或缓存线宽度粒度的内存访问敏感?

4

2 回答 2

2

ARM 使用独占监视器通过加载链接/存储条件实现对内存的独占访问。[1] 包含所有细节,我想说的是以下几点:

独家预订颗粒

当独占监视器标记地址时,可以标记为独占访问的最小区域称为独占保留颗粒 (ERG)。ERG 是在 8-2048 字节范围内以两个字节的倍数定义的实现。可移植代码不得假设 ERG 大小。

所以在我看来,你有点不走运。无论如何,大多数实际实现可能会保持一个很小的值,但据我所知,基本的 ARM 架构并不能保证,但也许有更多经验的人会发现我错了。:) 尽管如此,LL/SC 的所有实现都是某种形式的弱 LL/SC,所以你几乎永远不能完全确定 LL 和 SC 之间的存储不会总是杀死 SC,或者大多数情况下时间,或者可能永远不会......它只是依赖于架构和实现,我个人坚持使用 LL/SC 在紧密循环中实现 CAS 并像往常一样使用它并完成它。

[1] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/CJAGCFAF.html

于 2012-05-30T11:11:24.453 回答
1

请注意,LDREX/STREX 并没有像许多人认为的那样做。它们适用于多处理器系统,单处理器系统应考虑使用交换。ARM 文档通常非常好,但在这种特殊情况下,它们之间存在巨大差距。Linux 一直在不正确地使用这些指令,并且具有单处理器 ARM 内核的公司已经注意到这一点(由于人们在没有适当研究的情况下添加代码,Linux 存在许多与 ARM 相关的错误,每个版本都必须修复)。如果您在单处理器系统上拥有 L1 缓存,则可以,因为缓存支持该访问类型,如果它到达 AXI 总线,AMBA/AXI 规范会告诉硬件工程师,对于单处理器系统,您不需要支持该事务类型。不幸的是,ARM ARM/TRM 告诉软件工程师你应该停止使用交换并开始使用 LDREX/STREX,

这不是您的问题的答案,只是有关这些说明的一般信息,以尝试教育人们正确使用和所涉及的风险。(是的,去过那里,完成了,使用这些指令被烧毁了,必须修补 linux(在其他 linux 修补程序之上))

编辑....更多细节

在 ARM ARM 中:

Historically, support for shared memory synchronization has been with the read-locked-write operations
that swap register contents with memory; the SWP and SWPB instructions described in...

...

ARMv6 provides a new mechanism to support more comprehensive non-blocking shared-memory synchronization primitives
that scale for multiple-processor system designs.

...

The swap and swap byte instructions are deprecated in ARMv6. It is recommended that all software
migrates to using the new synchronization primitives.

...

Uniprocessor systems are only required to support the non-shared memory model, allowing them to support
synchronization primitives with the minimum amount of hardware overhead.

...

Multi-processor systems are required to implement an address monitor for each processor.


STREX:

<Rd> Specifies the destination register for the returned status value. The value returned is:
0  if the operation updates memory
1 if the operation fails to update memory.


MemoryAccess(B-bit, E-bit)
if ConditionPassed(cond) then
  processor_id = ExecutingProcessor()
  physical_address = TLB(Rn)
  if IsExclusiveLocal(physical_address, processor_id, 4) then
    if Shared(Rn) == 1 then
      if IsExclusiveGlobal(physical_address, processor_id, 4) then
        Memory[Rn,4] = Rm
        Rd = 0
        ClearExclusiveByAddress(physical_address,processor_id,4)
      else
        Rd = 1
    else
      Memory[Rn,4] = Rm
      Rd = 0
  else
  Rd = 1
ClearExclusiveLocal(processor_id)

AMBA/AXI 规格

The ARLOCK[1:0] or AWLOCK[1:0] signal selects exclusive access, and the RRESP[1:0] 
or BRESP[1:0] signal (see Table 7-1 on page 7-2) indicates the success or failure 
of the exclusive access.

...

If the master attempts an exclusive read from a slave that does not support exclusive
accesses, the slave returns the OKAY response instead of the EXOKAY response. The
master can treat this as an error condition indicating that the exclusive access is not
supported. It is recommended that the master not perform the write portion of this
exclusive operation.

...

b00 OKAY
b01 EXOKAY

...

ARLOCK/AWLOCK

b00 normal access
b01 exclusive access

所以在软件方面,ARM ARM 告诉我们使用 LDREX/STREX 而不是交换,部分原因是它可以扩展到多处理器、共享内存、系统。他们也告诉我们,单处理器系统不需要支持共享内存同步。因此,即使从软件方面来看,您也应该三思而后行……

我们从 STREX 的描述中知道,如果它返回独占 rd = 0,那么它就起作用了。如果 rd = 1 那么它不是排他性的(或其他原因)。LDREX 和 STREX 是成对完成的,共享内存系统逻辑正在寻找相同地址的对,并且硬件验证两者之间没有其他对该地址的访问。你担心谁会夹在两者之间?1)如果您中断/交换并且非常幸运 2)使用该内存的另一个处理器。linux 所做的,据我所知,进入一个紧密的无限循环,

while(1)
{
  ldrex
  strex
  if(rd==0) break;
}

现在在单处理器系统上,ARM ARM 都建议它们不需要支持共享访问,因为它更简单(为什么需要增加这种复杂性?)。

作为程序员你看不到的东西。为 ldrex 和 strex 设置了 ARLOCK 或 AWLOCK,如果您正在实现共享访问,那么您关心这些位。如果您正在实现共享访问,那么如果两者之间没有访问,则将 EXOKAY 返回给 strex。EXOKAY 是一个 b01,在 strex 伪代码中是独占的全局,rd = 0。如果硬件返回 OKAY,b00,它不是独占的,对于 strex,rd = 1。然后 AMBA/AXI 规范说,如果您不支持共享系统,可以返回 OKAY 进行独占访问。因此,在尚未实现独占访问的单处理器上,strex 可以和/或将始终返回 OKAY,而不是 EXOKAY。这意味着 strex 永远不会得到 rd = 0 并且 linux 在无限循环中挂起。

这里真正的 linux 错误是我们当时看到的代码说如果(ARMv6 或更高版本)则使用 LDREX/STREX,否则使用 SWP。如果(ARMv6 或更新版本和多处理器)修复错误,则使用 LDREX/STREX,否则使用 SWP。

这意味着其他任何人想要使用 LDREX/STREX 出于任何其他原因,这正是这张票中引起我注意的地方。

现在你问,缓存跟它有什么关系?L1 缓存在处理器内核内部,它不会在 AXI/AMBA 总线上输出。它为 strex 返回 EXOKAY,和/或它完全实现共享。因此,如果 L1 缓存打开,那么您将获得 EXOKAY(第一次或最终,我不确定)。

现在你问,如果有缓存未命中怎么办?如果 L1 缓存关闭,那么首先关闭,然后它会在没有可缓存位的情况下到达 L2 缓存边界。所以 L2 缓存会按原样传递它,并且它会作为独占的。当 L1 缓存打开时,它会像上面一样返回 EXOKAY(最终或总是,不知道)如果 L1 未命中,则 L1 执行缓存行填充,它执行可缓存的未锁定读取。这会导致 L2 命中或未命中,如果 L2 未命中,则它会转到供应商特定的逻辑,在这种情况下返回 OKAY 但这没关系,因为它没有被锁定,它是一个正常的访问。一旦 l2 和 l1 被填满,L1 就会执行原始传输并返回 EXOKAY。

现在这是关键,首先在硬件中实现它是一种浪费和风险,所以我希望单处理器 ARMv6 和更新版本不会返回 EXOKAY,你必须逐案测试。第二,它是让 linux 在缓存关闭的情况下运行的 PITA。事实上,这需要一些工作。所以你不太可能在linux中正常看到这个。但问题就在那里,人们已经看到了,任何时候你自己使用这些说明时,你都应该小心正确地使用它们。使用裸机编程来测试系统以查看它是否会挂起应该非常简单,应该需要几秒钟/几分钟来编写代码。将系统置于可以尝试该代码的状态可能需要更长的时间(中断引导加载程序,使用 jtag 跳转等)。

于 2012-05-30T14:36:39.120 回答