#はじめに
いまさらですが、Linuxの排他制御機構Read-copy Update (RCU)のコードを読んでみたいと思います。コードベースはLinux 4.3-rc7です。
RCUとはどういうものかについては説明しません。RCUの作者であるPaul E. McKenneyさんがたくさんドキュメントを書いてくれているので、そちらを読む方が正確で確実です。
#RCU
##rcu_read_lock, rcu_read_unlock
RCUのread側のためのAPIで、普通のロックと同じように、クリティカルセクションの最初でlockを、最後でunlockを実行します。lockと書いてありますが、ロックを取るわけではありません。
###rcu_read_lock
rcu_read_lock
の実際のコードは以下のようになります。
static inline void rcu_read_lock(void)
{
__rcu_read_lock();
__acquire(RCU);
rcu_lock_acquire(&rcu_lock_map);
RCU_LOCKDEP_WARN(!rcu_is_watching(),
"rcu_read_lock() used illegally while idle");
}
この中でRCUとして意味のあることをやっているのは__rcu_read_lock
のみであとはデバッグ用のコードです。
__rcu_read_lock
はCONFIG_PREEMPT_RCU
が有効か否かで動作が変わります。手元の環境では無効だったので、その前提で読んでいきます。
__rcu_read_lock
はpreempt_disable
を呼ぶだけです。preempt_disable
は名前の通り、コンテキスト切り替えを無効にします。これは無印RCUの制約である、read側のクリティカルセクションではコンテキスト切り替えが起きてはいけない、を満たすためのものだと思います1。クリティカルセクション内で自発的にスリープやブロックしないようにするのはプログラマの責任です。
###rcu_read_lockのデバッグコード
せっかくなので、他のデバッグ用コードも読んでいきます。
__acquire(RCU)
はマクロで、デフォルトでは何もしません。__CHECKER__
が定義されている場合は、__context__(RCU,1)
に展開されます。
__CHECKER__
はどうやらSparseという静的解析ツールを使うときに定義されるようです2。詳しい説明は省きますが、取っていないロックをアンロックしようとしたり、ロックをとったままコンテキスト切り替えをしようとするようなバグを検出できるようです。
rcu_lock_acquire(&rcu_lock_map)
はCONFIG_DEBUG_LOCK_ALLOC
とCONFIG_LOCKDEP
が有効のときに、ロックのデバッグ機能(lockdep)のための処理を行ないます。詳しい説明は省きますが、lockdepは複雑なロックの依存関係のバグ(デッドロック等)を検出するためのものです。
最後のRCU_LOCKDEP_WARN
はdynticks絡みのassertionのようですが、複雑な事情 [pdf]があるようで、正確なところはわかりません。時間があれば掘り下げるかもしれません。
###rcu_read_unlock
static inline void rcu_read_unlock(void)
{
RCU_LOCKDEP_WARN(!rcu_is_watching(),
"rcu_read_unlock() used illegally while idle");
__release(RCU);
__rcu_read_unlock();
rcu_lock_release(&rcu_lock_map); /* Keep acq info for rls diags. */
}
rcu_read_unlock
は基本的にrcu_read_lock
の逆操作をしているだけのようです。__rcu_read_unlock
もpreempt_enable
しているだけです。
#おわりに
次は、synchronize_rcu
あたりを読みたいと思います。