本文翻译自 Memory access ordering part 3: Memory access ordering in the Arm Architecture.
该系列有 3 篇文章,其余两篇是:

作者 Leif Lindholm 在 ARM 工作多年,经验丰富,虽然这篇文章发表于 2013 年,但是还是很有借鉴价值的。


前两篇文章分别介绍了内存访问顺序的概念内存屏障及它在Linux内核中的实现。我之所以选择按此顺序进行,是因为我想在开始详细介绍Arm架构的内存排序之前先传达基本概念。本文详细介绍了Arm体系结构的内存排序。
在Arm体系结构中与内存访问顺序有关的两个概念是内存类型memory types)和可共享性域shareability domains)。在 Armv6 和 Armv7 中它们逐渐引入,分别由Arm11Cortex family来实现。

摘要

在描述本文中提到的许多概念时,Arm Architecture Reference Manual经常使用“观察者”(observer),“被观察到”(is observed to)和“必须观察到”(must observe)这样的词语。实际上,这指的是主总线接口,控制这些接口以及互连的设备必须如何处理事务(transaction)。只有主接口才能观察事务。由于所有总线事务都是由主设备(master)发起的,因此可以根据主设备的排序规则推断出到达从设备接口的访问顺序。注意,事务排序并非简单地指事务离开主设备接口的顺序——它们通常可以在内存系统中重新排序,并且可以在没有显式排序的情况下,被不同的主设备以不同的顺序观察到。

内存类型

Armv6架构之前,关于内存的乱序访问并没有明确的定义——假定顺序执行模型Sequential Execution Model)适用于所有指令。实现缓存(cache)或缓冲(buffer)的处理器可以讲内存区域标记为可缓存的cacheable)或可缓冲的bufferable),而不会产生明显的副作用。
但是对于实现多核,乱序执行或仅允许某些访问被缓冲、而其他访问则同步发生的现代处理器而言,必须要定义一些约束规则:

  • 一个核的内存访问顺序与周围指令相关
  • 多核处理器中一个核的内存访问顺序可以被其他核观察到
  • 一个核的内存访问顺序可以被系统总线上的其他主设备观察到

我不会在本文中详细介绍不同的内存类型,但是我会概述与本文相关的要点。操作系统的MMU地址转换表中可以配置内存类型(及其附加属性,如缓存策略(cache policy))。

Normal memory

Normal memory 可以有效地存储所有数据和可执行代码。这种内存类型允许推测性读取、合并访问和重复读取(如果被异常打断)而没有副作用。对 Normal memory 的访问总是可以被缓冲的(buffered),在大多数情况下也可以被缓存(cached)——也可以配置成不被缓存(uncached)。除了纯粹地址依赖和控制依赖之外,Normal memory 访问没有隐式的排序。

Device memory 和 Strongly-ordered memory

Device 和 Strongly-ordered 内存类型用于外设和其他控制寄存器。Device 和 Strongly-ordered 类型非常相似,在支持LPAE的Armv7-A中,由于实现LPAE的处理器将Device和Strongly-order类型同等对待,这一点变得更加正确。[译者注:在Armv8-a 中已经没有 Strongly-order 类型了,只有 Device 类型]。未实现LPAE的Armv7-A处理器可以将Device memory设置为可共享(Shareable)或不可共享(Non-shareable)。
对这些类型的内存的访问必须按照执行程序建议的次数进行。对同一位置的两次写入必须作为两次写入执行,并且从同一位置的两次读取必须同时进行。当访问外设控制寄存器时,这显得很重要。但是,无法保证不同设备之间的内存访问顺序,或者不同内存类型之间的访问顺序。

屏障(Barriers)

屏障是逐步引入进Arm架构的。

  • 一些Armv5的处理器,例如Arm926EJ-S,实现了一个Drain Write Buffer 的cp15操作,该操作会暂停处理器执行直至所有的写缓冲都写入到外部存储器为止。
  • 随着Armv6内存模型的引入,此操作在体系结构方面进行了重新定义,并成为数据同步屏障Data Synchronization Barrier)。Armv6还引入了新的数据存储屏障Data Memory Barrier)和刷新预取缓冲区Flush Prefetch Buffer)的cp15操作。
  • Armv7在某种程度上改进了内存模型,扩展了屏障的含义。刷新预取缓冲区操作被重命名为指令同步屏障Instruction Synchronization Barrier)。
  • Armv7还为屏障操作分配了专用的指令编码:DMB,DSBISB.
  • 最后,Armv7将“可共享性”概念扩展到涵盖内部可共享域(Inner-shareable)和外部可共享域(Outer-shareable)(请参见下文)。这与AMBA4 ACE一起为我们提供了屏障。

那么这些屏障是什么?有什么用呢?

指令同步屏障(ISB)

指令同步屏障可确保从缓存中重新获取任何后续的指令,以便利用当前MMU的配置检查特权和访问权限。它用于确保任何先前执行的上下文更改操作(包括cp15操作)在ISB完成时已经完成。
其实访问类型和域与屏障并不是真正相关。Linux内核内存屏障原语没有使用ISBISB常用于内存管理,缓存控制以及上下文切换。

数据存储屏障(DMB)

DMB的基本功能如下:
它可以防止对数据访问指令进行重新排序。指定可共享行域内的所有主设备可以看见DMB之前所有的数据访问。DMB还确保在执行后续数据访问之前,所有显式的先前数据(或统一的)缓存维护操作都已经完成。
DMB指令有两个可选参数:操作类型和域。默认的操作类型是“加载和存储”,默认域是“系统”(System)。因此实际上DMBDMB SY的简写。操作类型和域的所有可能组合在任何处理器上都是合法的,即使它没有实现所描述的特定功能,也可以在内部替换成更强的屏障。
在Linux内核中,DMB指令的接口是smp_*mb()一组宏。

#define __smp_mb()	dmb(ish)
#define __smp_rmb()	__smp_mb()
#define __smp_wmb()	dmb(ishst)
#define dmb(opt)	asm volatile("dmb " #opt : : : "memory")

数据同步屏障(DSB)

DSBDMB类似,但是它还会阻止执行任何其他的指令,直到同步完成。
DSB会等待指定共享性域的所有缓存和分支预测器维护操作完成。如果访问类型是“加载和存储”,那么它还将等待TLB维护操作完成。
在Linux内核中,DSB指令的接口是*mb()一组宏。

#define mb()		dsb(sy)
#define rmb()		dsb(ld)
#define wmb()	    dsb(st)
#define dsb(opt)	asm volatile("dsb " #opt : : : "memory")

作用范围或者强度: ISB > DSB > DMB
ISB一般认为是清空流水线,重新获取指令和数据执行。

可共享性域(Shareability domains)

Arm体系架构的内存访问顺序在所谓的“可共享性域”内进行。共享域定义了总线拓扑中的“区域”,在该区域中,内存访问以可预测的方式进行,并可能保持一致(硬件加持)。在此域之外,观察者可能看不到与域内部相同的内存访问顺序。
下表显示了Armv7-A系统中可用的不同共享选项。

缩写描述
Non-shareableNSH此域仅由本地代理组成。永远不需要与其他核、处理器或设备进行同步的访问。通常不用于SMP系统
Inner ShareableISH此域(可能)由多个代理共享,但通常不是系统中所有的代理共享。
一个系统可以有多个内部共享域。影响一个内部共享与的操作不会影响系统中的其他内部共享域。
Outer ShareableOSH此域通常由多个代理共享,并且可能由多个内部共享域组成。影响外部共享域的操作也会隐式地影响其中所有的内部共享域(但不会以其他方式表现为内部共享域操作)。
对于实现了LPAECortex-A15 MPCore等处理器,所有的Device类型的内存访问都被视为可外部共享。对于其他处理器,可显式地设置可共享属性(可共享或者不可共享)。[译者注:Armv8-a架构Device类型的内存访问都是Outer Shareable]
Full SystemSY此域对系统的操作会影响系统中的所有代理,所有非共享域,所有内部共享域,以及所有外部共享域。

Armv6架构不支持单独的外部共享域。
可共享性(Shareability)是基于以下的方式有效地分配给系统中的每个内存事务的:

  • 访问域的内存属性(由MMU转换表决定)
  • 核的配置(在一个多核处理器中核之间可能有所不同)
  • 互连的实现
  • 互连和其他相连的主设备之间的集成

但是也有一些特定的操作(指令或者cp15配置选项)可以在定义其作用范围的域中进行。
下图是可共享域系统的一个例子。每个核的内部都有自己的非共享性域;一个cluster内的核之间是内部可共享域;Mali, Video decode, LCD以及DDR是一个外部可共享域;其他任何不在整个子系统中的东西都只是系统域的一部分。
在这里插入图片描述

屏障域和级别

除非指定了参数,否则屏障默认作用于系统域。所有的屏障指令都可以采用域说明符,所有不支持的域说明符都被认为是系统域。数据屏障还可以采用一个单独的参数(ST表示Store),以指示该屏障只影响存储访问,附近的加载操作可以自由地重新排序。
例如,在不区分共享域的处理器上,DMB ISHST就像DMB SY一样简单地执行,而在有共享域的处理器上,它的效率要高得多。[译者注:觉得应该是当作DMB ST来执行。]

总结

内存访问顺序是一个复杂的主题,希望这三篇文章可以提供一些有用的帮助。有关Arm架构的内存模型以及AMBA互连的更多的信息,请参阅下面列出的资源。

参考文献

Logo

更多推荐