TranslateProject/sources/tech/20220810 Hibernation in Fedora Workstation.md
DarkSun b160f1fa28 选题[tech]: 20220810 Hibernation in Fedora Workstation
sources/tech/20220810 Hibernation in Fedora Workstation.md
2022-08-11 05:02:47 +08:00

12 KiB
Raw Permalink Blame History

Hibernation in Fedora Workstation

Photo by Beth Jnr on Unsplash

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 dont 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 wont execute on their own nor interfere with your system if you dont 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 <<-EOF | sudo tee /etc/dracut.conf.d/resume.conf
    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 youll 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      <4009762816>
    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 <<-EOF | sudo tee /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
    EOF
    $ systemctl enable hibernate-preparation.service
    $ cat <<-EOF | sudo tee /etc/systemd/system/hibernate-resume.service
    [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 <<-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

Reboot your machine for the changes to take effect. The following SELinux configuration wont work if you dont reboot first.

SELinux wont 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 youve 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 swapfile
  • remove the swap subvolume
  • remove the dracut configuration and rebuild dracut
  • remove kernel cmdline args via grubby remove-args=
  • disable and remove hibernation preparation and resume services
  • remove systemd overrides for systemd-logind.service and systemd-hibernation.service
  • remove 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 Id like to make transparent that Ive 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.


via: https://fedoramagazine.org/hibernation-in-fedora-36-workstation/

作者:Alexander Wellbrock 选题:lujun9972 译者:译者ID 校对:校对者ID

本文由 LCTT 原创编译,Linux中国 荣誉推出