タダノキロク

日々の考えたことの記録 主に無駄なこと

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);
}
  1. preempt_disable()をコール
      カーネル内のプリエンプションを禁止(5.1章に書いているから後で読む)

  2. spin_acuore()をコール
      デバック用の関数っぽい。あとでみる

  3. LOCK_CONTENDEDのマクロを実行

    1. 第二引数(try)の do_raw_spin_trylockに第一引数(_lock)のraw_spinlock_t *lockでスピンロックが取れているか確認。
      実体はarch_spin_trylock。ここでなにかしてるっぽい。
      ここから新規
      よくわからんがticketの概念が在るらしい。共用体とか使ってて分かりづらい。
      一旦、TICKET_SLOWPATH_FLAGは無視する。
      以下 arch_spin_trylockの動作。 現在のlockoldに確保。 oldの先頭headと末尾tailを比較する。
    2. 一致(XORで0になれば)で0をリターン。
    3. 一致でなければ現在のlock.head_tailの最上位ビットを1にする。
      すでに確保したoldと今lockを比較して一致すればnewlockに書き込む。
      (cmpxchgを使用する)
      リターンはold==lockならばoldを、old!=lockならばnewを返す。
    4. lockheadtailが一致していたからlock取得?の場合。
    5. lock_contendedを実行
      いろいろやってるが、__lock_contendedが実体かな?
      なんだかlockdep.cのロック処理チェッカーの関数なので飛ばす。

    6. 第三引数(lock)のdo_raw_spin_lockに第一引数(_lock)で実行。
      (spinlock.hにある。関数の引数の間に__aquires(lock)がついてる関数)
      実体はarch_spin_lockにある。

      1. lock->ticketstailに最下位ビットだけ立てたincxadd
        incには元のlock->ticketsが入っている
        (xaddはデータを交換してから加算)
      2. incheadtailを比較して一致すれば抜ける。
      3. 一致していない場合は、
        • 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 */
}