Skip to content

Instantly share code, notes, and snippets.

@eloylp
Last active November 5, 2024 16:49
Show Gist options
  • Save eloylp/b0d64d3c947dbfb23d13864e0c051c67 to your computer and use it in GitHub Desktop.
Save eloylp/b0d64d3c947dbfb23d13864e0c051c67 to your computer and use it in GitHub Desktop.
Fedora 35 hibernation with swapfile, only for hibernation and resume

Fedora35 hibernation

This guide helps to configure the hibernation on a default Fedora35 (also worked fine in previous Fedora34) installation by using a swap file. The Fedora35 installation comes with btrfs as default filesystem. Also, it comes with a zram swap device:

$ swapon
NAME       TYPE      SIZE USED PRIO
/dev/zram0 partition   8G   0B  100

This device reserves a physical memory area in which all the content will be compressed (at its input) and uncompressed (at its output). But, we cannot hibernate with this type of swap space as it is only in memory.

After feedback received from contributors of this guide, seems we can say that this guide will work in both, encrypted and unencrypted setups.

important note before you continue: Some of us are experimenting problems with nvidia proprietary drivers. See below comments for more information on the investigation.

Motivation

Nowadays we have lots of RAM in our laptops. In my case, a guy with a laptop for programming, i only use swap space for hibernation. When i move from one location to another, i dont want to loose all my open stuff nor consuming battery while i am moving.

I want to preserve the zram swap device of the default configuration.

Solution

This are the steps we need to perform in our system:

  1. The hibernation is triggered by the user.
  2. The swap file is activated.
  3. The zram device is deactivated. If there are any memory pages present in zram, they will be moved to the activated (2) swap file.
  4. Hibernate the laptop.

And the following sequence for resuming:

  1. Power on the computer.
  2. Restore system state from the swap file at boot time.
  3. Activate the zram device.
  4. Deactivate the swap file. That could cause the zram device to start compressing data.

Seems there are efforts in order to make this much better, even with dynamic sized swap files, so only generating swap files for hibernation with the needed size on each moment, and more user friendly.

This guide will make use of a fixed size swap file, since probably for going further is better to just contribute in the mentioned systemd issue .

Steps

Default Fedora35 installations comes with btrfs as default filesystem. Such file system comes with the subvolume feature. Subvolumes are not partitions. They are just a logical separations of a filesystem at a file level. Some kind of operations like snapshots, will try to include the swap file. We need to prevent this by isolating the swap file into its own subvolume:

btrfs subvolume create /swap

The above will ensure the swap file will not be taken into account in other snaphots as they are not recursive in other subvolumes.

Next is to create our swapfile for hibernation. Ensure the specified size is enough for saving the contents of the RAM + the uncompressed contents of the zram swap space. How to determine the size ? that might be a rough estimation per use case. As root:

touch /swap/swapfile
chattr +C /swap/swapfile  ## Needed to disable Copy On Write on the file.
fallocate --length 33GiB /swap/swapfile  ## Please, calculate your size as mentioned in above comments.
chmod 600 /swap/swapfile 
mkswap /swap/swapfile 

Now lets prepare the path of the resume operation. We need add to the initramfs the necessary modules for resuming the system. We need to create a new file at /etc/dracut.conf.d/resume.conf with the following content:

add_dracutmodules+=" resume "

After that we need to regenerate the initramfs:

dracut -f

Next, we have to add the resume and resume_offset into the GRUB_CMDLINE_LINUX, so that Grub can instruct the kernel coordinates where the swap file resides, in order to resume the system.

For gathering the resume param we need the partition UUID in which the swap file is stored:

$ findmnt -no UUID -T /swap/swapfile
dbb0f71f-8fe9-491e-bce7-4e0e3125ecb8

Now lets gather the last needed data, we need the resume_offset. That is, the physical offset of our swap file in the file system. For doing that, we need to follow this guide.

Now we need to instruct GRUB to initialize the kernel with this coordinates. Edit the /etc/defaut/grub and grab there the parameters we just calculated:

GRUB_CMDLINE_LINUX="rd.luks.uuid=luks-4369a407-2be1-4f37-9764-ff848a0f2089 resume=UUID=dbb0f71f-8fe9-491e-bce7-4e0e3125ecb8 resume_offset=2459934 rhgb quiet"

Now lets re-configure the grub (UEFI setup assumed):

grub2-mkconfig -o /boot/grub2/grub.cfg

Note: We only want the swap file to be used when the hibernation takes place and in the resume stage. we are not going to configure fstab entries. To achieve that, one of the options is to use systemd. We are going to configure 2 systemd services. One for preparing the hibernation and the other one for resuming it:

For enabling the swap file and disabling the zram swap device before hibernation, lets create the file /etc/systemd/system/hibernate-preparation.service:

[Unit]
Description=Enable swap file and disable zram before hibernate
Before=systemd-hibernate.service

[Service]
User=root
Type=oneshot
ExecStart=/bin/bash -c "/usr/sbin/swapon /swap/swapfile && /usr/sbin/swapoff /dev/zram0"

[Install]
WantedBy=systemd-hibernate.service

The order is important in the above service definition. First of all we enable the swap file, which should have enough space to store the contents of the "in use RAM", plus the contents of the uncompressed zram swap device. Secondly, we disable the zram swap device. At that moment, the kernel will start moving all the memory pages from the zram swap device to the swap file if needed. After all of that, the hibernation will take place only in the swap file. Last step for the above script to have effect is to install this service in systemd:

# systemctl enable hibernate-preparation.service
Created symlink /etc/systemd/system/systemd-hibernate.service.wants/hibernate-preparation.service → /etc/systemd/system/hibernate-preparation.service.

We need to disable the swap file when the system just resumed. Lets create the /etc/systemd/system/hibernate-resume.service file:

[Unit]
Description=Disable swap after resuming from hibernation
After=hibernate.target

[Service]
User=root
Type=oneshot
ExecStart=/usr/sbin/swapoff /swap/swapfile

[Install]
WantedBy=hibernate.target

Then enable it by:

# systemctl enable hibernate-resume.service

Created symlink /etc/systemd/system/hibernate.target.wants/hibernate-resume.service → /etc/systemd/system/hibernate-resume.service.

In order to make the suspend-then-hibernate sequence to work, please take a look at this great comment and follow the instructions there.

In order to avoid false positives regarding to swap space, due to the zram device existence, we need to disable some checks:

mkdir -p /etc/systemd/system/systemd-logind.service.d/
cat <<-EOF | sudo tee /etc/systemd/system/systemd-logind.service.d/override.conf
[Service]
Environment=SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1
EOF

mkdir -p /etc/systemd/system/systemd-hibernate.service.d/
cat <<-EOF | sudo tee /etc/systemd/system/systemd-hibernate.service.d/override.conf
[Service]
Environment=SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1
EOF

Now you must reboot your computer, in order the steps until here take effect.

We also need to allow systemd-sleep system to read the swap file in SELinux . One option for allowing this is to make it fail. After that, use the audit2allow to do the white listing. Lets go step by step:

  1. Try to hibernate:

    # systemctl hibernate

    This should fail right now. Probably returning you to the display manager login. Also, logging details at /var/log/audit/audit.log .

  2. We can check the event happened with audit2allow inspecting the log, it will ouput something similar to this entry among others:

    # audit2allow -w -a
    
    type=AVC msg=audit(1630262756.460:2098): avc:  denied  { search } for  pid=26180 comm="systemd-sleep" name="swap" dev="dm-0" ino=256 scontext=system_u:system_r:systemd_sleep_t:s0 tcontext=system_u:object_r:unlabeled_t:s0 tclass=dir permissive=0
    	Was caused by:
    		Missing type enforcement (TE) allow rule.
    
    		You can use audit2allow to generate a loadable module to allow this access.3.
  3. To see what rule we must allow, just type:

    # audit2allow -b
    
    #============= systemd_sleep_t ==============
    allow systemd_sleep_t unlabeled_t:dir search;

    The above rule should be the only one in the output. If not, we could be white listing other elements by accident.

  4. Instruct SELinux to allow further attemps by executing the following commands:

    # cd /tmp
    # audit2allow -b -M systemd_sleep
    ******************** IMPORTANT ***********************
    To make this policy package active, execute:
    
    semodule -i systemd_sleep.pp
    
    # semodule -i systemd_sleep.pp

We should be ready to hibernate now. Lets just try it by:

# systemctl hibernate

After resuming, the swap file should be deactivated, so continuing with the default zram swap device setup:

$ swapon
NAME       TYPE      SIZE USED PRIO
/dev/zram0 partition   8G   0B  100

As a bonus, you can enable gnome environment hibernate buttons by installing this extension.

Please check that all your devices state are properly restored after resuming.

Troubleshooting

If you are having unexpected problems, inspectioning the journal will be of help to see errors in systemd scripts:

journalctl -f
@RNarayan73
Copy link

RNarayan73 commented Oct 30, 2024

Tried to follow this guide, but I’m stuck at the end by audit2allow. When I run the command sudo audit2allow -b, I get just two empty lines, the command audit2allow -b -M systemd_sleep also fails with the compilation error systemd_sleep.te:6:ERROR 'syntax error' at token '' on line 6:. Any ideas?

I tried this on Fedora 40 on a ext4 partition. My only deviations from the above steps were:

  • to create swapfile on /
  • use filefrag -v /swapfile to get the offset of the swapfile on ext4

The results were as follows:

  1. When I hibernate, the system goes into hibernation and switches off. It doesn't fail and get back to the login screen
  2. When I switch on the laptop after hibernation, the system doesn't resume; a hard reset is required to reboot the laptop and all the hibernated data is lost.
  3. running allow2audit -b outputs 2 blank lines (presumably because I had to do a hard reset) and
  4. 'sudo audit2allow -b -M systemd_sleep' throws up these very same errors

Has anyone had similar experiences?

Any suggestions for how I can investigate the issue after step 2?

@coy-yote
Copy link

There must be something difficult to catch that's missing in your setup @RNarayan73. I ran into similar issues a long while back with Fedora 38. I reinstalled, tried again, and it just worked.

I'm now running Fedora 40 with LUKS BTRFS. When I hibernate it locks the disk again and I'm reprompted for my LUKS password to wake it up. Even with that step everything comes up as expected for me now.

@universish
Copy link

universish commented Nov 5, 2024

I applied hibernation for fedora 41, hibernate is not working.
The system is installed on SSD. I installed it as btrfs. I updated from fedora 40 to fedora 41. I tried to enable Hibernate, but it didn't work.

I followed all the steps, and since most of them were blocked, I added sudo code and completed the step successfully.
https://fedoramagazine.org/hibernation-in-fedora-36-workstation/
Only this part was a problem:

    After you’ve logged in again check the audit log, compile a policy and install it. The -b option filters for audit log entries from last boot. The -M option compiles all filtered rules into a module, which is then installed using semodule -i.

    **$ audit2allow -b** #============= systemd_sleep_t ============== allow systemd_sleep_t unlabeled_t:dir search; **$ cd /tmp $ audit2allow -b -M systemd_sleep $ semodule -i systemd_sleep.pp**

I got the following errors:

$ sudo audit2allow -b
[sudo] password for macellan: 
Option ENRICHED not found - line 9
NOTE - using built-in end_of_event_timeout: 2
NOTE - using built-in logs: /var/log/audit/audit.log

$ cd /tmp

$ audit2allow -b -M systemd_sleep
Error opening config file (Erişim engellendi)
NOTE - using built-in end_of_event_timeout: 2
NOTE - using built-in logs: /var/log/audit/audit.log
Error opening /var/log/audit/audit.log (Erişim engellendi)
compilation failed:
systemd_sleep.te:6:ERROR 'syntax error' at token '0' on line 6:


/usr/bin/checkmodule:  error(s) encountered while parsing configuration
$ sudo audit2allow -b -M systemd_sleep
Option ENRICHED not found - line 9
NOTE - using built-in end_of_event_timeout: 2
NOTE - using built-in logs: /var/log/audit/audit.log
could not write output file: [Errno 13] Erişim engellendi: 'systemd_sleep.te'

$ audit2allow -b -M systemd_sleep
Option ENRICHED not found - line 9
NOTE - using built-in end_of_event_timeout: 2
NOTE - using built-in logs: /var/log/audit/audit.log
could not write output file: [Errno 13] Erişim engellendi: 'systemd_sleep.te'
$ systemctl hibernate
Call to Hibernate failed: Sleep verb 'hibernate' is not configured or configuration is not supported by kernel
$ swapon
NAME       TYPE      SIZE USED PRIO
/dev/zram0 partition   8G   0B  100
$ semodule -i systemd_sleep.pp
libsemanage.semanage_create_store: Could not read from module store, active modules subdirectory at /var/lib/selinux/targeted/active/modules. (Permission denied).
libsemanage.semanage_direct_connect: could not establish direct connection (Permission denied).
semodule:  Could not connect to policy handler

audit2allow -w -a
In this command output There was no line related to “systemd-sleep”.

I rebooted a few more times, but hibernate didn’t work:

$ systemctl hibernate
Call to Hibernate failed: Sleep verb 'hibernate' is not configured or configuration is not supported by kernel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment