
  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() を使用しているドライバがスピンロックを使用する
ように変更されれば、同じことが起こるでしょう :-)
