cli()/sti() 削除ガイド (Ingo Molnar が着手) ---------------------- 2.5.28 から、5 つの有名なマクロが SMP で削除され、UP でも消えていきつつ あります。 cli() sti() save_flags(flags) save_flags_cli(flags) restore_flags(flags) 今までは、cli() により、ドライバコードを割込みハンドラから保護することが 可能でした。しかし今後は、同期処理では、スピンロックやセマフォといった、 より軽量なメソッドを使う必要があります。 たとえば、次のようなことをやっていたドライバコード struct driver_data; irq_handler (...) { .... driver_data.finish = 1; driver_data.new_work = 0; .... } ... ioctl_func (...) { ... cli(); ... driver_data.finish = 0; driver_data.new_work = 2; ... sti(); ... } は、cli() されたセクションが実行されているあいだ、どのような割込みハン ドラ関数 (上の irq_handler() もその一つ) も実行されないことが cli() 関数によって保証されているので、SMP 的に正しいものでした。 しかしこれからは、より直接的なロックメソッドを使わなければなりません。 spinlock_t driver_lock = SPIN_LOCK_UNLOCKED; struct driver_data; irq_handler (...) { unsigned long flags; .... spin_lock_irqsave(&driver_lock, flags); .... driver_data.finish = 1; driver_data.new_work = 0; .... spin_unlock_irqrestore(&driver_lock, flags); .... } ... ioctl_func (...) { ... spin_lock_irq(&driver_lock); ... driver_data.finish = 0; driver_data.new_work = 2; ... spin_unlock_irq(&driver_lock); ... } 上記のコードには、幾つもの良い点があります。 - ロック関係がより理解しやすい - 実際にロックを使用することにより、 クリティカルセクションがはっきりと示されます。cli() の使用はあまりに 不明瞭です。理解しやすくなるということは、デバッグしやすくなるという ことを意味します。 - 速くなります。というのは、スピンロックは、潜在的に大量に使用される IRQ ロックよりも、獲得するまでが速いからです。加えて、driver_lock スピンロックはドライバでしか使用されていないので、ドライバは、例えば 重い SCSI 割込みが終了するのを待つ必要がありません。一方で cli() は 多くのドライバで使用されており、クリティカルセクションを IRQ ハンドラ 関数全体へと広げていたので、深刻なロックの競合を生み出していました。 移行を容易にするため、UP システムで定義されている cli(), sti(), save_flags(), save_flags_cli(), restore_flags() マクロはまだ残しています。 しかし、2.6 がリリースされるまでには、これらが使用されることもなくなって いくことでしょう。 ローカル割込み (カレント CPU での割込み) を無効にしたいドライバでは、 次の 5 つのマクロを使用できます。 local_irq_disable() local_irq_enable() local_save_flags(flags) local_irq_save(flags) local_irq_restore(flags) しかし、これらの意味とセマンティックスは非常にシンプルになっており、古い cli(), sti(), save_flags(flags), restore_flags(flags) のものとは大きく 異なっているので注意してください。SMP での意味は次のようになります。 local_irq_disable() => ローカル IRQ をオフにします。 local_irq_enable() => ローカル IRQ をオンにします。 local_save_flags(flags) => 現在の IRQ 状態を flags に保存します。 状態はオンでもオフでもかまいません。 (アーキテクチャによっては、ほかにも ビットがあります) local_irq_save(flags) => 現在の IRQ 状態を flags に保存し、 割込みを無効にします。 local_irq_restore(flags) => flags から IRQ 状態を復元します。 (local_irq_save は IRQ オン状態と IRQ オフ状態の両方を保存できます。 また、local_irq_restore は IRQ オン状態と IRQ オフ状態の、どちらへも 復元できます。) もう一つの関連する変更点は、synchronize_irq() が現在はパラメータをとり、 synchronize_irq(irq) となっていることです。この変更にはまた、SMP 同期を より軽量にするという目的もあります。このやり方で、対象の割込みハンドラが 終了するのを待つことができます。他の IRQ ソースを待つ必要はありません。 これらの変更はなぜおこなわれたのでしょうか? 主な理由は、cli()/sti() の インターフェースを維持するのが、アーキテクチャ的に重荷となっていたから です。これは、本当に問題となりました。新しい割込みシステムは、非常に能率 的になり、理解しやすくなり、デバッグしやすくなりました。また、少し速く なりました。cli()/sti() を使用しているドライバがスピンロックを使用する ように変更されれば、同じことが起こるでしょう :-)