Linux Kernel 2.6 Documentation:
/usr/src/linux/Documentation/cli-sti-removal.txt
cli-sti-removal.txt
cli()/sti() 削除ガイド
[プレインテキスト版]
- 原著作者: unknown
- 翻訳者: 川崎 貴彦 <takahiko(a)hakubi.co.jp>
- バージョン: 2.6.5
- 翻訳日時: 2004/05/04
cli()/sti() 削除ガイド (Ingo Molnar <mingo@redhat.com> が着手)
----------------------
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() を使用しているドライバがスピンロックを使用する
ように変更されれば、同じことが起こるでしょう :-)
Linux カーネル 2.6 付属文書一覧へ戻る