在 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.