眠れない、眠るとすぐに目覚める、復元後にデバイスが無効になる——これらは異なる段階における問題です。システムスリープとデバイスのruntime PMは異なる状態マシンであり、プラットフォームの能力、段階的な依存関係、およびウェイクアッププロトコル に基づいて特定する必要があります。
参照目標 参照箇所
サポートされるモードとデバイスの段階 最初の2節
ミラー復元とオンデマンドサスペンド hibernate、runtime PM
イベント競合と診断 ウェイクアップ、失敗段階
まず、現在のプラットフォームが何に対応しているか確認する
cat /sys/power/state
cat /sys/power/mem_sleep
state は要求可能な状態の一覧を示します。例えば freeze、mem、disk などです。mem_sleep は mem に関連するオプションモードの一覧を示し、角括弧で現在の選択項目が示されます。例えば s2idle [deep] は、mem が現在 deep に選択されていることを意味しますが、すべてのマシンが deep をサポートしているわけではありません。システムスリープ状態
方法 復元で依存する状態 主な違い
suspend-to-idle (s2idle、freeze) RAM内の既存状態 ソフトウェアによる協調凍結、デバイスサスペンド、CPUアイドル。従来のS3は不要
suspend-to-RAM (一般的なdeep) RAMに保持される状態 プラットフォームはより深いシステムスリープ状態に入る。ACPIプラットフォームでは通常S3に対応
hibernation (disk) 不揮発ストレージ内のメモリミラー 次回起動時にミラーを読み込み、古いカーネルの実行状態を復元
プラットフォームによっては shallow などの状態もオプションで存在します。消費電力と復元時間は、デバイス、ファームウェア、ミラーの規模などによって総合的に決定されるため、1W、100ms、または10秒といった値をインターフェースの保証として記述すべきではありません。また、s2idle が「浅いスリープしかできない」ことを意味するわけではありません。適切なプラットフォームでは、これを通じてより深い全体機器の低消費電力状態に達することも可能です。
サスペンドを段階に分ける理由
システムサスペンド:静止点を確立し、実行を再開する
準備と凍結
ユーザー空間、カーネルスレッド、ウェイクアップイベントの調整
1
デバイスサスペンドの各段階
依存関係に従い、suspend、late、noirq などのコールバックを処理
2
選択されたシステムスリープ状態への移行
s2idle と deep ではプラットフォームの移行方法が異なる
3
デバイスの復元と凍結解除
ウェイクアップ後に依存関係を復元し、失敗を処理してタスクを再開
4
これは完全なソースコードの呼び出しスタックを表すものではなく、デバイスの順序、プラットフォームの手順、および失敗時のフォールバックを考慮する必要があります。
システムは、新しい作業がデバイスに次々と流入するのを阻止しつつ、完了に必要な基盤を保持する必要があります。デバイスは通常、新しいI/Oを停止し、状態を保存してから、より遅い段階へ移行します。復元時には、まず依存関係を再構築し、その後上位層が作業を再発行できるようにします。
デバイスPMコールバックには、prepare/complete、suspend/resume、late/early、noirq などの段階があります。noirq は「ドライバがついにすべての割り込みを自由に無効にできる」という同義語ではなく、PMコアがデバイスの割り込み処理を調整済みの段階を意味します。非同期サスペンドや device links などは依存関係の実行に影響を与えるため、デバイス名の順序だけで推論することはできません。
ユーザータスクの凍結は、それらがすべてのユーザー空間ミューテックスを解放することを要求するものではなく、任意のビジネストランザクションを一貫性のあるスナップショットに変えるものでもありません。カーネルロックと凍結可能なスレッドについては、「凍結された側が復元またはサスペンドに必要なリソースを保持している」という待機ループを形成しないようにする必要があります。デバイスPM
hibernate:ミラーの保存と古い実行コンテキストの復元
hibernate では、復元可能なメモリミラーを作成し、不揮発ストレージに書き出す必要があります。これは「全RAM」を単純にバイト単位でコピーすることではありません。アイドルページ、保存不要なページ、および回収結果などはミラーの規模に影響を与え、ミラーは圧縮される可能性があります。また、すべてのページキャッシュが条件なくスキップされるわけでもありません。
ミラーを書き出す際、デバイスは凍結、凍結解除、そして最終的な電源状態への移行など、異なるパスを経験する可能性があります。そのため、ドライバの freeze/thaw、poweroff/restore コールバックには独立した意味があり、これらをすべて suspend/resume の別名として扱うことはできません。
復元は通常、新しく起動したカーネルまたは initramfs によってミラーが発見・ロードされ、保存されたカーネル状態に戻ります。デバイスresume、スワップファイルのオフセット、暗号化デバイスのロック解除、および起動段階のサポートは連携して行う必要があり、単に resume= を追加すれば必ず成功するわけではありません。また、ミラーが存在する状態で別の実行環境から関連ファイルシステムを書き換え、復元後の古いキャッシュが一貫性を保つことを期待することもできません。swsusp
ミラーにはメモリ内の機密状態が含まれるため、ストレージと復元のチェーンは、システムが既に持っている暗号化と信頼できる起動の設計に従うべきです。電源断後の全体機器の待機電力があるかどうかはハードウェアに依存し、hibernate という用語だけで絶対的なゼロワットが保証されるわけではありません。
runtime PM:使用カウント、依存関係、およびドライバ状態
runtime PM は、システム実行中に特定のデバイスのアクティビティとサスペンドを調整します。使用カウントは、まだ必要とされているデバイスがサスペンドされないようにするプロトコルの一つであり、サブデバイス、依存関係、ドライバの許可状態、および未完了のリクエストに関与します。autosuspend_delay_ms はその条件の一つに過ぎません。
ドライバがデバイスにアクセスする前には、pm_runtime_resume_and_get などの適切な同期取得および復元インターフェースを使用して取得し、失敗を確認する必要があります。使用を終了したら、プロトコルに従ってビジー時間をマーキングし、使用参照を解放します。復元失敗後にレジスタへのアクセスを続けることはできません。runtime PM
power/control の auto は runtime PM を許可し、on は通常 runtime サスペンドを禁止します。これは、システムスリープが許可されるかどうかとは異なります。PCI D3hot と D3cold にも、異なる電力供給および設定アクセスの条件があり、D3hot を「デバイスが完全に電源オフだがリンクは維持されている」と簡略化して記述することはできません。
使用参照がカバーする作業の範囲
同期デバイスアクセスの順序は、次の通りです:ランタイム参照の取得とデバイスの復元に成功し、ハードウェアにアクセスし、最後にこの参照を解放します。復元失敗時には利用可能なデバイスが得られないため、I/O を続けることもできず、失敗を成功した取得と見なして盲目的に解放することもできません。pm_runtime_resume_and_get() と pm_runtime_get_sync() のエラーパスにおけるカウントのセマンティクスは異なります。後者はエラー発生時に増加した使用カウントを自動的に取り消しません。runtime PM ヘルパーのセマンティクス
パス ハードウェアへのアクセス 現在の呼び出しで解放すべき使用参照
resume-and-get 失敗 なし 0
取得成功、I/O 成功 はい 1
取得成功、I/O 失敗 試行済み 1
取得成功、非同期I/Oが送信済みだが未完了 デバイスは依然として動作中 参照は、所有権プロトコルに従って完了/取消パスによって解放されるべき
以下のモデルは参照のペアリングのみをチェックし、復元失敗は戻り値で表し、転送失敗は例外で表します。PMカウントが0になっても直接サスペンドが実行されるわけではなく、したがって「put が返った時点でデバイスが電源オフになっている」と誤って示唆することもありません。
class Usage :
def __init__ ( self , resume_ok = True ) :
self . resume_ok = resume_ok
self . count = 0
self . accesses = 0
def resume_and_get ( self ) :
if not self . resume_ok :
return False
self . count += 1
return True
def put ( self ) :
assert self . count > 0
self . count -= 1
def io ( self , fail = False ) :
assert self . count > 0
self . accesses += 1
if fail :
raise OSError ( " transfer failed" )
return " ok"
def transfer ( pm , fail = False ) :
if not pm . resume_and_get ( ) :
return " resume failed"
try :
return pm . io ( fail )
finally :
pm . put ( )
pm = Usage ( False )
assert transfer ( pm ) == " resume failed" and ( pm . count , pm . accesses ) == ( 0 , 0 )
pm = Usage ( )
assert transfer ( pm ) == " ok" and ( pm . count , pm . accesses ) == ( 0 , 1 )
try :
transfer ( pm , fail = True )
except OSError :
pass
else :
raise AssertionError ( " 転送エラーを伝播すべき" )
assert ( pm . count , pm . accesses ) == ( 0 , 2 )
assert pm . resume_and_get ( ) and pm . resume_and_get ( )
pm . put ( )
assert pm . count == 1 pm . put ( )
assert pm . count == 0
Linux 7.2 の pm_runtime_put_autosuspend() は、自身で last_busy を更新してから参照を解放します。二重アンダースコア付きの __pm_runtime_put_autosuspend() は、この時間更新を担当しません。古いドライバにおける "mark_last_busy + put" の組み合わせを読む際には、ターゲットバージョンの include/linux/pm_runtime.h に照らして確認する必要があります。autosuspend は、遅延、アクティブなサブデバイス、PM QoS、ドライバの状態、および保留中のリクエストなどの条件にも制約されます。
転送にDMAを使用する場合、関数が「送信成功」を返した時点で、ハードウェアはメモリへのアクセスを続けている可能性があります。参照のライフサイクルは、完了または停止が確認された取消パスまで延長され、両方のパスで参照が一度だけ解放されることが保証されなければなりません。送信関数の finally だけで参照を解放すると、この同期モデルが非同期デバイスに誤って適用されてしまいます。
サブデバイスの使用参照を解放した後、親デバイスのサスペンドを試み、その後サブデバイスのサスペンドを進めます。使用カウントがゼロ、待機期間が経過、サブデバイスがアイドル、ドライバが許可している——これらはそれぞれ満たすべき条件です。
図を準備しています 再読み込み
カウントが0になれば、デバイスはすぐに眠るのか? put が0になっても、サスペンドを防ぐ条件の一つが取り除かれるだけであり、デバイスがすでにサスペンドされていることを保証するものではありません。サブデバイスの復元には、その親依存関係がまず利用可能である必要があります。アクティブなサブデバイスは親デバイスのサスペンドを阻止することもあります。
{"id":"runtime-pm-usage-and-state","title":"カウントが0になれば、デバイスはすぐに眠るのか?","summary":"put が0になっても、サスペンドを防ぐ条件の一つが取り除かれるだけであり、デバイスがすでにサスペンドされていることを保証するものではありません。サブデバイスの復元には、その親依存関係がまず利用可能である必要があります。アクティブなサブデバイスは親デバイスのサスペンドを阻止することもあります。","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><label class=\"check\"><input type=\"checkbox\" id=\"auto\" checked><span data-i18n=\"auto\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"busy\"><span data-i18n=\"busy\"></span></label><div class=\"actions\"><button id=\"getP\" data-i18n=\"getP\"></button><button id=\"putP\" data-i18n=\"putP\"></button></div><div class=\"actions\"><button id=\"getC\" data-i18n=\"getC\"></button><button id=\"putC\" data-i18n=\"putC\"></button></div><div class=\"track-row\"><span data-i18n=\"parent\"></span><div id=\"parent\" class=\"track\"></div></div><div class=\"dependency\">↑</div><div class=\"track-row\"><span data-i18n=\"child\"></span><div id=\"child\" class=\"track\"></div></div><div class=\"actions\"><button id=\"time\" data-i18n=\"time\"></button></div><div class=\"actions\"><button id=\"tryP\" data-i18n=\"tryP\"></button><button id=\"tryC\" data-i18n=\"tryC\"></button></div><div class=\"metrics\"><div><span data-i18n=\"clock\"></span><strong id=\"clock\"></strong></div></div><div class=\"card\"><header data-i18n=\"result\"></header><div class=\"copy\"><p id=\"explanation\" role=\"status\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"initial\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"refused\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"delayed\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"childActive\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"driverBusy\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"disabled\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"done\"></p><p class=\"reserve\" aria-hidden=\"true\" data-i18n=\"already\"></p></div></div><p class=\"caption\" data-i18n=\"note\"></p><div class=\"actions\"><button id=\"reset\" data-i18n=\"reset\"></button></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n.dependency{text-align:center;font-size:25px;color:var(--accent);height:28px}.metrics{grid-template-columns:1fr}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}let usage=[0,1],active=[true,true],last=[0,0],time=0,message='initial';function draw(){for(let i=0;i<2;i++){cells(i?'child':'parent',[{text:'usage '+usage[i],cls:usage[i]?'on':'empty'},{text:t(active[i]?'active':'suspended'),cls:active[i]?'gold':'empty'}]);$('#get'+'PC'[i]).disabled=usage[i]===3;$('#put'+'PC'[i]).disabled=usage[i]===0;}set('clock',time);set('explanation',t(message));}function attempt(i){if(!active[i])message='already';else if(!$('#auto').checked)message='disabled';else if(usage[i])message='refused';else if(i===0&&active[1])message='childActive';else if(time-last[i]<100)message='delayed';else if(i===1&&$('#busy').checked)message='driverBusy';else{active[i]=false;message='done';}draw();}for(let i=0;i<2;i++){$('#get'+'PC'[i]).onclick=()=>{usage[i]++;active[i]=true;if(i===1)active[0]=true;draw();};$('#put'+'PC'[i]).onclick=()=>{usage[i]--;if(usage[i]===0)last[i]=time;draw();};$('#try'+'PC'[i]).onclick=()=>attempt(i);}$('#time').onclick=()=>{time+=100;draw();};$('#reset').onclick=()=>{usage=[0,1];active=[true,true];last=[0,0];time=0;message='initial';$('#auto').checked=true;$('#busy').checked=false;draw();};draw();","audio":false,"strings":{"active":"アクティブ","already":"デバイスはすでにサスペンドされており、重複した変換はない。","auto":"runtime 自動サスペンドを許可","busy":"サブデバイスドライバが EBUSY を返す","child":"サブデバイス","childActive":"サブデバイスがまだアクティブであるため、親デバイスが先にサスペンドできない。","clock":"モデル時間 / ms","delayed":"自動サスペンドの遅延がまだ到達していない。","disabled":"ポリシーが runtime 自動サスペンドを許可していない。","done":"条件が満たされ、デバイスは runtime suspend に入る。","driverBusy":"ドライバが EBUSY を返し、デバイスはアクティブなまま。","getC":"サブデバイス get","getP":"親デバイス get","heading":"カウントが0になれば、デバイスはすぐに眠るのか?","initial":"まだ試行なし;サブデバイスに1つの使用参照がある。","intro":"親/サブデバイスの参照を取得または解放し、モデル時間を進めて、自動サスペンドの処理を試みます。カウント、遅延、アクティブなサブデバイス、およびドライバの拒否が、それぞれどのようにサスペンドを阻止するかを観察します。","note":"サブデバイスの参照を解放し、100 ms 待ってから、まず親デバイスを試行し、次にサブデバイスを試行します。結果が異なるのは、依存条件によるものであり、「カウントが0になれば電源オフになる」からではありません。","parent":"親デバイス","putC":"サブデバイス put","putP":"親デバイス put","refused":"使用カウントがまだ0ではないため、サスペンドできない。","reset":"リセット","result":"最新のサスペンド試行結果","scope":"1つの親デバイスと1つのサブデバイス。使用カウントはそれぞれ最大3、auto は両方に有効、遅延は固定 100 ms。get は成功した同期復元を折りたたみ、最後の put はビジー時間を更新します。自動サスペンドはボタントリガー時にチェックされます。ドライバの EBUSY はサブデバイスのみに使用されます。エラーリカバリ、デバイスリンク、および実際のワークキューは省略され、ホスト電源は制御されません。","scopeLabel":"モデルの範囲と規約","suspended":"サスペンド済み","time":"100 ms 経過","tryC":"サブデバイス autosuspend を試行","tryP":"親デバイス autosuspend を試行"}}
ウェイクアップ:能力、ポリシー、および実際のイベントを分離する
条件 確認すべきこと
デバイスの能力 ウェイクアップイベントを生成できるか
ポリシーの許可 power/wakeup などが許可しているか
実際の伝播 イベントが親デバイスおよびプラットフォームを通じてCPUに到達できるか
属性を有効化しても、ハードウェアの配線、ファームウェア、および全体の依存パスの条件を代替することはできません。
スリープ準備中に新しいイベントが発生し、サスペンドの中止を要求する場合があります。ユーザー空間とカーネルは、wakeup_count などのプロトコルを使用して、「イベントがないことを確認してから、実際にスリープする前に新しいイベントが来る」という競合を処理できます。pm_wakeup_irq は限定的なIRQの手がかりを提供しますが、すべてのウェイクアップソースの完全な台帳ではありません。
wakeup_count は「確認してから眠る」間の窓をどのように閉じるか
1回の協調試行は、次のように理解できます:まず、アクティブなウェイクアップイベントがない状態でのカウント n を読み取り、ユーザー空間の準備を完了し、その後 n を書き戻します。カウントが同じままで、処理中のイベントがない場合に限り、書き戻しが成功します。書き戻しが成功すると、その後のチェックが有効になり、新しいイベントによって今回のサスペンドを中止させることができます。読み取りは、処理中のイベントの終了を待つ場合もあり、常に即座に返る単純な統計読み取りではありません。
新しいイベントの発生位置 元のスリープ判断を沿用できない理由
読み取り後、書き戻し前に処理が終了 カウントが n から n+1 に変化し、n への書き戻しが失敗
書き戻し時に処理中 完了カウントがまだ増加していなくても、アクティブイベントにより書き戻しが失敗
書き戻し成功後、スリープチェック前 チェックで新しいカウントまたはアクティブイベントが発見され、今回の変換が中止
1回のスリープ試行には前後2回のイベントチェックが必要
カウント n を読み取る
アクティブイベントの終了を待つ
n を書き戻す
カウントとアクティブ状態を比較
スリープ中のチェック
変化があれば今回の変換を中止
イベントウィンドウ ①
イベントウィンドウ ②
① は書き戻し失敗を引き起こす;② は後のスリープチェックで pending を発見させる
これは協調プロトコルの模式図であり、実際のスリープエントリおよびスリープ進入後のイベントは、プラットフォームのウェイクアップメカニズムに依存します。
以下は、1回の試行中のアトミックチェックポイントをシミュレートしたものです。finished は終了したモデルのウェイクアップ活動の数を表し、active は処理中の数を表します。これらは元の割り込みカウントではありません。実際のカーネルには、ロック、カウントのパッキング、ブロッキング待機、および追加の中止ソースが含まれます。ここでの pending は状態のみを比較し、カーネルがイベントを検出した後にチェックの有効化をクリアする副作用を再現するものではありません。
class WakeupGate :
def __init__ ( self ) :
self . finished = 0
self . active = 0
self . saved = None
def begin ( self ) :
self . active += 1
def end ( self ) :
assert self . active > 0
self . active -= 1
self . finished += 1
def read ( self ) :
if self . active :
raise BlockingIOError ( " 実際のブロッキング読み取りでは、アクティブ終了を待つ必要がある" )
return self . finished
def save ( self , observed ) :
self . saved = None
if self . active or observed != self . finished :
return False
self . saved = observed
return True
def pending ( self ) :
assert self . saved is not None
return self . active > 0 or self . finished != self . saved
gate = WakeupGate ( )
n = gate . read ( )
gate . begin ( ) ; gate . end ( )
assert not gate . save ( n )
gate = WakeupGate ( )
n = gate . read ( )
gate . begin ( )
assert gate . finished == n and not gate . save ( n )
try :
gate . read ( )
except BlockingIOError :
pass
else :
raise AssertionError ( " アクティブイベント中は、このモデルの安定した読み取りを提供できない" )
gate = WakeupGate ( )
assert gate . save ( gate . read ( ) ) and not gate . pending ( )
gate . begin ( )
assert gate . pending ( )
gate . end ( )
assert gate . pending ( )
assert gate . save ( gate . read ( ) ) and not gate . pending ( )
チェックが通過したのは、このチェックポイントが条件を満たしていることを示すだけで、その後に割り込みが発生しないことを意味しません。その後のPMチェックおよびプラットフォームのウェイクアップメカニズムは、実際のスリープエントリに近づいた時点、さらにはスリープ進入後のイベントを処理する必要があります。能力、ポリシー、および伝播パスは依然として成立している必要があります。したがって、モデル内の not pending() を「無条件でウェイクアップを無効化してスリープする」と記述してはいけません。インターフェース契約は、カーネル Documentation/ABI/testing/sysfs-power の wakeup_count エントリで確認でき、実装は drivers/base/power/wakeup.c にあります。
失敗の段階に応じて特定する
実際の mem_sleep を記録してから、段階を特定します:
現象 調査の起点
進入できない 凍結、デバイスコールバック、および依存関係が完了したか
進入直後に目覚める 実際のウェイクアップイベントと準備中の競合
目覚められない 能力、ポリシー、およびプラットフォームの伝播パス
復元後に単一デバイスが無効 対応する復元コールバックおよびリソース再構築
最後のログエントリで言及されたデバイスが、必ずしも根本原因であるとは限りません。
journalctl - k - b
cat /sys/power/pm_wakeup_irq
属性の存在とログの権限は環境に依存します。power:suspend_resume などの tracepoint を組み合わせて段階的な所要時間を観察できます。トレースを有効化する際には、trace_pipe を任意の現在のディレクトリ内のファイルとして扱うのではなく、マシンに実際にマウントされている tracefs のパスを使用してください。
CONFIG_PM_DEBUG をサポートするシステムでは、pm_test を使用して freezer、devices などの段階を階層型でテストし、プラットフォームが実際にスリープ状態に入る前の故障範囲を絞り込むことができます。テストは実際にタスクを凍結したりデバイスをサスペンドしたりするため、復元手段のあるテスト環境で実行する必要があります。このページでリストされている読み取りコマンド自体はスリープをトリガーしません。デバッグドキュメント
pm_trace は RTC を使用してトレースフィンガープリントを保存しますが、クロックに影響を与える副作用があるため、説明なしで最初のデバッグコマンドとして使用するのは適切ではありません。ドライバのエラーは、コールバックの戻り値、依存関係、および繰り返しテストと組み合わせて確認する必要があり、ハッシュの一致のみを根因と見なしてはいけません。