Dell XPS 13 9305にArch Linux + KDE Plasma (Wayland) という環境で、ラップトップの蓋を閉じたときにhibernateする設定がうまく動かなかった話。systemctl hibernate は問題なく動くのに、蓋を閉じたときだけ失敗するという状況だった。
やりたいことは以下の通り。
| 状態 | リッド閉じたら |
|---|---|
| バッテリー | 即hibernate |
| AC電源 | suspend → 一定時間後にhibernate |
PowerDevil(Plasma標準の電源管理)でうまくいかない
KDE Plasmaでは電源管理をPowerDevilというデーモンが担当しており、systemd-logindのリッドスイッチ処理をblockモードのinhibitorで横取りしている。
$ systemd-inhibit --list
WHO PID WHAT WHY MODE
PowerDevil 1271 handle-lid-switch KDE handles power events block
Plasmaのシステム設定で「蓋を閉じたときの動作」を変更すると、~/.config/powerdevilrcに反映される。
[Battery][SuspendAndShutdown]
LidAction=2
SleepMode=3
設定自体は正しく保存されているが、蓋を閉じると失敗のループに入る。
$ journalctl -b -t systemd-logind
Lid closed.
The system will hibernate now!
Operation 'hibernate' finished. ← 数秒で終わる = 失敗
The system will hibernate now! ← リトライ
Operation 'hibernate' finished. ← また失敗
...(蓋を開けるまで繰り返し)
Lid opened.
正常なhibernateは1分ほどかかるのに対して、数秒で「完了」しているので明らかに失敗している。
原因: PowerDevilのwakeupsourcehelper
蓋を閉じた瞬間のjournalを見ると、PowerDevilがhibernate要求と並行してwakeupsourcehelperというヘルパープロセスを起動している。
16:51:43 Lid closed.
16:51:43 Started [email protected]
16:51:43 The system will hibernate now!
wakeupsourcehelperはsuspend中のスプリアスウェイクアップを防ぐために/sys/power/wakeup_countを読み書きするroot権限のヘルパーである。このヘルパーがhibernateのfreezeフェーズと競合する。
kernel: Freezing remaining freezable tasks aborted after 0.000 seconds
(1 tasks refusing to freeze, wq_busy=0):
systemd-sleep: Failed to put system to sleep. System resumed again:
Device or resource busy
カーネルスレッドの1つがfreezeを即座に拒否し、hibernateが失敗する。蓋が閉じたままなのでPowerDevilが再試行し、失敗のループに陥る。
手動のsystemctl hibernateが成功するのは、wakeupsourcehelperが起動されないためである。
wakeupsourcehelperは既にupstreamで無効化されている
PowerDevil 6.5.0以降、upstreamではセキュリティレビュー待ちとしてwakeupsourcehelperが無効化されている。しかしArch Linuxのパッケージ(確認時点で6.6.0)ではまだ有効なまま出荷されている。
logindに処理を委譲するアプローチが使えない理由
「PowerDevilのLidAction=0(何もしない)に設定し、systemd-logindのHandleLidSwitchで処理させる」という方法を試みたが、これは機能しない。
PowerDevilが登録するinhibitorはhandle-lid-switchというlow-levelタイプであり、logind.confのLidSwitchIgnoreInhibited=yesはhigh-levelのinhibitorしか無視しない。
Low level inhibitor locks ("handle-power-key", "handle-suspend-key", "handle-hibernate-key", "handle-lid-switch"), are always honored, irrespective of this setting.
さらにこのinhibitor文字列はPowerDevilのソースコードにハードコードされており、設定で無効化することはできない。Bug 385331でも設定可能にする要望がWONTFIXで閉じられている。
対処法
1. wakeupsourcehelperを無効化する
ヘルパーバイナリをリネームして無効化する。
sudo mv /usr/lib/kf6/kauth/wakeupsourcehelper /usr/lib/kf6/kauth/wakeupsourcehelper.disabled
upstreamで既に無効化されている機能なので、実害はない。
2. powerdevilrcでリッドアクションを設定する
~/.config/powerdevilrc:
[AC][SuspendAndShutdown]
LidAction=1
SleepMode=3
[Battery][SuspendAndShutdown]
LidAction=2
SleepMode=3
PowerDevilのSleepModeとLidActionの値は以下の通り。
LidAction:
| 値 | 動作 |
|---|---|
| 0 | 何もしない |
| 1 | Sleep(SleepModeに従う) |
| 2 | Hibernate |
SleepMode(LidAction=1のときに使用される):
| 値 | 動作 | logind D-Busメソッド |
|---|---|---|
| 1 | Suspend | Suspend() |
| 2 | Hybrid Sleep | HybridSleep() |
| 3 | Suspend then Hibernate | SuspendThenHibernate() |
つまり上記の設定では:
- バッテリー時:
LidAction=2→ PowerDevilがlogindのHibernate()を直接呼ぶ → 即hibernate - AC時:
LidAction=1+SleepMode=3→ PowerDevilがlogindのSuspendThenHibernate()を呼ぶ → まずsuspendし、一定時間後にsystemdがhibernateを実行
AC時のhibernateはsystemd自身が実行するため、PowerDevilのwakeupsourcehelperは介在しない。
設定の反映:
qdbus6 org.kde.org_kde_powerdevil /org/kde/Solid/PowerManagement reparseConfiguration
3. suspend-then-hibernateの遅延時間を設定する
/etc/systemd/sleep.conf.d/hibernate-delay.conf:
[Sleep]
HibernateDelaySec=20min
AC時にsuspendしてから20分後にhibernateへ移行する。suspend中にACを抜いてもタイマーは進行するので、「AC接続で蓋を閉じる→ACを抜く→しばらくしてhibernate」という動作になる。