From 150f0eca1bf39eb41dc358b33395ded7e6025f1d Mon Sep 17 00:00:00 2001 From: Mathias Eek <51080320+Cliffback@users.noreply.github.com> Date: Mon, 11 May 2026 15:05:04 +0200 Subject: [PATCH 1/3] Fix hibernation on btrfs swapfiles with dm-crypt (systemd 256+) systemd 256+ cannot resolve btrfs swapfiles on dm-crypt to their backing block device, causing CanHibernate to return 'na' and systemd-sleep to fail with 'Stale file handle'. Work around this with SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK on both systemd-logind and systemd-hibernate services. Also detect and fix stale resume_offset when the swapfile has been recreated at a different physical location on the btrfs filesystem. Ref: https://github.com/systemd/systemd/issues/30083 --- bin/omarchy-hibernation-remove | 2 ++ bin/omarchy-hibernation-setup | 28 +++++++++++++++++++++++++--- migrations/1778497305.sh | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 migrations/1778497305.sh diff --git a/bin/omarchy-hibernation-remove b/bin/omarchy-hibernation-remove index 49bad5bd6e..60b4461840 100755 --- a/bin/omarchy-hibernation-remove +++ b/bin/omarchy-hibernation-remove @@ -48,6 +48,8 @@ fi echo "Removing suspend-then-hibernate configuration" sudo rm -f /etc/systemd/logind.conf.d/lid.conf sudo rm -f /etc/systemd/sleep.conf.d/hibernate.conf +sudo rm -f /etc/systemd/system/systemd-logind.service.d/hibernate.conf +sudo rm -f /etc/systemd/system/systemd-hibernate.service.d/bypass.conf # Remove mkinitcpio resume hook echo "Removing resume hook" diff --git a/bin/omarchy-hibernation-setup b/bin/omarchy-hibernation-setup index d8e32c94e2..eac140c30b 100755 --- a/bin/omarchy-hibernation-setup +++ b/bin/omarchy-hibernation-setup @@ -90,13 +90,15 @@ echo "HOOKS+=(resume)" | sudo tee "$MKINITCPIO_CONF" >/dev/null # Ensure keyboard backlight doesn't prevent sleep sudo cp -p "$OMARCHY_PATH/default/systemd/system-sleep/keyboard-backlight" /usr/lib/systemd/system-sleep/ +# Resolve resume device and offset +sudo swapon -p 0 "$SWAP_FILE" 2>/dev/null +RESUME_DEVICE=$(findmnt -no SOURCE -T "$SWAP_FILE" | sed 's/\[.*\]//') +RESUME_OFFSET=$(sudo btrfs inspect-internal map-swapfile -r "$SWAP_FILE") + # Add resume= kernel parameters so the initramfs resume hook knows where to find the # hibernation image. Without these, resume happens late (after GPU drivers load) and fails. if [[ ! -f $RESUME_DROP_IN ]]; then echo "Adding resume kernel parameters" - sudo swapon -p 0 "$SWAP_FILE" 2>/dev/null - RESUME_DEVICE=$(findmnt -no SOURCE -T "$SWAP_FILE" | sed 's/\[.*\]//') - RESUME_OFFSET=$(sudo btrfs inspect-internal map-swapfile -r "$SWAP_FILE") if [[ -n $RESUME_OFFSET ]]; then sudo mkdir -p /etc/limine-entry-tool.d echo "KERNEL_CMDLINE[default]+=\" resume=$RESUME_DEVICE resume_offset=$RESUME_OFFSET\"" | sudo tee "$RESUME_DROP_IN" >/dev/null @@ -104,6 +106,26 @@ if [[ ! -f $RESUME_DROP_IN ]]; then else echo "Warning: Could not determine resume offset for $SWAP_FILE" >&2 fi +elif [[ -n $RESUME_OFFSET ]] && ! grep -q "resume_offset=$RESUME_OFFSET" "$RESUME_DROP_IN"; then + echo "Updating stale resume_offset in $RESUME_DROP_IN" + sudo sed -i "s/resume_offset=[0-9]*/resume_offset=$RESUME_OFFSET/" "$RESUME_DROP_IN" + sudo sed -i "s/resume_offset=[0-9]*/resume_offset=$RESUME_OFFSET/" /etc/default/limine +fi + +# Work around systemd not being able to resolve btrfs swapfiles on dm-crypt +# as valid hibernation devices (see https://github.com/systemd/systemd/issues/30083) +LOGIND_DROP_IN="/etc/systemd/system/systemd-logind.service.d/hibernate.conf" +if [[ ! -f $LOGIND_DROP_IN ]]; then + echo "Adding systemd-logind hibernation bypass" + sudo mkdir -p /etc/systemd/system/systemd-logind.service.d + printf '[Service]\nEnvironment="SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1"\n' | sudo tee "$LOGIND_DROP_IN" >/dev/null +fi + +HIBERNATE_DROP_IN="/etc/systemd/system/systemd-hibernate.service.d/bypass.conf" +if [[ ! -f $HIBERNATE_DROP_IN ]]; then + echo "Adding systemd-hibernate bypass" + sudo mkdir -p /etc/systemd/system/systemd-hibernate.service.d + printf '[Service]\nEnvironment="SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1"\n' | sudo tee "$HIBERNATE_DROP_IN" >/dev/null fi # Use ACPI alarm for RTC wakeup on s2idle systems (needed for suspend-then-hibernate) diff --git a/migrations/1778497305.sh b/migrations/1778497305.sh new file mode 100644 index 0000000000..05dd6789b8 --- /dev/null +++ b/migrations/1778497305.sh @@ -0,0 +1,34 @@ +echo "Fix hibernation on btrfs swapfiles with dm-crypt (systemd 256+)" + +MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/omarchy_resume.conf" +SWAP_FILE="/swap/swapfile" +RESUME_DROP_IN="/etc/limine-entry-tool.d/resume.conf" + +# Only apply if hibernation is configured +if [[ ! -f $MKINITCPIO_CONF ]] || ! grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then + exit 0 +fi + +# Add systemd bypass drop-ins for btrfs swapfile on dm-crypt +# (see https://github.com/systemd/systemd/issues/30083) +LOGIND_DROP_IN="/etc/systemd/system/systemd-logind.service.d/hibernate.conf" +if [[ ! -f $LOGIND_DROP_IN ]]; then + sudo mkdir -p /etc/systemd/system/systemd-logind.service.d + printf '[Service]\nEnvironment="SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1"\n' | sudo tee "$LOGIND_DROP_IN" >/dev/null +fi + +HIBERNATE_DROP_IN="/etc/systemd/system/systemd-hibernate.service.d/bypass.conf" +if [[ ! -f $HIBERNATE_DROP_IN ]]; then + sudo mkdir -p /etc/systemd/system/systemd-hibernate.service.d + printf '[Service]\nEnvironment="SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1"\n' | sudo tee "$HIBERNATE_DROP_IN" >/dev/null +fi + +# Fix stale resume_offset if swapfile was recreated at a different physical location +if [[ -f $RESUME_DROP_IN ]] && [[ -f $SWAP_FILE ]]; then + RESUME_OFFSET=$(sudo btrfs inspect-internal map-swapfile -r "$SWAP_FILE" 2>/dev/null) + if [[ -n $RESUME_OFFSET ]] && ! grep -q "resume_offset=$RESUME_OFFSET" "$RESUME_DROP_IN"; then + sudo sed -i "s/resume_offset=[0-9]*/resume_offset=$RESUME_OFFSET/" "$RESUME_DROP_IN" + sudo sed -i "s/resume_offset=[0-9]*/resume_offset=$RESUME_OFFSET/" /etc/default/limine + sudo limine-mkinitcpio + fi +fi From 160ebbccdd647f85cb7db1fce930d57898be64ab Mon Sep 17 00:00:00 2001 From: Mathias Eek <51080320+Cliffback@users.noreply.github.com> Date: Mon, 11 May 2026 21:28:57 +0200 Subject: [PATCH 2/3] Set reboot-required flag in hibernation migration --- migrations/1778497305.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migrations/1778497305.sh b/migrations/1778497305.sh index 05dd6789b8..d8fc91fa7e 100644 --- a/migrations/1778497305.sh +++ b/migrations/1778497305.sh @@ -32,3 +32,5 @@ if [[ -f $RESUME_DROP_IN ]] && [[ -f $SWAP_FILE ]]; then sudo limine-mkinitcpio fi fi + +omarchy-state set reboot-required From 4cf0f4a608d46c2121feb7d355ec5706223f9dbf Mon Sep 17 00:00:00 2001 From: Mathias Eek <51080320+Cliffback@users.noreply.github.com> Date: Mon, 11 May 2026 21:51:04 +0200 Subject: [PATCH 3/3] Place resume hook before filesystems so hibernate image restores correctly --- bin/omarchy-hibernation-available | 2 +- bin/omarchy-hibernation-remove | 9 +++++---- bin/omarchy-hibernation-setup | 16 ++++++++++------ migrations/1778497305.sh | 12 ++++++++++-- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/bin/omarchy-hibernation-available b/bin/omarchy-hibernation-available index 701a8f7202..e66d10a4ec 100755 --- a/bin/omarchy-hibernation-available +++ b/bin/omarchy-hibernation-available @@ -12,7 +12,7 @@ SWAPSIZE=$(( 1024 * ${SWAPSIZE_KB:-0} )) HIBERNATION_IMAGE_SIZE=$(cat /sys/power/image_size) -if (( SWAPSIZE > HIBERNATION_IMAGE_SIZE )) && [[ -f /etc/mkinitcpio.conf.d/omarchy_resume.conf ]]; then +if (( SWAPSIZE > HIBERNATION_IMAGE_SIZE )) && grep -q "resume" /etc/mkinitcpio.conf.d/omarchy_hooks.conf 2>/dev/null; then exit 0 else exit 1 diff --git a/bin/omarchy-hibernation-remove b/bin/omarchy-hibernation-remove index 60b4461840..a0bcf28795 100755 --- a/bin/omarchy-hibernation-remove +++ b/bin/omarchy-hibernation-remove @@ -3,10 +3,10 @@ # omarchy:summary=Remove hibernation setup including swap and boot resume settings # omarchy:requires-sudo=true -MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/omarchy_resume.conf" +HOOKS_CONF="/etc/mkinitcpio.conf.d/omarchy_hooks.conf" # Check if hibernation is configured -if [[ ! -f $MKINITCPIO_CONF ]] || ! grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then +if [[ ! -f $HOOKS_CONF ]] || ! grep -q "resume" "$HOOKS_CONF"; then echo "Hibernation is not set up" exit 0 fi @@ -52,8 +52,9 @@ sudo rm -f /etc/systemd/system/systemd-logind.service.d/hibernate.conf sudo rm -f /etc/systemd/system/systemd-hibernate.service.d/bypass.conf # Remove mkinitcpio resume hook -echo "Removing resume hook" -sudo rm "$MKINITCPIO_CONF" +echo "Removing resume hook from $HOOKS_CONF" +sudo sed -i 's/resume //' "$HOOKS_CONF" +sudo rm -f /etc/mkinitcpio.conf.d/omarchy_resume.conf echo "Regenerating initramfs..." sudo limine-mkinitcpio diff --git a/bin/omarchy-hibernation-setup b/bin/omarchy-hibernation-setup index eac140c30b..1b43e0258b 100755 --- a/bin/omarchy-hibernation-setup +++ b/bin/omarchy-hibernation-setup @@ -26,12 +26,12 @@ if ! $NO_REBUILD && ! command -v limine-mkinitcpio &>/dev/null; then exit 0 fi -MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/omarchy_resume.conf" +HOOKS_CONF="/etc/mkinitcpio.conf.d/omarchy_hooks.conf" SWAP_FILE="/swap/swapfile" RESUME_DROP_IN="/etc/limine-entry-tool.d/resume.conf" # Check if hibernation is already configured -if [[ -f $MKINITCPIO_CONF ]] && grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then +if [[ -f $HOOKS_CONF ]] && grep -q "resume" "$HOOKS_CONF"; then # Fix empty resume_offset if btrfs map-swapfile failed during initial setup if [[ -f $RESUME_DROP_IN ]] && grep -q 'resume_offset="$' "$RESUME_DROP_IN" && [[ -f $SWAP_FILE ]]; then RESUME_OFFSET=$(sudo btrfs inspect-internal map-swapfile -r "$SWAP_FILE" 2>/dev/null) @@ -82,10 +82,14 @@ if ! swapon --show | grep -q "$SWAP_FILE"; then sudo swapon -p 0 "$SWAP_FILE" fi -# Add resume hook to mkinitcpio -sudo mkdir -p /etc/mkinitcpio.conf.d -echo "Adding resume hook to $MKINITCPIO_CONF" -echo "HOOKS+=(resume)" | sudo tee "$MKINITCPIO_CONF" >/dev/null +# Add resume hook to mkinitcpio — must come before filesystems so the kernel +# can restore the hibernation image before root is mounted read-write. +if [[ -f $HOOKS_CONF ]] && ! grep -q "resume" "$HOOKS_CONF"; then + echo "Inserting resume hook before filesystems in $HOOKS_CONF" + sudo sed -i 's/\bfilesystems\b/resume filesystems/' "$HOOKS_CONF" +fi +# Remove legacy append-style config that put resume in the wrong position +sudo rm -f /etc/mkinitcpio.conf.d/omarchy_resume.conf # Ensure keyboard backlight doesn't prevent sleep sudo cp -p "$OMARCHY_PATH/default/systemd/system-sleep/keyboard-backlight" /usr/lib/systemd/system-sleep/ diff --git a/migrations/1778497305.sh b/migrations/1778497305.sh index d8fc91fa7e..a78b958199 100644 --- a/migrations/1778497305.sh +++ b/migrations/1778497305.sh @@ -1,14 +1,22 @@ echo "Fix hibernation on btrfs swapfiles with dm-crypt (systemd 256+)" -MKINITCPIO_CONF="/etc/mkinitcpio.conf.d/omarchy_resume.conf" +HOOKS_CONF="/etc/mkinitcpio.conf.d/omarchy_hooks.conf" +LEGACY_RESUME_CONF="/etc/mkinitcpio.conf.d/omarchy_resume.conf" SWAP_FILE="/swap/swapfile" RESUME_DROP_IN="/etc/limine-entry-tool.d/resume.conf" # Only apply if hibernation is configured -if [[ ! -f $MKINITCPIO_CONF ]] || ! grep -q "^HOOKS+=(resume)$" "$MKINITCPIO_CONF"; then +if ! grep -q "resume" "$HOOKS_CONF" 2>/dev/null && [[ ! -f $LEGACY_RESUME_CONF ]]; then exit 0 fi +# Move resume hook from appended position (after fsck) to before filesystems, +# so the kernel can restore the hibernation image before root is mounted rw. +if [[ -f $HOOKS_CONF ]] && ! grep -q "resume" "$HOOKS_CONF"; then + sudo sed -i 's/\bfilesystems\b/resume filesystems/' "$HOOKS_CONF" +fi +sudo rm -f "$LEGACY_RESUME_CONF" + # Add systemd bypass drop-ins for btrfs swapfile on dm-crypt # (see https://github.com/systemd/systemd/issues/30083) LOGIND_DROP_IN="/etc/systemd/system/systemd-logind.service.d/hibernate.conf"