天天看點

ARM64 記憶體屏障

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對指令亂序執行的幾條規則是這樣的:

  1. 在單一的core上,指令完成的順序是串行的。注意,master收到slave的(有時不一定是

    slave, 比如,device記憶體的E屬性)完成資訊為一個執行完成。

  2. 在單一的core上下發的指令,slave上完成的順序是無法保證串行的。
  3. 多個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的記憶體屏障指令。

    未完待續…

繼續閱讀