5章同期処理#2 20160207Linux Kernel 勉強会 #18
詳細Linuxカーネル 5章 同期処理を読む(続き)
前回力尽きた、カーネルが使用している同期技法のスピンロックを読む。
スピンロックの確保 spin_lock()
スピンロックを確保するための関数 spin_lock()
は、
spinlock_t
構造体のアドレスを引数にとる。
Spin_lock()
は raw_spin_lock()
->_raw_spin_lock()
ー>__raw_spin_lock()
-の順にコール/マクロ化され、実体は__raw_spin_lock
になる。
static inline void __raw_spin_lock(raw_spinlock_t *lock) { preempt_disable(); spin_acquire(&lock->dep_map, 0, 0, _RET_IP_); LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock); }
preempt_disable()
をコール
カーネル内のプリエンプションを禁止(5.1章に書いているから後で読む)spin_acuore()
をコール
デバック用の関数っぽい。あとでみるLOCK_CONTENDED
のマクロを実行- 第二引数(
try
)のdo_raw_spin_trylock
に第一引数(_lock
)のraw_spinlock_t *lock
でスピンロックが取れているか確認。
実体はarch_spin_trylock
。ここでなにかしてるっぽい。
ここから新規
よくわからんがticketの概念が在るらしい。共用体とか使ってて分かりづらい。
一旦、TICKET_SLOWPATH_FLAG
は無視する。
以下arch_spin_trylock
の動作。 現在のlock
をold
に確保。old
の先頭head
と末尾tail
を比較する。 - 一致(XORで0になれば)で0をリターン。
- 一致でなければ現在の
lock.head_tail
の最上位ビットを1にする。
すでに確保したold
と今lock
を比較して一致すればnew
をlock
に書き込む。
(cmpxchg
を使用する)
リターンはold==lock
ならばold
を、old!=lock
ならばnew
を返す。 lock
のhead
とtail
が一致していたからlock取得?の場合。lock_contended
を実行
いろいろやってるが、__lock_contended
が実体かな?
なんだかlockdep.c
のロック処理チェッカーの関数なので飛ばす。第三引数(
lock
)のdo_raw_spin_lock
に第一引数(_lock
)で実行。
(spinlock.hにある。関数の引数の間に__aquires(lock)
がついてる関数)
実体はarch_spin_lock
にある。-
lock->tickets
とtail
に最下位ビットだけ立てたinc
をxadd
inc
には元のlock->tickets
が入っている
(xadd
はデータを交換してから加算) -
inc
のhead
とtail
を比較して一致すれば抜ける。 - 一致していない場合は、
inc.head
に最新のlock->tickets.head
をリード。inc.tail
(元々)とinc.head
(最新)を比較(__tickets_equal)。- 一致するまで
SPIN_THRESHOLD
回繰り返す。 - 一致したら(
goto clear_slowpath
)で後処理をするらしい。
ここまでで時間切れ!
-
- 第二引数(
疑問点。
* どこでぐるぐるスピンロックを待っているのか。
* tryでロックが取れないと終わっているように見える。
#define LOCK_CONTENDED(_lock, try, lock) \ do { \ if (!try(_lock)) { \ lock_contended(&(_lock)->dep_map, _RET_IP_); \ lock(_lock); \ } \ lock_acquired(&(_lock)->dep_map, _RET_IP_); \ } while (0)
static __always_inline int arch_spin_trylock(arch_spinlock_t *lock) { arch_spinlock_t old, new; old.tickets = READ_ONCE(lock->tickets); //現在のlockからticketsを確認 if (!__tickets_equal(old.tickets.head, old.tickets.tail)) // return 0; new.head_tail = old.head_tail + (TICKET_LOCK_INC << TICKET_SHIFT); new.head_tail &= ~TICKET_SLOWPATH_FLAG; /* cmpxchg is a full barrier, so nothing can move before it */ return cmpxchg(&lock->head_tail, old.head_tail, new.head_tail) == old.head _tail; }
static __always_inline void arch_spin_lock(arch_spinlock_t *lock) { register struct __raw_tickets inc = { .tail = TICKET_LOCK_INC }; inc = xadd(&lock->tickets, inc); if (likely(inc.head == inc.tail)) goto out; for (;;) { unsigned count = SPIN_THRESHOLD; do { inc.head = READ_ONCE(lock->tickets.head); if (__tickets_equal(inc.head, inc.tail)) goto clear_slowpath; cpu_relax(); } while (--count); __ticket_lock_spinning(lock, inc.tail); } clear_slowpath: __ticket_check_and_clear_slowpath(lock, inc.head); out: barrier(); /* make sure nothing creeps before the lock is taken */ }