パワーマネジメント概要(その3)

今回は、前回に続いてカーネル2.6の新しいパワーマネジメント機構における、「デバイス・パワーマネジメント」と「ドライバへの要求」を取り上げる。

カーネル2.6.4がリリースされて、カーネル2.6も少しずつ安定していくとともに、商用ディストリビューションにも徐々に採用されつつある。今回で、カーネル2.6での隠れた目玉機能とも言えるパワーマネジメントの解説を一旦終える事にする。

デバイス・パワーマネジメント

カーネルのCONFIG_PMパラメータが有効の場合、「struct device」はデバイスの現在のパワー状態と、能力について記述しているオブジェクトを含んでいる。デバイスがカーネル2.6の標準的なドライバ・モデル・コアとして登録される場合、この情報はデバイスのディレクトリ中の「power」という名のsysfsファイルによってエクスポートされる。

このファイルを読むことで、以下のようにデバイスの現在のパワー・ステータスを表示できる。

sh-2.05a# cat /sys/devices/pci0/00:00.0/power
unknown 0 enabled
sh-2.05a# cat /sys/devices/pci0/00:02.0/power 
unknown 2 enabled

デバイスの状態は、「Power State」、「Usage Count」、 「Enabled Flag」の3つの要素で示される。

Power State

これは以下のいずれかである。

  • on
  • int1
  • int2
  • off
  • unknown

システムの電源が入れられた場合、デバイスは最初に「unknown」状態となる。多くのドライバが一般的なPM(パワーマネジメント)モデルから教えられるように、それらはデバイスのあるべきパワー状態を、正確にセットするべきである。

「off」は「on」と同様に、最も一般的なPM状態である。もしデバイスがどんな種類のランタイム・パワーマネージメントをサポートする場合、それは「off」になっていることを示す。さらにこれは、デバイスがシステムの状態遷移に入るべき状態であることも表している。

「int1」および「int2」は、いくつかのデバイスがサポートし始めている、「on」と「off」の間の中間状態である。それらは、ロー・パワー・モードと、ロー・レジューム・レイテンシ(低いレイテンシ:短時間でレジュームできる状態)の間の折衷案を表わしている。現在のところは、sysfs経由でどの状態をデバイスがサポートするかを、決める方法はない。

Usage Count

デバイスはそれ自身の電源について、多くの暗黙または明示的に依存するものを持っている。子供のデバイスに電源を入れるために、その親に依存している場合がある。他のデバイスでは、デバイス間の関係を横切る依存性を持っていることがある。「Usage Count」の値は、この各デバイスに依存するユーザの総数である。

ランタイム中は、「Usage Count」による依存ユーザが0のデバイスだけが、サスペンドを許される。

Enabled Flag

各デバイスは、全システムのパワーマネージメントを無効にする機能を持っている。これはまた必然的に、自身のランタイム・パワーマネージメントも無効にする。

sysfsファイルの最終的な値としては、このデバイスがパワーマネージメントを無効にしたかどうかを伝える。典型的には、デバイスはパワーマネージメント制御を決して無効にすべきではない。従って、依存するすべてのデバイスについて報告すべきである。

デバイスのパワー状態をセットするために、ユーザはこのsysfsファイルに記述することができる。有効な値は次の通りである。

  • on
  • int1
  • int2
  • off

このファイルに書くことで、PMコアがこのデバイスの状態を保存し、電源offを引き起こすきっかけとなる。

FIXME:

  • 現在のところ、割り込みを有効にしたまま、デバイスの安全な電源断操作を行なう手段を決めるための方法は無い。

注意点

  • デバイスは、既になっているのと同じ状態になれないことがある。
  • デバイスは、もし最初に「on」状態にならなければ、より高い状態に移れない場合がある。
  • ユーザはこのsysfsファイルを使用して、デバイスの「Usage Count」や「Enabled Flag」を操作しない方がよい。

ドライバへの要求

パワーマネージメントをサポートするために、ドライバは「サスペンド」と「レジューム」のメソッド(手段)を、struct device_driverの中に実装しなくてはならない。より詳細には、新しいドライバモデルに関する文書(Documentation/driver-model/driver.txt)を参照する事。

メソッドは次のように定義される。

enum {
        DEVICE_PWR_ON   = 0,
        DEVICE_PWR_INT1 = 1,
        DEVICE_PWR_INT2 = 2,
        DEVICE_PWR_OFF  = 3,
        DEVICE_PWR_UNKNOWN = 4,
        DEVICE_PWR_MAX  = 5,
};

enum {
        POWER_ON        = 0,
        POWER_STANDBY   = 0x10,
        POWER_SUSPEND   = 0x12,
        POWER_HIBERNATE = 0x14,
        POWER_EMERG     = 0x18,
};

enum {
        SUSPEND_SAVE_STATE,
        SUSPEND_POWER_DOWN,
};

enum {
        RESUME_POWER_ON,
        RESUME_RESTORE_STATE,
};

struct device_driver {
       ...
       int     (*suspend)      (struct device * dev, u32 state, u32 level);
       int     (*resume)       (struct device * dev, u32 level);
};

状態パラメータは、「システム・パワー遷移に入る状態」か、「ユーザがランタイム・パワー遷移の間に要求した特定の状態」のいずれかである。ほとんどのシステム遷移について、デバイスが入るべき状態はDEVICE_PWR_OFFと等価である。またこの状態は、PCIまたはACPIで定義されたD3(SoftOff)と同じである。

一般的には、特別な要求がなければ、デバイスは中間状態に入るべきではない。スタンバイ状態への遷移にさえ、システムはそれをサポートしないことがあるからである。

レベル・パラメータはスリープ・シーケンスの現在の状態である。サスペンド用には次のうちの1つである。

        SUSPEND_SAVE_STATE
        SUSPEND_POWER_DOWN

レジューム用には次のうちの1つである。

        RESUME_POWER_ON
        RESUME_RESTORE_STATE

システムPMの取り扱い

システムPM(システム・パワーマネジメント)とのインタフェイスでは、システム・パワーの遷移の間、ドライバの各メソッドに対する2回の呼び出しがある。呼び出しはシステムのスリープ遷移の実行の際に、以下の手順と条件で行なわれる。

IRQ On      SUSPEND_SAVE_STATE
--------------------------------
IRQ Off     SUSPEND_POWER_DOWN

            RESUME_POWER_ON
--------------------------------
IRQ On      RESUME_RESTORE_STATE

セーブ状態レベルの間、ドライバには次のように期待される。

  • 状態を変えるトランザクションをすべてブロックする
    - 入出力要求
    - ioctls
  • メモリを割り付けて、デバイス状態を保存する
  • デバイス用リソースの開放
  • 他のデバイスへのパワー依存性の解放(後で説明)

パワーダウンおよびパワーオン・レベルの間、デバイスはそのパワーをコントロールすることだけを行っているべきである。システムPMの要求中に、割込みは(ランタイムのPM要求ではないかも知れないが)無効になる。

リストア状態レベルの間、ドライバは次のことをすべきである。

  • (もし必要ならば)デバイスを再初期化する
  • リソースを再び獲得すること
  • デバイスの保存された状態をリストアすること
  • 状態を保存したメモリの開放
  • 他のデバイス上のパワー依存性の回復(後で説明)

パワーマネジメント遷移の全体に渡って、ドライバは次のことを行ってはならない。

  • GFP_KERNELを使用したメモリの獲得
    代わりにGFP_NOIOまたはGFP_ATOMICを使用すること
  • 何らかの手段でユーザスペースとやり取りすること。
    サスペンド・プロセスがカーネルに入る前に、ドライバのユーザスペース・コンポーネントに通知されるべきである。
    これをするためのメカニズムはまだ定義されない。しかしそれをカーネル中で実行すべきではないことは明確である。

ランタイムPMの取り扱い

ランタイムPM(ランタイム・パワーマネジメント)は、2、3の例外を除いて、このレベルのシステムPMに非常に似ている。

類似性:

  • サスペンドする場合にドライバは、suspend()メソッドへの2つの呼出しを順に受け取る。1つ目は「Save State」(状態の保存)で、もう1つは「パワーダウン」(電源断)である。
  • ドライバはさらに、resume()への2つの呼出しを受け取る。1つ目が「Power On」(電源投入)で、もう1つが、「Restore State」(状態の復帰)である。

相違性:

  • すべての要求は、割り込み可能とともにくる。もしデバイスが割込みを無効にすることを要求する場合、それは次の手順を使用して、チェックすべきである:
   int irqs_disabled(void); 

   [ Returns true if IRQs are disabled.]

デバイスは、割込みが可能で、パワーダウン・シーケンスの間に無効になることを要求する場合、処理を継続するべきでない。

  • 他に何かあったかも知れない...

Usage Count

「Usage Count」は、いくつかのデバイスが他のデバイスに対して持っている電力供給の依存性を表わす。最も一般的なインスタンスは親子関係である。子供は、電源を入れて貰う親に依存する。さらにそれは、ファンキーなボード・レイアウトで実現しているような依存性を含ませることもできる。

未使用のデバイスだけがサスペンドできる。また、親デバイスは子供より前にはサスペンドされない。祖先の関係は、パワーマネジメント・コアによって暗黙的に扱われる。従ってデバイスはそれに直接対処する必要はない。デバイスがサスペンドするか、削除されたか、シャットダウンする場合、全ての明示的な依存性も削除されるべきである。デバイスが初期化されるか、レジュームする場合は、依存性を回復することができる。

次のファンクションはデバイスのための「Usage Count」を設定するために使用することができる。

void device_pm_get(struct device * dev);
void device_pm_get_locked(struct device * dev);
void device_pm_put(struct device * dev);
void device_pm_put_locked(struct device * dev);

ドライバのsuspend()およびresume()メソッドは、これらのファンクションとともに、内部のセマフォに依存している。従ってドライバは、ロック付バージョンで呼ぶべきである。

Disability Count(無効カウント)

デバイスはサスペンドへの遷移を安全に実現できないということを、ドライバが知っている時が来た。この実例はあまり無いが、次のようにデバイスにさもなければ損害をもたらすような極限状況に制限されている。

  • ファームウェアのフラッシュ中
  • デバイスが明示的にinitシーケンスに対して、デバイスへの損害を防ぐことを要求している時

これらの滅多に無い状況に対処するために、デバイスの無効カウント(Disability Count)が設定される場合がある。

基本はこれを使用しないこと!

正の値の無効カウントを持ったデバイスは、デバイスだけでなく全システムがサスペンドするのを防げる事になる。

もしこれを使用しなければならない場合は、クリティカルなオペレーション中にだけデバイスを無効にして、必ずそれが終わるとすぐに、有効にする必要がある。

これの実装をする時には注目されて問題になるかも知れない。また同意が得られなければ、これの呼出しはドライバから取り除かれるだろう。