diff options
| author | 2026-05-29 11:18:15 +0100 | |
|---|---|---|
| committer | 2026-05-29 11:18:15 +0100 | |
| commit | 6e0c5c33438e5e898bd075c33a45b3abf9d1b26b (patch) | |
| tree | c7387db08eaf33f55eb7f1e3cca331f92fbce9be | |
| parent | ad8e14860fa0ca978f5ef6e02860d24f5e39c361 (diff) | |
| download | dotfiles-6e0c5c33438e5e898bd075c33a45b3abf9d1b26b.tar.gz dotfiles-6e0c5c33438e5e898bd075c33a45b3abf9d1b26b.tar.bz2 dotfiles-6e0c5c33438e5e898bd075c33a45b3abf9d1b26b.zip | |
feat(suspend): re-enable suspend on s2idle, drop diagnostic scaffolding
Confirmed root cause: this hardware's S3 (deep) firmware path triggers a
fatal wake-from-suspend hang only on linux-hardened. INIT_ON_FREE + slab
hardening + tighter locking turn a latent driver race that stock linux
gets away with into an unrecoverable panic so early the journal isn't
even flushed. mem_sleep_default=s2idle bypasses the BIOS S3 path
entirely (s0ix is a pure-kernel low-power state) and suspends/resumes
reliably under hardened.
This is a widespread Lenovo S3 firmware issue across post-2018
ThinkPads (see Ubuntu T560, X1C9/10/11 reports). Lenovo themselves
moved newer firmwares to s2idle-only. Not a linux-hardened bug per se;
just hardened being a strict enough kernel to make the bug fatal.
Keep:
* mem_sleep_default=s2idle in etc/kernel/cmdline-linux-hardened.tmpl
(only the hardened UKI; stock linux keeps unchanged shared cmdline)
Revert (all the diagnostic / speculative scaffolding from the last
few commits):
* MODULES=(intel_lpss_pci) → MODULES=() — Arch wiki touchpad fix was
not the cause here
* nmi_watchdog=panic softlockup_panic=1 panic=10 — only needed to
auto-reboot during diagnosis
* no_console_suspend — diagnostic-only
* etc/systemd/logind.conf.d/20-no-suspend.conf — masking workaround
* sleep-target masking block in run_onchange_after_deploy-etc.sh.tmpl,
replaced with a one-shot cleanup that removes any leftover
/dev/null symlinks from systems that ran the previous version
* systemd-pstore.service from systemd-units/system.txt — added only to
catch the diagnostic panic
* diagnose-suspend.sh helper (and its .gitignore/.chezmoiignore entries)
* sway suspend → lock-session keybind workaround
* power-menu.sh Suspend entry restoration
* KEYBINDS.md docs
| -rw-r--r-- | .chezmoiignore | 1 | ||||
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | KEYBINDS.md | 26 | ||||
| -rw-r--r-- | dot_config/sway/config | 6 | ||||
| -rw-r--r-- | dot_config/sway/executable_power-menu.sh | 7 | ||||
| -rw-r--r-- | etc/kernel/cmdline-linux-hardened.tmpl | 2 | ||||
| -rw-r--r-- | etc/mkinitcpio.conf | 2 | ||||
| -rw-r--r-- | etc/mkinitcpio.d/linux-hardened.preset | 10 | ||||
| -rw-r--r-- | etc/systemd/logind.conf.d/20-no-suspend.conf | 17 | ||||
| -rwxr-xr-x | run_onchange_after_deploy-etc.sh.tmpl | 14 | ||||
| -rw-r--r-- | systemd-units/system.txt | 1 |
11 files changed, 36 insertions, 51 deletions
diff --git a/.chezmoiignore b/.chezmoiignore index ae7912d..99af635 100644 --- a/.chezmoiignore +++ b/.chezmoiignore @@ -2,7 +2,6 @@ KEYBINDS.md README.md bootstrap.sh migrate-podman-to-btrfs.sh -diagnose-suspend.sh meta/ systemd-units/ etc/ @@ -4,4 +4,3 @@ node_modules/ *.swp /migrate-podman-to-btrfs.sh -/diagnose-suspend.sh diff --git a/KEYBINDS.md b/KEYBINDS.md index 9040c2a..898c78c 100644 --- a/KEYBINDS.md +++ b/KEYBINDS.md @@ -326,7 +326,7 @@ Mod key: `Super` (Mod4). Only personal additions beyond sway defaults listed. | `XF86AudioMicMute` | Mic mute toggle | | `XF86Bluetooth` | Bluetooth power toggle (bluetoothctl) | | `XF86ScreenSaver` | Lock screen + pause media (same as Super+Shift+s) | -| `XF86Sleep` | Lock session (suspend disabled — see logind drop-in) | +| `XF86Sleep` | Suspend system (systemctl suspend) | | `XF86WLAN` | Toggle Wi-Fi (rfkill) | | `XF86RFKill` | Toggle all radios (rfkill) | | `Super+Shift+Return` | Open ghostty with yazi (file manager) | @@ -364,18 +364,18 @@ runs the action and exits the mode; `Escape` or `Return` exits without acting. Provides non-XF86 access to the rare hardware toggles and TUI launchers. -| In `system` mode | XF86 equivalent | Action | -| ----------------- | --------------- | ------------------------------- | -| `b` | `XF86Bluetooth` | Bluetooth power toggle | -| `w` | `XF86WLAN` | Wi-Fi toggle (rfkill wifi) | -| `r` | `XF86RFKill` | Toggle all radios (rfkill) | -| `s` | `XF86Sleep` | Lock session (suspend disabled) | -| `d` | `XF86Display` | Display mode toggle | -| `v` | `XF86Tools` | Floating pulsemixer | -| `k` | `XF86Keyboard` | Floating glow KEYBINDS.md | -| `m` | `XF86Favorites` | Notification picker | -| `n` | — | Toggle Do-Not-Disturb (mako) | -| `Escape`/`Return` | — | exit submode | +| In `system` mode | XF86 equivalent | Action | +| ----------------- | --------------- | ---------------------------- | +| `b` | `XF86Bluetooth` | Bluetooth power toggle | +| `w` | `XF86WLAN` | Wi-Fi toggle (rfkill wifi) | +| `r` | `XF86RFKill` | Toggle all radios (rfkill) | +| `s` | `XF86Sleep` | Suspend (systemctl suspend) | +| `d` | `XF86Display` | Display mode toggle | +| `v` | `XF86Tools` | Floating pulsemixer | +| `k` | `XF86Keyboard` | Floating glow KEYBINDS.md | +| `m` | `XF86Favorites` | Notification picker | +| `n` | — | Toggle Do-Not-Disturb (mako) | +| `Escape`/`Return` | — | exit submode | ## Typing / Input diff --git a/dot_config/sway/config b/dot_config/sway/config index 817e7c4..48e5993 100644 --- a/dot_config/sway/config +++ b/dot_config/sway/config @@ -173,7 +173,7 @@ bindsym $mod+Ctrl+bracketleft exec ~/.config/sway/brightness-osd.sh down bindsym XF86AudioMicMute exec pactl set-source-mute @DEFAULT_SOURCE@ toggle bindsym XF86Bluetooth exec ~/.config/sway/bt-toggle.sh bindsym XF86ScreenSaver exec "playerctl -a pause; swaylock -f -e -c 000000" -bindsym XF86Sleep exec loginctl lock-session +bindsym XF86Sleep exec systemctl suspend bindsym XF86WLAN exec ~/.config/waybar/wifi-toggle.sh bindsym XF86RFKill exec rfkill toggle all @@ -225,13 +225,13 @@ bindsym $mod+z mode "qr" # System / hardware submode — non-XF86 alternatives for the rarely used # hardware toggles and TUI launchers above. Enter with Super+x; mnemonic -# letters (b=bluetooth, w=wifi, r=rfkill, s=lock-session, d=display, +# letters (b=bluetooth, w=wifi, r=rfkill, s=sleep, d=display, # v=volume-mixer, k=keybinds, m=notification-picker). mode "system" { bindsym b exec ~/.config/sway/bt-toggle.sh, mode "default" bindsym w exec ~/.config/waybar/wifi-toggle.sh, mode "default" bindsym r exec rfkill toggle all, mode "default" - bindsym s exec loginctl lock-session, mode "default" + bindsym s exec systemctl suspend, mode "default" bindsym d exec ~/.config/sway/display-toggle.sh, mode "default" bindsym v exec $term --class=floating -e pulsemixer, mode "default" bindsym k exec $term --class=floating -e glow -p ~/dotfiles/KEYBINDS.md, mode "default" diff --git a/dot_config/sway/executable_power-menu.sh b/dot_config/sway/executable_power-menu.sh index 0a7de91..999259c 100644 --- a/dot_config/sway/executable_power-menu.sh +++ b/dot_config/sway/executable_power-menu.sh @@ -2,10 +2,9 @@ # Keyboard-driven power menu via wofi --dmenu (j/k navigation). set -eu -# Suspend entry intentionally omitted while suspend is disabled -# system-wide. See etc/systemd/logind.conf.d/20-no-suspend.conf. choice=$(printf '%s\n' \ " Lock" \ + " Suspend" \ " Logout" \ " Reboot" \ " Poweroff" | @@ -18,6 +17,10 @@ case "$choice" in playerctl -a pause exec swaylock -f -e -c 000000 ;; + *Suspend) + playerctl -a pause + exec systemctl suspend + ;; *Logout) exec swaymsg exit ;; *Reboot) exec systemctl reboot ;; *Poweroff) exec systemctl poweroff ;; diff --git a/etc/kernel/cmdline-linux-hardened.tmpl b/etc/kernel/cmdline-linux-hardened.tmpl index 45dff5e..be99a9c 100644 --- a/etc/kernel/cmdline-linux-hardened.tmpl +++ b/etc/kernel/cmdline-linux-hardened.tmpl @@ -1 +1 @@ -rd.luks.name={{ output "lsblk" "-dno" "UUID" (printf "/dev/%s" .luksRootPartition) | trim }}=root root=/dev/mapper/root rw mem_sleep_default=s2idle no_console_suspend nmi_watchdog=panic softlockup_panic=1 panic=10 +rd.luks.name={{ output "lsblk" "-dno" "UUID" (printf "/dev/%s" .luksRootPartition) | trim }}=root root=/dev/mapper/root rw quiet mem_sleep_default=s2idle diff --git a/etc/mkinitcpio.conf b/etc/mkinitcpio.conf index d8ae3ff..fab5fa1 100644 --- a/etc/mkinitcpio.conf +++ b/etc/mkinitcpio.conf @@ -4,7 +4,7 @@ # run. Advanced users may wish to specify all system modules # in this array. For instance: # MODULES=(usbhid xhci_hcd) -MODULES=(intel_lpss_pci) +MODULES=() # BINARIES # This setting includes any additional binaries a given user may diff --git a/etc/mkinitcpio.d/linux-hardened.preset b/etc/mkinitcpio.d/linux-hardened.preset index 90be917..fcb94a5 100644 --- a/etc/mkinitcpio.d/linux-hardened.preset +++ b/etc/mkinitcpio.d/linux-hardened.preset @@ -1,9 +1,11 @@ # mkinitcpio preset for the 'linux-hardened' kernel. Produces a UKI at # /boot/EFI/Linux/arch-linux-hardened.efi alongside the stock linux UKI. -# Uses its own cmdline file (etc/kernel/cmdline-linux-hardened.tmpl) -# carrying hang-detection knobs while we validate the suspend/resume -# fix on this hardware. The stock linux UKI keeps the minimal -# etc/kernel/cmdline.tmpl. Register the EFI entry once with efibootmgr: +# Uses its own cmdline file (etc/kernel/cmdline-linux-hardened.tmpl) to +# force `mem_sleep_default=s2idle`: this hardware's S3 firmware path +# wakes reliably on stock linux but hard-hangs the hardened kernel +# (INIT_ON_FREE + slab hardening turn a latent driver race fatal). +# Stock linux keeps the minimal etc/kernel/cmdline.tmpl. Register the +# EFI entry once with efibootmgr: # # sudo efibootmgr --create --disk /dev/nvme0n1 --part 1 \ # --label 'Arch Hardened' --loader '\EFI\Linux\arch-linux-hardened.efi' diff --git a/etc/systemd/logind.conf.d/20-no-suspend.conf b/etc/systemd/logind.conf.d/20-no-suspend.conf deleted file mode 100644 index 1b58aa4..0000000 --- a/etc/systemd/logind.conf.d/20-no-suspend.conf +++ /dev/null @@ -1,17 +0,0 @@ -[Login] -# Suspend is disabled while the linux-hardened wake-from-S3 hang is -# unresolved (NVMe / i915 / iwlwifi driver UAF surfaced by INIT_ON_FREE -# + slab hardening). Lid close, suspend/hibernate keys, and idle action -# all fall back to session lock instead of suspend. The sleep/suspend/ -# hibernate targets are also masked at the unit level via the etc/ -# deploy script as belt-and-braces against `systemctl suspend` from -# anywhere. Screen-off (DPMS) and swaylock continue to be driven by -# swayidle and are unaffected. -HandleLidSwitch=lock -HandleLidSwitchExternalPower=lock -HandleLidSwitchDocked=ignore -HandleSuspendKey=lock -HandleSuspendKeyLongPress=ignore -HandleHibernateKey=lock -HandleHibernateKeyLongPress=ignore -IdleAction=ignore diff --git a/run_onchange_after_deploy-etc.sh.tmpl b/run_onchange_after_deploy-etc.sh.tmpl index a60d833..5b60972 100755 --- a/run_onchange_after_deploy-etc.sh.tmpl +++ b/run_onchange_after_deploy-etc.sh.tmpl @@ -30,15 +30,15 @@ done # sudo-rs: /etc/pam.d/sudo-i is a symlink to /etc/pam.d/sudo sudo ln -sfT sudo /etc/pam.d/sudo-i -# Mask sleep/suspend/hibernate at the unit level. See -# etc/systemd/logind.conf.d/20-no-suspend.conf for context. This blocks -# `systemctl suspend` from any source (manual, scripts, GUI menus) in -# addition to the logind key/lid handler overrides. To re-enable: -# remove these symlinks (and revert the logind drop-in) then -# `sudo systemctl daemon-reload`. +# Clean up sleep-target masks from the earlier hardened-suspend +# workaround. Now that mem_sleep_default=s2idle resolves the wake hang, +# suspend is enabled again. Remove any leftover /dev/null symlinks. for target in sleep.target suspend.target hibernate.target \ hybrid-sleep.target suspend-then-hibernate.target; do - sudo ln -sfT /dev/null "/etc/systemd/system/$target" + link="/etc/systemd/system/$target" + if [ -L "$link" ] && [ "$(readlink "$link")" = "/dev/null" ]; then + sudo rm -f "$link" + fi done sudo systemctl daemon-reload diff --git a/systemd-units/system.txt b/systemd-units/system.txt index db74386..582a508 100644 --- a/systemd-units/system.txt +++ b/systemd-units/system.txt @@ -5,7 +5,6 @@ systemd-timesyncd.service systemd-resolved.service systemd-oomd.service -systemd-pstore.service reflector.timer paccache.timer acpid.service |
