aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-05-29 11:18:15 +0100
committerLibravatar sommerfeld <sommerfeld@sommerfeld.dev>2026-05-29 11:18:15 +0100
commit6e0c5c33438e5e898bd075c33a45b3abf9d1b26b (patch)
treec7387db08eaf33f55eb7f1e3cca331f92fbce9be
parentad8e14860fa0ca978f5ef6e02860d24f5e39c361 (diff)
downloaddotfiles-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--.chezmoiignore1
-rw-r--r--.gitignore1
-rw-r--r--KEYBINDS.md26
-rw-r--r--dot_config/sway/config6
-rw-r--r--dot_config/sway/executable_power-menu.sh7
-rw-r--r--etc/kernel/cmdline-linux-hardened.tmpl2
-rw-r--r--etc/mkinitcpio.conf2
-rw-r--r--etc/mkinitcpio.d/linux-hardened.preset10
-rw-r--r--etc/systemd/logind.conf.d/20-no-suspend.conf17
-rwxr-xr-xrun_onchange_after_deploy-etc.sh.tmpl14
-rw-r--r--systemd-units/system.txt1
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/
diff --git a/.gitignore b/.gitignore
index e8c1d56..9a33068 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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