Assembly ARM LL/SC通过寄存器宽度或缓存线宽度进行独占访问?

Assembly ARM LL/SC通过寄存器宽度或缓存线宽度进行独占访问?,assembly,arm,atomic,lock-free,load-link-store-conditional,Assembly,Arm,Atomic,Lock Free,Load Link Store Conditional,我正在使用ARM上的LL/SC开发下一版本的无锁数据结构库 对于我的LL/SC用例,我需要在LDREX和STREX之间使用一个STR。(而不是使用它来模拟CAS。) 现在,我已经编写了代码,这很有效。 然而,让我担心的是,它可能并不总是有效的。 我在PowerPC上读到,如果访问与LL/SC目标相同的缓存线,则会破坏LL/SC 所以我想如果我的STR目标和LL/SC目标在同一缓存线上,那么pow,我死了 现在,LL/SC目标和STR目标始终位于不同的malloc()中,因此它们直接位于同一缓存线

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

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

现在,我已经编写了代码,这很有效。 然而,让我担心的是,它可能并不总是有效的。 我在PowerPC上读到,如果访问与LL/SC目标相同的缓存线,则会破坏LL/SC

所以我想如果我的STR目标和LL/SC目标在同一缓存线上,那么pow,我死了

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

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

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


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

ARM使用独占监视器通过负载链接/存储条件实现对内存的独占访问。[1] 有所有的细节,重要的是,我要说的是:

独家保留颗粒

当独占监视器标记地址时,可以 被标记为独占访问被称为独占预订 颗粒(ERG)。ERG由实现定义,范围为8-2048 字节,以两个字节的倍数表示。可移植代码不能假定 任何尺寸的都可以

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


[1]

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

这不是对你问题的回答,只是关于这些说明的一般信息,旨在教育人们正确使用和相关风险。(是的,在那里,完成了,由于使用这些指令而被烧掉了,不得不修补linux(在其他linux修补程序的基础上))

编辑…更多详细信息

在手臂上:

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)
因此,在软件方面,ARM告诉我们使用LDREX/STREX而不是swap,部分原因是它可以扩展到多处理器、共享内存系统。但它们也告诉我们,单处理器系统不需要支持共享内存同步。因此,即使是从软件方面,也有一个线索,你应该三思而后行

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

while(1)
{
  ldrex
  strex
  if(rd==0) break;
}
现在在单处理器系统上,ARM和ARM都建议不需要支持共享访问,因为它更简单(为什么需要增加复杂性?)

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

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

这将转换为任何其他想要使用LDREX/ST的人
while(1)
{
  ldrex
  strex
  if(rd==0) break;
}