#author("2017-09-08T12:30:01+09:00","default:afuruta","afuruta") #author("2017-09-08T14:03:38+09:00","default:afuruta","afuruta") * /sys/power/state [#ncb3a707] 次の表は /sys/power/state (kernel 内対応関数 &ogdefs(state_store(),state_store,main.c);) に書き込む文字列と kernel の挙動です。PC (パソコン) で調査したのでアーキテクチャやプラット・ホームの機能で詳細な挙動に違いがあるかもしれません。関連ドキュメントは &ogfileone(/Documentation/power/states.txt); です。 |書き込む文字列 (kernel 内定数)|プロセス稼働|CPU稼働|主記憶内容|電力管理|ACPI 相当|主要な &ogdefs(dev_pm_ops); call back&sup([1]);|h |freeze&br;(&ogdefs(PM_SUSPEND_FREEZE);)|停止|継続 (idle 状態が長くなる)|保持|周辺 IO デバイス 低消費電力化|S0|susped - (wake up) - resume| |standby&br;(&ogdefs(PM_SUSPEND_STANDBY);)|停止|boot processor は最小限の内部状態を維持して停止&sup([2]);、それ以外の processor は内部状態を失って offline。復帰過程では boot processor が復帰後、その他の processor を online にしている|保持|周辺 IO デバイス + CPU とその周辺 + 主記憶 低消費電力化|S1|susped - (wake up) - resume| |mem&br;(&ogdefs(PM_SUSPEND_MEM);)|停止|boot processor は内部状態が待避されて停止&sup([3]);、それ以外の processor は内部状態を失って offline&br;(全停止に至る過程で boot processor 以外を offline にしている。復帰過程も boot processor 復帰後、その他の processor を online にしている)|保持|周辺 IO デバイス + CPU とその周辺 + 主記憶 低消費電力化&br;(standby よりも多くの復帰操作が必要な積極的な低消費電力化)|S3|susped - (wake up) - resume| |disk&br;(&ogdefs(PM_SUSPEND_MAX);)|停止|内部状態を失って全停止&br;(全停止に至る過程で boot processor 以外を offline にして hibernation image 作成、online にして hibernation image を swap 領域へ書き出している)|swap 領域へ待避した後、喪失|wakeup に必要な回路以外は電源 off|S4|freeze - (hibernation image 作成) - thaw - (hibernation image 書き出し) - poweroff - (power_down) - (wake up) - restore| &sup([1]); call back のうち prepare, complete, *_early, *_late, *_noirq, runtime_* は省略してあります。 &sup([2]); CPU に電源が供給されています。CPU 内部の詳細は公開されていないので推測するしか無いのですが、スタティック動作の回路は状態を維持します。ダイナミック動作の回路は状態を失い、PLL の様な clock 供給・分配系の回路は停止すると考えられます。 &sup([3]); 一般的に専用のハードウエアが processor の内部状態を待避復旧しています。このような専用のハードウエアは簡易なプロセッサとプログラムの組み合わせで構成されることもあります。 state ノードに "freeze" を書き込むと単に process が停止し device が suspend するのに対し、kernel 内の dev_pm_ops の freeze メンバ(call back pointer) は "disk" を書き込み hibernate (swap 領域にメモリ内容を書き出し電源 off) する時に呼ばれます。注意が必要です。 #textbox(note,suspend - resume が上手くいかない場合){{ debugfs が /sys/kernel/debug に mount されているならば、&span(ConsoleOut){# };&span(ConsoleIn){cat /sys/kernel/debug/suspend_stats}; (kernel 内対応関数 &ogdefs(suspend_stats_show());, &span(ConsoleOut){# };&span(ConsoleIn){cat /sys/kernel/debug/wakeup_sources}; (kernel 内対応関数 &ogdefs(wakeup_sources_stats_show());) で suspend, resume, freeze 周辺の問題の要約を表示できます。&ogfileone(/Documentation/power/basic-pm-debugging.txt); にテストとデバッグの方法が書かれています。 }} * dev_pm_ops call back が呼ばれるまでの call trace - platform_device の場合 [#ic2dd772] &ogdefs(dump_stack()); を使って dev_pm_ops メンバが指している call back 関数が呼ばれるまでの call trace を列挙します。i8253_ref ドライバを修正して取得しました。 &ogdefs(platform_device); 以外の device では途中から call path が変化します。 ** state = { freeze, standby, mem } [#w1f5f63f] state ノードに "freeze", "standby", "mem" を書き込むと &ogdefs(dev_pm_ops); メンバの suspend, resume を呼び出します。 *** suspend [#af86db45] state に "mem" を書き込んだ時に採取されたログです。kernel 内で file system 処理を通過した後、書き込まれた文字列を解釈する関数は &ogdefs(state_store(),state_store,power); です。"mem" を &ogdefs(PM_SUSPEND_MEM); に変換する関数が &ogdefs(decode_state()); です。 #pre(soft){{ [85109.654592] Call Trace: [85109.654609] [<ffffffff817d2279>] &ogdefs(dump_stack);+0x63/0x81 [85109.654618] [<ffffffffc033d75d>] i8253_ref_suspend+0x4d/0x60 [i8253_ref] [85109.654627] [<ffffffff81474ade>] ? acpi_thermal_resume+0xfa/0xfa [85109.654634] [<ffffffff8150d829>] &ogdefs(platform_pm_suspend);+0x29/0x50 [85109.654641] [<ffffffff815187ae>] &ogdefs(dpm_run_callback);+0x4e/0x150 [85109.654647] [<ffffffff81519542>] &ogdefs(__device_suspend);+0x132/0x3b0 [85109.654651] [<ffffffff8151b27a>] &ogdefs(dpm_suspend);+0x13a/0x310 [85109.654654] [<ffffffff8151b967>] &ogdefs(dpm_suspend_start);+0x57/0x60 [85109.654660] [<ffffffff810c8c2f>] &ogdefs(suspend_devices_and_enter);+0x8f/0x780 [85109.654664] [<ffffffff810c9416>] &ogdefs(pm_suspend);+0xf6/0x3b0 [85109.654668] [<ffffffff810c7d69>] &ogdefs(state_store);+0x79/0xf0 [85109.654676] [<ffffffff813b33df>] &ogdefs(kobj_attr_store);+0xf/0x20 [85109.654681] [<ffffffff81275bfd>] &ogdefs(sysfs_kf_write);+0x3d/0x50 [85109.654687] [<ffffffff812750aa>] &ogdefs(kernfs_fop_write);+0x12a/0x180 [85109.654692] [<ffffffff811fa198>] &ogdefs(__vfs_write);+0x28/0xf0 [85109.654697] [<ffffffff811fcda9>] ? __sb_start_write+0x49/0xf0 [85109.654705] [<ffffffff81320373>] ? security_file_permission+0x23/0xa0 [85109.654709] [<ffffffff811fa889>] &ogdefs(vfs_write);+0xa9/0x1b0 [85109.654713] [<ffffffff811fb656>] &ogdefs(SyS_write);+0x46/0xb0 [85109.654719] [<ffffffff81218742>] ? __close_fd;+0x82/0xa0 [85109.654725] [<ffffffff817da0b2>] &ogdefs(system_call_fastpath);+0x16/0x75 [85109.654743] i8253_ref i8253_ref.0.auto: Suspended. }} *** resume [#wa75f719] 前の節 suspend の続きです。call trace を見ると &ogdefs(suspend_devices_and_enter()); までは全く同じ trace になっています。 #pre(soft){{ [85110.121742] Call Trace: [85110.121761] [<ffffffff817d2279>] &ogdefs(dump_stack);+0x63/0x81 [85110.121773] [<ffffffffc033d8cd>] i8253_ref_resume+0x4d/0x60 [i8253_ref] [85110.121781] [<ffffffff8150d87b>] &ogdefs(platform_pm_resume);+0x2b/0x50 [85110.121789] [<ffffffff815187ae>] &ogdefs(dpm_run_callback);+0x4e/0x150 [85110.121794] [<ffffffff81518d76>] &ogdefs(device_resume);+0xd6/0x200 [85110.121798] [<ffffffff8151a479>] &ogdefs(dpm_resume);+0x129/0x320 [85110.121801] [<ffffffff8151aa55>] &ogdefs(dpm_resume_end);+0x15/0x30 [85110.121808] [<ffffffff810c8cc7>] &ogdefs(suspend_devices_and_enter);+0x127/0x780 [85110.121812] [<ffffffff810c9416>] &ogdefs(pm_suspend);+0xf6/0x3b0 [85110.121816] [<ffffffff810c7d69>] &ogdefs(state_store);+0x79/0xf0 [85110.121827] [<ffffffff813b33df>] &ogdefs(kobj_attr_store);+0xf/0x20 [85110.121831] [<ffffffff81275bfd>] &ogdefs(sysfs_kf_write);+0x3d/0x50 [85110.121839] [<ffffffff812750aa>] &ogdefs(kernfs_fop_write);+0x12a/0x180 [85110.121845] [<ffffffff811fa198>] &ogdefs(__vfs_write);+0x28/0xf0 [85110.121850] [<ffffffff811fcda9>] ? __sb_start_write+0x49/0xf0 [85110.121860] [<ffffffff81320373>] ? security_file_permission+0x23/0xa0 [85110.121864] [<ffffffff811fa889>] &ogdefs(vfs_write);+0xa9/0x1b0 [85110.121868] [<ffffffff811fb656>] &ogdefs(SyS_write);+0x46/0xb0 [85110.121874] [<ffffffff81218742>] ? __close_fd+0x82/0xa0 [85110.121881] [<ffffffff817da0b2>] &ogdefs(system_call_fastpath);+0x16/0x75 [85110.121984] i8253_ref i8253_ref.0.auto: Resumed. counter=0x02f8 }} device driver の suspend が完了した後、&ogdefs(suspend_devices_and_enter()); は続けて &ogdefs(suspend_enter()); を呼び出します。&ogdefs(suspend_enter()); ではプラット・ホームの機能により分岐して suspend する関数を呼び分けています。&ogdefs(freeze_enter()); または &ogdefs(suspend_ops);->enter() です。&ogdefs(freeze_enter()); は CPU をなるべく動作しないようにするだけです。&ogdefs(suspend_ops);->enter() はプラットホームの機能を十分に使いシステム全体を低消費電力状態で停止する処理になります。wake up すると 停止した所から実行が継続します。プラットホーム毎に &ogdefs(suspend_ops); の内容は変わります。&ogrefs(platform_suspend_ops); 構造体を実装した場所を追跡してみて下さい。 ** state = { disk } [#fab0bc10] state ノードに "disk" を書き込むと &ogdefs(dev_pm_ops); メンバの freeze, thaw, poweroff, restore が呼び出されます。 *** freeze [#f68658ab] freeze は hibernation image を作成する前に呼び出されます。以降 thaw, poweroff, restore とも &ogdefs(hibernation_snapshot()); から呼ばれているように crall trace が得られます。 #pre(soft){{ [95474.290649] Call Trace: [95474.290657] [<ffffffff817d2279>] &ogdefs(dump_stack);+0x63/0x81 [95474.290661] [<ffffffffc033d7bd>] i8253_ref_freeze+0x4d/0x60 [i8253_ref] [95474.290665] [<ffffffff81474ade>] ? acpi_thermal_resume+0xfa/0xfa [95474.290668] [<ffffffff8150d8c9>] &ogdefs(platform_pm_freeze);+0x29/0x50 [95474.290671] [<ffffffff815187ae>] &ogdefs(dpm_run_callback);+0x4e/0x150 [95474.290673] [<ffffffff81519542>] &ogdefs(__device_suspend);+0x132/0x3b0 [95474.290675] [<ffffffff8151b27a>] &ogdefs(dpm_suspend);+0x13a/0x310 [95474.290678] [<ffffffff810c9e14>] &ogdefs(hibernation_snapshot);+0xb4/0x370 [95474.290679] [<ffffffff810ca8ce>] &ogdefs(hibernate);+0x16e/0x220 [95474.290681] [<ffffffff810c7dd4>] &ogdefs(state_store);+0xe4/0xf0 [95474.290684] [<ffffffff813b33df>] &ogdefs(kobj_attr_store);+0xf/0x20 [95474.290687] [<ffffffff81275bfd>] &ogdefs(sysfs_kf_write);+0x3d/0x50 [95474.290689] [<ffffffff812750aa>] &ogdefs(kernfs_fop_write);+0x12a/0x180 [95474.290692] [<ffffffff811fa198>] &ogdefs(__vfs_write);+0x28/0xf0 [95474.290694] [<ffffffff811fcda9>] ? __sb_start_write+0x49/0xf0 [95474.290697] [<ffffffff81320373>] ? security_file_permission+0x23/0xa0 [95474.290699] [<ffffffff811fa889>] &ogdefs(vfs_write);+0xa9/0x1b0 [95474.290700] [<ffffffff811fb656>] &ogdefs(SyS_write);+0x46/0xb0 [95474.290700] [<ffffffff811fb656>] SyS_write+0x46/0xb0 [95474.290703] [<ffffffff81218742>] ? __close_fd+0x82/0xa0 [95474.290706] [<ffffffff817da0b2>] &ogdefs(system_call_fastpath);+0x16/0x75 [95474.290725] i8253_ref i8253_ref.0.auto: Freezed. }} *** thaw [#uc740713] thaw は普通 hibernation image を swap 領域に書き込む前に呼び出されます。freeze の後何かのエラーが発生した場合も thaw が呼ばれます。恐らくエラーは領域不足で hibernation image を作れなかったか、swap 領域不足が見込まれる場合が殆どでしょう。 #pre(soft){{ [95474.837030] Call Trace: [95474.837038] [<ffffffff817d2279>] &ogdefs(dump_stack);+0x63/0x81 [95474.837042] [<ffffffffc033d92d>] i8253_ref_thaw+0x4d/0x60 [i8253_ref] [95474.837045] [<ffffffff8150d91b>] &ogdefs(platform_pm_thaw);+0x2b/0x50 [95474.837049] [<ffffffff815187ae>] &ogdefs(dpm_run_callback);+0x4e/0x150 [95474.837051] [<ffffffff81518d76>] &ogdefs(device_resume);+0xd6/0x200 [95474.837052] [<ffffffff8151a479>] &ogdefs(dpm_resume);+0x129/0x320 [95474.837056] [<ffffffff810c9f60>] &ogdefs(hibernation_snapshot);+0x200/0x370 [95474.837057] [<ffffffff810ca8ce>] &ogdefs(hibernate);+0x16e/0x220 [95474.837059] [<ffffffff810c7dd4>] &ogdefs(state_store);+0xe4/0xf0 [95474.837062] [<ffffffff813b33df>] &ogdefs(kobj_attr_store);+0xf/0x20 [95474.837064] [<ffffffff81275bfd>] &ogdefs(sysfs_kf_write);+0x3d/0x50 [95474.837067] [<ffffffff812750aa>] &ogdefs(kernfs_fop_write);+0x12a/0x180 [95474.837069] [<ffffffff811fa198>] &ogdefs(__vfs_write);+0x28/0xf0 [95474.837071] [<ffffffff811fcda9>] ? __sb_start_write+0x49/0xf0 [95474.837074] [<ffffffff81320373>] ? security_file_permission+0x23/0xa0 [95474.837076] [<ffffffff811fa889>] &ogdefs(vfs_write);+0xa9/0x1b0 [95474.837078] [<ffffffff811fb656>] &ogdefs(SyS_write);+0x46/0xb0 [95474.837078] [<ffffffff811fb656>] SyS_write+0x46/0xb0 [95474.837080] [<ffffffff81218742>] ? __close_fd+0x82/0xa0 [95474.837083] [<ffffffff817da0b2>] &ogdefs(system_call_fastpath);+0x16/0x75 [95474.837117] i8253_ref i8253_ref.0.auto: Thawed. counter=0x1620 }} *** poweroff [#p197c212] poweroff は hibernation image を swap 領域に書き込んだ後、電源 off をする前に呼ばれます。 #pre(soft){{ [95488.527795] Call Trace: [95488.527804] [<ffffffff817d2279>] &ogdefs(dump_stack);+0x63/0x81 [95488.527809] [<ffffffffc033d98d>] i8253_ref_poweroff+0x4d/0x60 [i8253_ref] [95488.527814] [<ffffffff81474ade>] ? acpi_thermal_resume+0xfa/0xfa [95488.527818] [<ffffffff8150d969>] &ogdefs(platform_pm_poweroff);+0x29/0x50 [95488.527823] [<ffffffff815187ae>] &ogdefs(dpm_run_callback);+0x4e/0x150 [95488.527825] [<ffffffff81519542>] &ogdefs(__device_suspend);+0x132/0x3b0 [95488.527827] [<ffffffff8151b27a>] &ogdefs(dpm_suspend);+0x13a/0x310 [95488.527829] [<ffffffff8151b967>] &ogdefs(dpm_suspend_start);+0x57/0x60 [95488.527833] [<ffffffff810ca68c>] &ogdefs(hibernation_platform_enter);+0x3c/0x110 [95488.527835] [<ffffffff817ce3d8>] &ogdefs(power_down);+0x1f/0xa1 [95488.527837] [<ffffffff810ca939>] &ogdefs(hibernate);+0x1d9/0x220 [95488.527839] [<ffffffff810c7dd4>] &ogdefs(state_store);+0xe4/0xf0 [95488.527843] [<ffffffff813b33df>] &ogdefs(kobj_attr_store);+0xf/0x20 [95488.527846] [<ffffffff81275bfd>] &ogdefs(sysfs_kf_write);+0x3d/0x50 [95488.527849] [<ffffffff812750aa>] &ogdefs(kernfs_fop_write);+0x12a/0x180 [95488.527853] [<ffffffff811fa198>] &ogdefs(__vfs_write);+0x28/0xf0 [95488.527855] [<ffffffff811fcda9>] ? __sb_start_write+0x49/0xf0 [95488.527859] [<ffffffff81320373>] ? security_file_permission+0x23/0xa0 [95488.527862] [<ffffffff811fa889>] &ogdefs(vfs_write);+0xa9/0x1b0 [95488.527864] [<ffffffff811fb656>] &ogdefs(SyS_write);+0x46/0xb0 [95488.527864] [<ffffffff811fb656>] SyS_write+0x46/0xb0 [95488.527867] [<ffffffff81218742>] ? __close_fd+0x82/0xa0 [95488.527870] [<ffffffff817da0b2>] &ogdefs(system_call_fastpath);+0x16/0x75 [95488.527907] i8253_ref i8253_ref.0.auto: Poweroffed. counter=0xbcde }} *** restore [#fbd23cc3] restore は kernel が起動した後、swap 領域から hibernation image を読み取り主記憶に展開した後呼ばれます。kernel 起動中の printk 時刻は 0.000000 から始まります。hibernation image の展開が終わった後、printk の時刻は poweroff 前の時刻と整合して続くように修正されます。 #pre(soft){{ [95499.554259] Call Trace: [95499.554259] [<ffffffff817d2279>] &ogdefs(dump_stack);+0x63/0x81 [95499.554259] [<ffffffffc033d9ed>] i8253_ref_restore+0x4d/0x53 [i8253_ref] [95499.554259] [<ffffffff8150d9bb>] &ogdefs(platform_pm_restore);+0x2b/0x50 [95499.554259] [<ffffffff815187ae>] &ogdefs(dpm_run_callback);+0x4e/0x150 [95499.554259] [<ffffffff81518d76>] &ogdefs(device_resume);+0xd6/0x200 [95499.554259] [<ffffffff8151a479>] &ogdefs(dpm_resume);+0x129/0x320 [95499.554259] [<ffffffff810c9ecb>] &ogdefs(hibernation_snapshot);+0x16b/0x370 [95499.554259] [<ffffffff810ca8ce>] &ogdefs(hibernate);+0x16e/0x220 [95499.554259] [<ffffffff810c7dd4>] &ogdefs(state_store);+0xe4/0xf0 [95499.554259] [<ffffffff813b33df>] &ogdefs(kobj_attr_store);+0xf/0x20 [95499.554259] [<ffffffff81275bfd>] &ogdefs(sysfs_kf_write);+0x3d/0x50 [95499.554259] [<ffffffff812750aa>] &ogdefs(kernfs_fop_write);+0x12a/0x180 [95499.554259] [<ffffffff811fa198>] &ogdefs(__vfs_write);+0x28/0xf0 [95499.554259] [<ffffffff811fcda9>] ? __sb_start_write+0x49/0xf0 [95499.554259] [<ffffffff81320373>] ? security_file_permission+0x23/0xa0 [95499.554259] [<ffffffff811fa889>] &ogdefs(vfs_write);+0xa9/0x1b0 [95499.554259] [<ffffffff811fb656>] &ogdefs(SyS_write);+0x46/0xb0 [95499.554259] [<ffffffff811fb656>] SyS_write+0x46/0xb0 [95499.554259] [<ffffffff81218742>] ? __close_fd+0x82/0xa0 [95499.554259] [<ffffffff817da0b2>] &ogdefs(system_call_fastpath);+0x16/0x75 [95549.131204] i8253_ref i8253_ref.0.auto: Restored. counter=0x10fd }} hibernate (state=disk)から復帰するとき kernel は reset 状態から起動します。上の stack dump を見ると &ogdefs(state_store()); から呼ばれたように見えます。一方で kernel 起動中に何かの process が /sys/power/state に書き込む様なことは起きていません。kernel 内部から /sys/power/state に書き込むこともしていません。&ogdefs(hibernation_snapshot()); から呼び出している &ogdefs(create_image()); 内で CPU レジスタを待避する &ogdefs(save_processor_state()); 関数、hibernate image を読み込んで実行再開するポイントになる &ogrefs(swsusp_arch_suspend(),swsusp_arch_suspend); 関数、CPU レジスタを復活する &ogdefs(restore_processor_state()); 関数の呼び出しとグローバル変数 &ogrefs(in_suspend,in_suspend,hibernate.c); (&ogdefs(__nosavedata); が付いていて、hibernation image に含まれないことに注意する) の巧妙な変化により、hibernate に入るために /sys/power/state に "disk" を書き込んだ処理の続きから再開されます。