BTRFS swap file with hibernate (update to 2022)


I’ve been collecting the steps to using a swap file with BTRFS. Basically, some things have changed recently, so:

  1. Apparently the compression attribute is no longer needed: Can't create swap file: swapon failed: Invalid argument - #7 by dalto

  2. Does this script still work with a minor adjustment? How to enable Hibernation to Swapfile on BTRFS? - #2 by joekamprad

  3. Since the attribute need not be set, does that mean that in fact BTFS now allows you to have a compressed swap file?

So far, I have created a @swap subvolume and followed the steps in the script manually (set the copy-on-write attribute and allocated 16G on disk which matches my RAM size). I updated my /etc/fstab to use that and currently htop confirms that swap is active on the file so I’m happy with that. Out of fear about compression I use comrpess=none option as follows:

# grep swap /etc/fstab 
UUID=10840302-2838-4187-842a-eee8f883ed50 /swap          btrfs   subvol=/@swap,defaults,noatime,compress=none 0 0
/swap/swapfile                            none           swap    defaults 0 0

So I am happy that swap actually works and the first part of the script should recreate my current state.

Now, the last part of the script edits grub to set the resume UUID and the resume offset. It downloads and compiles a small C program which I hope still works (Another question; does it work only when compress=none or does it also work when you actually allow BTRFS to compress the file?). Here is that part:

    # :: btrfs_map_physical :: #
    gcc -O2 -o btrfs_map_physical btrfs_map_physical.c

    # :: edit grub :: #
    offset=$(./btrfs_map_physical /swap/swapfile)
    offset_arr=($(echo ${offset}))
    offset_pagesize=($(getconf PAGESIZE))
    offset=$((offset_arr[25] / offset_pagesize))
    btrfsroot=`findmnt / -no UUID`

Running these manually yielded for me:

# echo $btrfsroot 
# echo $offset

I made a copy of /etc/default/grub and ran the next commands on that:

    sed -i "s#loglevel=3#resume=UUID=$btrfsroot loglevel=3#" /etc/default/grub.copy 
    sed -i "s/loglevel=3/resume_offset=$offset loglevelbtrfs property set /swap/swapfile compression none=3/" /etc/default/grub.copy

This actually errored out with:

+ sed -i 's/loglevel=3/resume_offset=7611648 loglevelbtrfs property set /swap/swapfile compression none=3/' /etc/default/grub.copy
sed: -e expression #1, char 64: unknown option to `s'

I think I just need to escape the / character in ‘/swap/swapfile’ as sed applies it to the s/// command. So I went with;

    sed -i "s#loglevel=3#resume=UUID=$btrfsroot loglevel=3#" /etc/default/grub.copy 
    sed -i "s/loglevel=3/resume_offset=$offset loglevelbtrfs property set \/swap\/swapfile compression none=3/" /etc/default/grub.copy

This yielded:

# diff /etc/default/grub /etc/default/grub.copy
< GRUB_CMDLINE_LINUX_DEFAULT="quiet resume=UUID=46a0d838-1f5e-4e1b-87c5-914aad2cd98c loglevel=3 nowatchdog nvme_load=YES"
> GRUB_CMDLINE_LINUX_DEFAULT="quiet resume=UUID=46a0d838-1f5e-4e1b-87c5-914aad2cd98c resume=UUID=10840302-2838-4187-842a-eee8f883ed50 resume_offset=7611648 loglevelbtrfs property set /swap/swapfile compression none=3 nowatchdog nvme_load=YES"

Now this seems a bit garbled to me. Clearly I need to remove resume=UUID=46a0d838-1f5e-4e1b-87c5-914aad2cd98c which is the old UUID and go with the second resume entry which is the new one. But loglevelbtrfs property set /swap/swapfile compression none=3 seems like it should be btrfs property set /swap/swapfile compression none loglevel=3 instead?


No, it means it automatically disables compression when you disable CoW.

Sure if you need to set it in /etc/default/grub?

Thanks, I see now. So now I am certain about the setup of the disk, but the grub part seems wrong. Even accounting for the change I mentioned, I would get:

GRUB_CMDLINE_LINUX_DEFAULT="quiet resume=UUID=10840302-2838-4187-842a-eee8f883ed50 resume_offset=7611648 loglevel=3 btrfs property set /swap/swapfile compression none nowatchdog nvme_load=YES"  

I think I just need:

GRUB_CMDLINE_LINUX_DEFAULT="quiet resume=UUID=10840302-2838-4187-842a-eee8f883ed50 resume_offset=7611648 loglevel=3 nowatchdog nvme_load=YES" 

This must be some problem in that linked script…
Why would it try to pass the btrfs command as a kernel parameter?

That’s what doesn’t make sense to me either and I just realised it was a problem I introduced! I must have accidentally pasted that into the script because in the original (if you follow the link) that does not exist. I am fixing that now and will report back.

1 Like

The sed line: you have too many slashes (/ characters).
Note that with sed you can use other characters instead of slash, e.g.

echo foobar | sed 's/r/x/'
echo foobar | sed 's|r|x|'

do the same thing.

1 Like

Agreed. I actually escaped the / as \/ instead, but nice to learn I could specify my own delimiter.


Thank you all for your help so far! I’ve rebooted using the resume/resume_offset parameters and it worked fine!

Now, my question is, how do I actually suspend to disk? Is there some package I need to install? Is there some command I can run?

I am using GNOME 42 and the only option I have is to sleep as opposed to power off after writing to disk):

Screenshot from 2022-06-05 19-50-31

systemctl hibernate

Thanks @pebcak , this did the trick!

When I restarted, I was thrown in front of a locked screen:

  • Entering my password would not work (authentication failed)
  • Switching to another TTY with Ctrl+Alt+Fx would not give me another login prompt
  • I could ping the host over the network, but trying to SSH from another host would fail with kex_exchange_identification: read: Connection reset

It seems to me that I am missing something from the resume part? I think that the actual loading from the disk into RAM works, but maybe something that is needed to properly restore the state does not run?

I should note that I updated /etc/mkinitcpio.conf with:

HOOKS="base udev autodetect modconf block keyboard keymap consolefont filesystems resume"

Any ideas what could be missing? I had to power-cycle to be able to work again…

Well, what do you know, I just retried it and it worked! I was able to systemctl hibernate and then power on again into my running desktop. Only difference is that I exited all applications except for the GNOME terminal which I used to run systemctl hibernate on.

Will retry with some running applications now… (e.g. Firefox where I am typing this).

Did you rebuild your initramfs afterwards?

Yes, basically in my history I see:

  428  grub-mkconfig -o /boot/grub/grub.cfg
  429  mkinitcpio -P

I just tried again with Firefox running and it worked initially, but then froze. I was actually in the middle of writing:

Success! I am typing this after another hibernate/power-on cycle…

As I was typing the above the machine froze…

1 Like