パワーマネジメント概要(その2)
システム・パワーマネジメント
ステイタス
パワーマネージメント・モデルでは、システム・パワーマネージメント状態を次の5つのステイタスとして、一般化する。
- On
- Standby
- Suspend
- Hibernate
- Emergency
たとえ異なるプラットフォームで、上記の各状態と異なる名前を持つ場合があっても、それらは一般に同じものとして定義する。
On は、勿論オン状態である。システムは活動中である。
Standbyは、低いレイテンシおよびローパワー状態である。この状態でもし、最小限といえどもシステム・コンテキストがあれば、失われる。システム・コンテキストが失われない場合これは、ACPI S1と等価である。必ずしも必要ではないが、(中間的なデバイス・パワー状態が実現できれば、Standbyにとって理想的である)が、デバイスはローパワー状態にサスペンドされる場合がある。
Suspendは、Suspend-To-Ram状態である。この状態では、メモリのコンテンツはハードウェアによって維持される。他のすべてのシステム・コンテキストは失われる。デバイスはローパワー状態に置かれると予想される。
Hibernateは、Suspend-To-Diskである。この状態では、メモリのコンテンツは待機手順の間にディスク、または他の永続性がある記憶装置に書き出され、レジューム/リブート時にメモリに読み出される。
Emergencyは優先度の高いSuspend-To-Diskである。典型的な例としては、ラップトップ・コンピュータのバッテリ・パワーが危機的に低い場合に使用される。
ユーザ・インタフェイス
「パワーマネージメント・ ハンドラ」は、1つ以上のシステム・パワーマネージメント状態をサポートすることができるドライバである。システムでは、一度に複数のハンドラを登録されることがある。それらでは各スリープ状態を、オーバラップして、または排他的にサポートする。ユーザは、どのハンドラを各スリープ状態で使用したいか決定することができる。
PMモデルは、「/sys/power」のsysfsディレクトリを通して見る事ができる。このディレクトリは典型的には次のように見える:
tree /sys/power/ /sys/power/ |– acpi | `– states |– emerg |– hibernate |– power_state |– standby `– suspend
この場合は「acpi」だけの例だが、ディレクトリはPMコアで登録されるハンドラを表わす。各ディレクトリ中の「states」ファイルは、ハンドラがサポートするパワー状態を示す。
「power_state」以外のレギュラー・ファイルは、各々のパワー状態を表わす、そしてハンドラは、それらの状態を使用するためにインストールされる。この各ファイルから読むことで、PM ハンドラ(ディレクトリのうちの1つと同じ名前)の名前、あるいはハンドラがインストールされない場合には「none」を表示できる。
状態ファイルに書くことは、ハンドラをセットさせる。ファイルに書かれた名前は、ハンドラの名前と正確に(即ちディレクトリの名前)一致させる必要がある。ファイルに「none」を書くことは、ハンドラをNULLにリセットさせる。
ファイル「power_state」は、システム・サスペンド・シーケンスを起こすために使用される。このファイルから読むことで、有効なシーケンスの名前を表示させることができる。このファイルに書かれる値は、状態の名前と正確に一致させる必要がある。それはディレクトリ中の1つのファイルの名前である。
TODO:
カーネル・コマンドライン経由でPM ハンドラをセットする方法を決定すること。
パワーマネージメント・ハンドラ
enum { POWER_ON = 0, POWER_STANDBY = 0x10, POWER_SUSPEND = 0x12, POWER_HIBERNATE = 0x14, POWER_EMERG = 0x18, }; struct suspend_ops { u32 state_mask; int (*prepare) (u32 state); int (*store) (u32 state); int (*sleep) (u32 state); int (*load) (u32 state); int (*cleanup) (u32 state); struct kobject kobj; }; extern int suspend_ops_register(struct suspend_ops *); extern void suspend_ops_unregister(struct suspend_ops *);
PM ハンドラは、スリープ状態をプラットホームに入力する操作の、そのプラットホーム固有のセットである。さらにそれは、サポートするスリープ状態のマスクを含んでいる。ハンドラはスタートアップで登録されるべきであるが、それがローダブル・モジュールに存在する場合には、未登録となる。定義の例としては、次のようになる。
static struct suspend_ops acpi_suspend_ops = { .state_mask = (POWER_STANDBY | POWER_SUSPEND), .kobj = { .name = “acpi” }, };
これは、操作が何も無いのであまり有用ではないのだが、ハンドラを登録するために必要とされる最低量のコードである。
メソッドは、サスペンド/レジュームのシーケンス中で順に呼ばれる。NULLメソッドは無視される。ハンドラは、スリープの状態によってはハンドラの中で何もしない場合がある。
prepare()は、スリープ状態に入っているとプラットホームに通知し、そして必要な準備を行う。プラットホームは、知っている限りのことを確認するべきである。それによって、安全にその状態にサスペンドすることができる。
store()は、プラットホームが永続性がある記憶装置にメモリ・イメージを書くためのトリガである。これがStandbyかSuspend操作である場合、プラットホームは何もするべきでない。イメージを書き出すデバイスやその書き出し形式を決めることは、プラットホーム次第である。
sleep()は実際にシステムをスリープ状態にするコードを、プラットホームに実行させる。プラットホームでの全てのlow-levelの退避とレジュームを行うコードをここで扱うとともに、また正確に退避したのと同じ場所から実行を再開させるだろうと想定している。
注:
実際には、退避されたイメージがディスクからロードされる前に、カーネルが新しいカーネルでブートし始めるので、Suspend-to-diskのスリープ状態にはこの問題がない。一般的にはこの問題は解決されるべきだが、まだ対処されていない。(訳注:現在は対処されているので、この問題は無い)
load()はプラットホームに、システム・イメージを以前保存した永続性がある記憶装置からロードさせる。対象となるデバイスやその形式を決めることは、プラットホーム次第である。
cleanup()は最後に呼ばれる。これはプラットホームにとって、これらの手順で使用されたプラットホームで特有の項目の後始末をさせる機会でもある。
パワーマネジメント・シーケンス
システム・スリープのトランザクションには、イベントのセット・シーケンスがある。そこでは、ハンドラのメソッド呼び出しとサスペンドするデバイスの組合せを展開させる。
メモリ状態が保存されたままだったか否かに依存するので、レジュームのパスに差がある。メモリが保存されていれば、正確に終わったのと同じ命令に返ることが可能である。そうでなければカーネルは明示的に、適切にレジュームが処理されたかをチェックする関数を呼ばなければならない。これは現在、swsuspが行っている事で、一般的なPMモデルの物真似には十分だろう。
イベントのシーケンスは次のようになっている。
- デバイスがサスペンドできる事の確認
- prepare()メソッドの呼び出し
- デバイス状態の保存
- store()メソッド呼び出し
- IRQ無効化
- 全てのデバイスの電源断
- sleep()メソッドの呼び出し
Standby/Suspend用
- デバイスの電源ON
- IRQを有効化Enable IRQs.
- load() メソッド呼び出し
- 全てのデバイス状態のリストア
- cleanup()メソッドの呼び出し
Hibernate/Emergency用
- load()メソッドの呼び出し
- 全てのデバイス状態のリストア
- cleanup()メソッドの呼び出し
(レジュームで呼出されるの時までに、既にIRQsは有効になっていて、デバイスも調査されているはずである)
次回は、デバイス・パワーマネジメントについて記述する。