fedora, btrfs, and hibernation

Here’s how to hibernate to disk when using the automatic partitioning scheme in Fedora 34 (btrfs+zram), optionally with luks enabled.

Originally this post was a lot longer, detailing each and every step suited for you to copy and paste. I decided to go ahead and automate the whole process with ansible instead.

A few things to keep in mind:

  • only run this after you’ve audited the playbook and understand what it’s doing
  • you’re using this at your own risk
  • Secure Boot must be disabled
  • this has only been tested with Fedora 34

FAQ

  • Why can’t I hibernate (by default) in Fedora 34?

Since Fedora 33 (yes, this actually pertains to Fedora 33 as well, but it has not been tested), zram is now being used in place of traditional swap. Since zram is backed by actual volatile memory, it can’t be used for hibernation (write current memory state to disk - within swap, and then poweroff). This means we’ll have to use a traditional swapfile backed by storage.

  • Does btrfs make a difference?

Yes, you may notice the steps to create a swapfile are different than what you’re used to. We’ll have to use kernel 5.0+, and set NOCOW in order to gain btrfs swapfile support. More information is available on the btrfs wiki along with the ArchWiki.

Process

Below provides a very high level overview of the process, for more detail, check out my ansible playbook.

  • create an empty swapfile
    • set its attributes to NOCOW (chattr +C)
    • allocate the swapfile to be the same size as your system’s memory
      • note: you may be okay with a swap partition smaller than the size of your memory
  • set up a Linux swap area on your swapfile
  • enable your swapfile for paging and swapping
  • update /etc/fstab with your swapfile information
  • since there’s already an existing swap target (default zram device), create systemd overrides to avoid false positive errors about swap size.
  • get the physical swap offset by running btrfs_map_physical.c, as recommended by the ArchWiki.
    • since this step includes running some C program from the internet, I’ve opted to use a container (you should be auditing the code regardless though)
  • calculate the real swap offset
    • this is done by dividing the physical swap offset from above by the system’s page size
  • construct resume arguments for grub
    • use the uuid of your swapfile along with the swap offset calculated above
    • use grubby to update kernel arguments to include resume and resume_offset
  • add the resume module to dracut and regenerate the initramfs
    • add resume module under /etc/dracut.conf.d/resume.conf
    • dracut -f