ARM命令LDREX和STREX实现spinlock

时间:2022-01-30 01:21:03

在 include/asm-arm/spinlock.h 下有這麼一段

#if __LINUX_ARM_ARCH__ < 6
#error SMP not supported on pre-ARMv6 CPUs
#endif

好啦,前提就是:只有 ARM core 版本 >=6 才可以繼續

all spin lock primitives 到最後都是使用下面這個基本型: 

static inline void __raw_spin_lock(raw_spinlock_t *lock)
{
    unsigned long tmp;

1      __asm__ __volatile__(
2"1:    ldrex    %0, [%1]\n"
3"    teq    %0, #0\n"
4"    strexeq    %0, %2, [%1]\n"
5"    teqeq    %0, #0\n"
6"    bne    1b"
7   : "=&r" (tmp)
8  : "r" (&lock->lock), "r" (1)
9 : "cc");

    smp_mb();
}

[指令重點]:

ldrex 指令是 core 6 以後才有的,跟 strex 配成一對指令,可以請 bus 監控從 ldrex 到 strex 之間有無其他的 CPU 或 DMA 來存取這個位址 ,若有的話,strex 會在第一個 register 裡設定值為 1(non-exclusive by this CPU) 並且令 store 動作失敗,若沒有,strex 會在第一個 register 裡設定值為 0(exclusive access by this CPU) 並且令 store 動作成功。

Code Trace Discussion: 

Line 1: __volatile__ 告訴 compiler ,不要對這塊 assembly template 做最佳化動作,因為我們裡面有 loop 讀取 memory 動作,最佳化的結果可能導致 compiler 用一個 register 來 cache 它的值,不會老老實實的去讀 memory... 呵呵,這不是我們想要的動作喔!

Line 2: 把 lock 讀到 tmp,並請 bus monitor 這個 memory

Line 3: 測試 lock 是否為 0,若非 0,表示 lock 已經被別人取得了,則 Line 4,5 都不做了,然後  Line 6 一定 branch,做 spin 的動作。若為 0,表示有機會取得 lock,繼續做 Line 4.5. 

Line 4: 重點來了!,核對 bus monitor 的結果,若是exclusive access 則 tmp 設為 0 並且把 1 儲存到 lock,若是 non-exclusive access(有其他 CPU 來動過) 則 tmp設為 1並且不做儲存 lock 的動作。

Line 5: 測試 tmp

Line 6: 若 tmp 為 0 表示剛剛對 lock 動作是 exclusive,可以離開迴圈,若 tmp 為 1,則做 spin 動作。

Line 7: tmp 用 register 來操作,同時是 input 及 output 令它為 %0

Line 8: &lock->lock 用 register 來操作 ,令它為 %1,值 1 用用 register 來操作 ,令它為 %2

Line 9: 此 template 會改到 condition code,加入 clobber list 以告訴 compiler 有這回事

好了,終於看完了,真的很佩服那些 coding 的 kernel hackers ....

思考問題: ARM v6 以前有個  SWP 指令可以 lock bus and swap memory ,一樣可以用來完成 exclusive access ,但是比起 ldrex,strex 這對指令有什麼缺點呢?

ANS: 用滑鼠反白下幾行

SWP lock bus,其他 CPU 所有動作都不能做,但是 ldrex,strex就不會有這種現象,使用 ldrex,strex 時若其他 CPU不來 access 這個特定的 memory 就可以平行的做動作,增加平行執行的 performance


原文链接:http://blog.roodo.com/use_the_force/archives/3420371.html.