ARM64 記憶體屏障
-v0.1 2018/5/21 Sherlock init
-v0.2 2018/5/27 Sherlock add write sample
本文試圖梳理aarch64構架下的記憶體屏障的邏輯,細節的東西還要去看ARMv8的手冊。其實,
“ARM Cortex-A Series Programmer’s Guide for ARMv8-A”這本書的第13章,memory
order已經對記憶體屏障的内容做了比較入門的講解。
要了解記憶體屏障,要知道memory的一些特性,這裡的memory不是隻指記憶體。而是從CPU角度
看到的存儲空間。如果CPU對一系列的指令執行是嚴格串行的,我們是不用外加上面記憶體
屏障的。比如:
str x1 [x2] // A
str x2 [x3] // B
A指令完全執行完,B指令才執行。這樣根本不需要記憶體屏障指令的介入。
但是現在處理器和記憶體系統之間的速度差距已經非常大,如果要上面的A執行完成才執行B
cpu要消耗大量的時間幹等在那裡。為了緩解CPU和記憶體系統間的速度差距,同時也為了不斷
提升CPU的效率,現代CPU存在很多指令執行上的技術,導緻的結果是指令執行的結果和之前
的邏輯已經不一樣了。
CPU上存在着多種指令執行的技術,其中導緻需要加上記憶體屏障執行的是CPU上的指令亂序
執行。
為了解釋清楚CPU執行亂序執行需要引入兩個基本的概念,一個是記憶體類型(memory type),
另外一個是master/slave.
記憶體類型定義的CPU和記憶體互相作用時的一些性質,記憶體類型隻有normal和device兩種,
normal基本上可以對應DDR,device基本上可以對應裝置的MMIO. normal的記憶體可以配置成
cacheable和non-cacheable, 其中cacheable的記憶體屬性又可以進一步配置shareability的屬性。
device的記憶體的屬性使用GRE表述,G是gather,R是re-order, E是write early aknowledge,
每種屬性可以是nX(e.g. nG), 就是不支援的意思。關于normal和device下面的諸多屬性,
可以查閱本文開始時提到的ARM程式設計手冊13章中的内容。
master和slave是晶片裡面的概念,master是一個動作的發起者,slave是一個動作的接收
者。一般的一個晶片裡,master有各個core,以及外設的DMA控制器。slave有記憶體和MMIO.
基于以上的概念,我們看記憶體屏障為啥有存在的意義。master(CPU, 不确定DMA是不是這樣)
對normal記憶體的操作,存在很多提升效率的處理(多發射,亂序執行,執行預取…).
我們回到亂序執行上來。ARMv8的CPU對指令亂序執行的幾條規則是這樣的:
-
在單一的core上,指令完成的順序是串行的。注意,master收到slave的(有時不一定是
slave, 比如,device記憶體的E屬性)完成資訊為一個執行完成。
- 在單一的core上下發的指令,slave上完成的順序是無法保證串行的。
-
多個master上看到的操作是不保序的。
這樣就會導緻:
在一個core上:
等待A條件出現
A條件出現後執行B動作
如果B動作先被執行,則可能出錯。是以要在之間加上記憶體屏障:
等待A條件出現
記憶體屏障
A條件出現後執行B動作
在多個core上:
core 1 core 2 DMA check flag set flag get date from DMA range
core1的邏輯是先用DMA搬資料,搬完資料後設立一個标記位。core2不斷的在檢測标記
位,當檢測到标記位的時候,core2就可以使用DMA搬好的資料了。
但是,core1的DMA和set flag兩個操作可能是亂序執行的,可能在core2看來flags已經
置位了,但是其實DMA的資料還沒有搬完, 這時,如果core2去使用資料,就有可能出錯。
正确的做法是在DMA和set flag執行加上記憶體屏障,確定DMA完成了,再去set flag.
注意,這裡其實就是上面的第三點。另外,這裡和cache一緻性沒有關系, 如果,我們
單獨看dma,或者單獨看flag,兩者各自都是硬體保證cache一緻性的。
具體來說ARMv8上的記憶體屏障執行有ISB,DMB,DSB。ISB和指令相關,後面兩個和記憶體
通路相關。具體的差別可以查文章開頭提到的書。
具體驅動代碼, 可以參考下arm64下幾組read/write的實作,代碼在arch/arm64/include/asm/io.h.
可以看到同一個write函數有一下三種類型:
__raw_writeb
writeb_relaxed(v,c)
writeb(v,c)
第一個是str指令的封裝,第二個用于I/O記憶體也是str的封裝,第三個可以用于normal的
記憶體讀寫,其實是真是write之前加了一個DSB的記憶體屏障指令。
未完待續…