• Hibernation in Fedora Workstation

    From Fedora Magazine@fedora.magazine@bbs.alt119.net to All on Tue Aug 30 20:45:52 2022
    From Newsgroup: alt.os.linux.fedora



    This article walks you through the manual setup for hibernation in Fedora Linux 36 Workstation using BTRFS and is based on a gist by eloylp on github.

    Goal and Rationale

    Hibernation stores the current runtime state of your machine - effectively the contents of your RAM, onto disk and does a clean shutdown. Upon next boot this state is restored from disk to memory such that everything, including open programs, is how you left it.

    Fedora Workstation uses ZRAM. This is a sophisticated approach to swap using compression inside a portion of your RAM to avoid the slower on-disk swap files. Unfortunately this means you don';t have persistent space to move your RAM upon hibernation when powering off your machine.

    How it works

    The technique configures systemd and dracut to store and restore the contents of your RAM in a temporary swap file on disk. The swap file is created just before and removed right after hibernation to avoid trouble with ZRAM. A persistent swap file is not recommended in conjunction with ZRAM, as it creates some confusing problems compromising your systems stability.

    A word on compatibility and expectations

    Hibernation following this guide might not work flawless on your particular machine(s). Due to possible shortcomings of certain drivers you might experience glitches like non-working wifi or display after resuming from hibernation. In that case feel free to reach out to the comment section of the gist on github, or try the tips from the troubleshooting section at the bottom of this article.

    The changes introduced in this article are linked to the systemd hibernation.service and hibernation.target units and hence won';t execute on their own nor interfere with your system if you don';t initiate a hibernation. That being said, if it does not work it still adds some small bloat which you might want to remove.

    Hibernation in Fedora Workstation

    The first step is to create a btrfs sub volume to contain the swap file.

    $ btrfs subvolume create /swap

    In order to calculate the size of your swap file use swapon to get the size of your zram device.

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

    In this example the machine has 16G of RAM and a 8G zram device. ZRAM stores roughly double the amount of system RAM compressed in a portion of your RAM. Let that sink in for a moment. This means that in total the memory of this machine can hold 8G * 2 + 8G of RAM which equals 24G uncompressed data. Create and configure the swapfile using the following commands.

    $ touch /swap/swapfile
    # Disable Copy On Write on the file
    $ chattr +C /swap/swapfile
    $ fallocate --length 24G /swap/swapfile
    $ chmod 600 /swap/swapfile
    $ mkswap /swap/swapfile

    Modify the dracut configuration and rebuild your initramfs to include the

    resume

    module, so it can later restore the state at boot.

    $ cat
    add_dracutmodules+=" resume "
    EOF
    $ dracut -f

    In order to configure grub to tell the kernel to resume from hibernation using the swapfile, you need the UUID and the physical offset.

    Use the following command to determine the UUID of the swap file and take note of it.

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

    Calculate the correct offset. In order to do this you';ll unfortunately need gcc and the source of the btrfs_map_physical tool, which computes the physical offset of the swapfile on disk. Invoke gcc in the directory you placed the source in and run the tool.

    $ gcc -O2 -o btrfs_map_physical btrfs_map_physical.c
    $ ./btrfs_map_physical /path/to/swapfile

    FILE OFFSET EXTENT TYPE LOGICAL SIZE LOGICAL OFFSET PHYSICAL SIZE DEVID PHYSICAL OFFSET
    0 regular 4096 2927632384 268435456 1 4096 prealloc 268431360 2927636480 268431360 1 4009766912
    268435456 prealloc 268435456 3251634176 268435456 1 4333764608
    536870912 prealloc 268435456 3520069632 268435456 1 4602200064
    805306368 prealloc 268435456 3788505088 268435456 1 4870635520
    1073741824 prealloc 268435456 4056940544 268435456 1 5139070976
    1342177280 prealloc 268435456 4325376000 268435456 1 5407506432
    1610612736 prealloc 268435456 4593811456 268435456 1 5675941888

    The first value in the PHYSICAL OFFSET column is the relevant one. In the above example it is 4009762816.

    Take note of the pagesize you get from getconf PAGESIZE.

    Calculate the kernel resume_offset through division of physical offset by the pagesize. In this example that is 4009762816 / 4096 = 978946.

    Update your grub configuration file and add the resume and resume_offset kernel cmdline parameters.

    grubby --args="resume=UUID=dbb0f71f-8fe9-491e-bce7-4e0e3125ecb8 resume_offset=2459934" --update-kernel=ALL

    The created swapfile is only used in the hibernation stage of system shutdown and boot hence not configured in fstab. Systemd units control this behavior, so create the two units hibernate-preparation.service and hibernate-resume.service.

    $ cat
    [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
    EOF
    $ systemctl enable hibernate-preparation.service
    $ cat
    [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
    EOF
    $ systemctl enable hibernate-resume.service

    Systemd does memory checks on login and hibernation. In order to avoid issues when moving the memory back and forth between swapfile and zram disable some of them.

    $ mkdir -p /etc/systemd/system/systemd-logind.service.d/
    $ cat /override.conf
    [Service]
    Environment=SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1
    EOF
    $ mkdir -p /etc/systemd/system/systemd-hibernate.service.d/
    $ cat
    [Service]
    Environment=SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1
    EOF

    Reboot your machine for the changes to take effect. The following SELinux configuration won';t work if you don';t reboot first.

    SELinux won';t like hibernation attempts just yet. Change that with a new policy. An easy although "brute" approach is to initiate hibernation and use the audit log of this failed attempt via audit2allow. The following command will fail, returning you to a login prompt.

    systemctl hibernate

    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

    Check that hibernation is working via systemctl hibernate again. After resume check that ZRAM is indeed the only active swap device.

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

    You now have hibernation configured.

    GNOME Shell hibernation integration

    You might want to add a hibernation button to the GNOME Shell "Power Off / Logout" section. Check out the extension Hibernate Status Button to do so.

    Troubleshooting

    A first place to troubleshoot any problems is through journalctl -b. Have a look around the end of the log, after trying to hibernate, to pin-point log entries that tell you what might be wrong.

    Another source of information on errors is the Problem Reporting tool. Especially problems, that are not common but more specific to your hardware configuration. Have a look at it before and after attempting hibernation and see if something comes up. Follow up on any issues via BugZilla and see if others experience similar problems.

    Revert the changes

    To reverse the changes made above, follow this check-list:

    remove the swapfileremove the swap subvolumeremove the dracut configuration and rebuild dracutremove kernel cmdline args via grubby -remove-args=disable and remove hibernation preparation and resume servicesremove systemd overrides for systemd-logind.service and systemd-hibernation.serviceremove SELinux module via semodule -r systemd_sleep

    Credits and Additional Resources

    This article is a community effort based primarily on the work of eloylp. As author of this article I';d like to make transparent that I';ve participated in the discussion to advance the gist behind this but many more minds contributed to make this work. Make certain to check out the discussion on github.

    There are already some ansible playbooks and shell scripts to automate the process depicted in this guide. For example check out the shell scripts by krokwen and pietryszak or the ansible playbook by jorp

    See the arch wiki for the full guide on how to calculate the swapfile offset.

    https://fedoramagazine.org/hibernation-in-fedora-36-workstation/
    --- Synchronet 3.21d-Linux NewsLink 1.2