Setup hibernation on Silverblue / Kionite?

There is a tutorial for 36 Workstation. I for now replaced every “/swap” with “/var/swap”, but not sure if that would just work.

Like creating the swap partition in the /var directory and using it the same way.

Do you need to change partitions during setup or something? Is everything included in the setup tutorial?

2 Likes

Yes, you can follow that tutorial and get hibernation to work on Fedora Silverblue.

I have some comments on that guide, however:

  1. dracut is not needed for Silverblue.
  2. You can get the resume offset with sudo btrfs inspect-internal map-swapfile -r /var/swap/swapfile if your btrfs tools are v >= 6.1, which they are on Silverblue 37. There’s no need to download and compile an external program.
  3. The hibernate-preparation.service and hibernate-resume.service configuration is unnecessary and just slows down the hibernation process. Rather, you should just add your swapfile to /etc/fstab.

To elaborate on item 3 above, there’s no harm in automatically mounting your swapfile via /etc/fstab. By default it will get a much lower priority than your zram swap, so it will most likely only ever be used when hibernating. The system will automatically know to use the swapfile for hibernating, because the zram swap is not large enough, as it is configured to be 50% the size of your total RAM.

3 Likes

Hi Eddie, I have done the tutorial along with your comments. Now when I want to run systemctl hibernate for the first time the following error message appears. The swapfile is mounted and the resume parameters are added. Any idea?

Error:
Failed to hibernate system via logind: Not enough swap space for hibernation

Edit:
My failure i forgot this chapter:

$ 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

Does your system use the swap in addition to the ram? Or just for hibernating?

Just for hibernating.

NAME TYPE SIZE USED PRIO
/dev/zram0 partition 8G 0B 100
/var/swap/swapfile file 40G 0B -2

The exact same thing happened to me :slight_smile:

I’m not sure why it should even be needed though, as my swapfile should be large enough to fit everything. It’s possibly related to this: Failed to hibernate system via logind: Not enough swap space for hibernation · Issue #15354 · systemd/systemd · GitHub

Basically that bug in that link indicates there were permissions issues with the systemd-logind.service accessing the swapfile. They recommended adding an override to the service to set a debug flag.
When I did that I found it just reported that access was denied to the swapfile, with no further details given.

While you could just disable all checks of the swapfile before hibernating, those checks are there for a reason so it’s probably better to fix it so they work than to just disable them.

What’s happening on my system, and almost certainly on yours, is that SELinux is denying our access to the swapfile we created manually. If you were to run sudo audit2allow -b you’ll see:

#============= systemd_logind_t ==============
allow systemd_logind_t unlabeled_t:dir search;

The fix isn’t the obvious one though, you shouldn’t just blindly add that to your policy. First, you should actually label your swapfile how it’s expected to be, with the swapfile_t label. The swapfile_t label needs to be added to the /var/swap folder and everything inside it, which we can make a permanent rule and then apply it to the current system with:

sudo semanage fcontext -a -t swapfile_t '/var/swap(/.*)?'
sudo restorecon -RF /var/swap

You can confirm your swapfile is now correctly labeled with ls -Z /var/swap/swapfile.

Now when we try to hibernate and it fails, audit2allow gives us a new policy recommendation:

#============= systemd_logind_t ==============
allow systemd_logind_t swapfile_t:dir search;
allow systemd_logind_t unlabeled_t:dir search;

The unlabled_t is leftover from the previous denials, and we don’t want that. Since it’s much easier to use audit2allow to generate the policy for us than it is to do it by hand, I recommend just rebooting to clear the audit.log, running only the hibernate command again, and then using audit2allow to create policy for only the one denial.

Reboot, then only run sudo systemctl hibernate after the boot, then the following to generate policy from the denial and load the policy:

sudo audit2allow -b
# check that only the swapfile_t policy recommendation is listed
sudo audit2allow -b -M systemd_hibernate
sudo semodule -i systemd_hibernate.pp

The system should now hibernate (or at least flicker and log you out if there are other hibernate related problems that cause it to immediately resume)

1 Like

For my own benefit, when I find this later, the full set of steps for hibernation setup is the following.

1. Create the btrfs subvolume

sudo btrfs subvolume create /var/swap

2. Calculate the needed size of your swapfile

Use swapon to get the size of your current zram swap. The size you need for the swapfile (to be safe) is:

(ram_size - zram_size) + (zram_size * 2)

Or equivalently:

ram_size + zram_size

3. Create the swapfile

You no longer have to do all the steps by hand, btrfs has a subcommand to do them all for you;

sudo btrfs filesystem mkswapfile --size 40g --uuid clear /var/swap/swapfile

I have 32G or RAM and 8G of zram, so I’m creating a 40 G(i)B swapfile in this case.

4. Find the UUID of the swapfile

sudo findmnt -no UUID -T /var/swap/swapfile

Looks something like ffe8a473-6525-4a46-a7e1-e7fea5bdf20e

5. Find the offset on the disk

Btrfs has a native command for this too now, and it doesn’t require any math to convert it.

sudo btrfs inspect-internal map-swapfile -r /var/swap/swapfile

Looks something like 6148378

6. Add the kernel parameters for the swapfile

sudo grubby --args="resume=UUID=ffe8a473-6525-4a46-a7e1-e7fea5bdf20e resume_offset=6148378" --update-kernel=ALL

Replace the ffe8a473-6525-4a46-a7e1-e7fea5bdf20e and 6148378 with the values you found in the prior steps

7. Add the swapfile to your fstab with low priority

Add a line like the following to the end of your /etc/fstab. This sets the priority of the swapfile as low as it can possibly be so it won’t get used unless it has to:

# add swapfile, but set priority as low as possible so it only gets used if zram (pri=100) can't be
/var/swap/swapfile                        none                    swap    defaults,pri=0 0 0

8. Enable swapfile

sudo swapon /var/swap/swapfile

9. Add SELinux labeling

Add the permanent rule for the label, and apply the rule to the files.

sudo semanage fcontext -a -t swapfile_t '/var/swap(/.*)?'
sudo restorecon -RF /var/swap

10. Reboot

You want to have a clean audit.log for the next step, and the best way to do that is to reboot and do absolutely nothing else except the steps that follow.

11. Try to Hibernate (it won’t work)

After you login, try to hibernate from the command-line. It won’t work, but it generates audit events we can use for the next steps.

sudo systemctl hibernate
# it won't work, but that's expected

12. Generate SELinux policy

Check to make sure you don’t have other random things in your policy.

sudo audit2allow -b

You should only get back:

#============= systemd_logind_t ==============
allow systemd_logind_t swapfile_t:dir search;

If you get back anything else, you’ll need to manually edit the generated policy, and recompile it by hand.

Generate the policy from the audit2allow rules, which gives you both a *.te file and a compiled *.pp file. If you need to do manual changes, you’ll use the .te file and overwrite the .pp file from this.

cd /tmp
sudo audit2allow -b -M systemd_hibernate

13. (Optional) Manually edit and re-compile the policy

If you had extraneous results in your audit2allow, those are now in the policy it generated for you. Luckily it provides you the source file (.te) as well as the compiled policy file (.pp), so you can edit and recompile.

Edit the policy file and fix it up. Editing SELinux policy files is beyond the scope of this, but in simple cases might not be too hard. Make sure to remove unnecessary requires as well as the polcies.

sudo nano systemd_hibernate.te

Then re-compile it into a .pp file.

sudo checkmodule -M -m -o systemd_hibernate.mod systemd_hibernate.te
sudo semodule_package -o systemd_hibernate.pp -m systemd_hibernate.mod

14. Load the new SELinux policy

Load the new policy into the permanent database.

sudo semodule -i systemd_hibernate.pp

15. Try to Hibernate (again)

This time it should actually work. It’s possible you have other problems though. One common case is the screen only going black for a second and then coming back to the login screen, which can be caused by a few different things.

Troubleshooting

Adding an override to the systemd-logind.service so it include more debug data in the journalctl should almost always be your first step.

  1. Add the override folder and file

The normal systemctl edit method doesn’t seem to work for me for some reason.

sudo mkdir -p /etc/systemd/system/systemd-logind.service.d/
sudo nano /etc/systemd/system/systemd-logind.service.d/override.conf

You can undo this change later by just removing the file and folder that were created, and rebooting.

  1. Populate the override file

Add this to the file and save it:

[Service]
Environment=SYSTEMD_LOG_LEVEL=debug
  1. Reboot your system.

Restarting the service logs you out and causes weird bugs in the service, so just reboot.

  1. View the logs for the service
sudo journalctl -u systemd-logind.service
# scroll to the bottom with 'GG' to see the most recent logs
6 Likes

Thanks a lot for this! I am currently automating this into a script but have some questions

  1. Is zram always RAM/4 ?

  2. I am also currently getting errors at point 12

The rest worked on Fedora Kinoite!

@siosm is there any issue with OSTree? Can this complete procedure be done here?

1 Like

I don’t think you can assume zram will always be a specific device, no.

As I mentioned, SELinux policy is it’s own while rabbit hole. There’s one system audit.log file that captures all security events (loggable successful ones and attempted violations), and audit2allow will parse out all violations in the file and attempt to generate policy using available macros to allow them. If you have any other security events on the system, they’ll also show up in the log and audit2allow will generate policy to allow them too.
Luckily the policy you need for this is pretty much dependent only on the swapfile name and location. Really all that should be missing is a persistent SELinux label on the file, and a macro call to allow it to be used as a swapfile. If you generate the code for the policy once, you should be able to reuse it on any system with matching file name and location of the swapfile,.and just follow the instructions for compiling the policy from source (explained if you had to hand-edit it).

1 Like

IIRC, Fedora sets zram to 50% of the host system’s RAM, but it caps out at 8GB.

1 Like

Nice guide :+1:

Are you sure it’s not enough to just go with ram_size? My understanding is that zram already resides in RAM; this would mean it’s not possible to exceed the max cap for RAM, and that it’s therefore unnecessary to allocate any additional swap space for zram.

Hibernation just moves RAM to swap – it doesn’t treat compressed zram data any differently from other data.

Oh my mistake, I misunderstood the RAM/4. It’s technically possible to adjust your zram size, so what assumption to make is up to you.
Zram is compressed, and the listed zram size is the size of the compressed content. If you read about it, the recommended best case compression assumption seems to be about 50%, so it’s recommended to assume your worst case uncompressed size is zram_size * 2. The compressed size takes up RAM, so you have to make sure to subtract that space from your overall RAM size when calculating the swapfile size.

At the end, your swapfile just needs to be big enough to hold all your RAM contents. You can make it bigger if you’re unsure or want to be really sure.
With the SELinux policy added, you no longer have to disable pre-checking of the swapfile, so it will always prevent sleeping and report that the swapfile is too small if it isn’t big enough at some point. That check occurs on every sleep/hibernate, and errors are reported to journald.

Ok yes thats what mine seems to be too

Yes, zram is compressed, but I don’t see how that is relevant when it comes to hibernating? To me it seems like it just moves whatever is in RAM to swap. Then, when resuming it just moves whatever is in swap to RAM. Just moving raw data, no compression/decompression being performed.

So a swapfile the exact same size as your RAM seems like it should always be enough, as long as you make sure that the swapfile is only ever used for hibernation.

I’ve configured two laptops like this and haven’t noticed any issues, though maybe I’ve just been lucky.

This is also somewhat dependent upon the way ram is moved to the swap partition AND how the graphics are suspended/hibernated. For a true quick restart without graphics problems the hibernation should also move the content of the GPU ram to swap, and that also requires swap space. Users of nvidia gpus have a specific service to properly suspend and restore the gpu ram.

To me that would mean the swap used for hibernation should be at a minimum the size of RAM + the size of GPU RAM + a little extra for safety sake.

My understanding of zram is that what you write to the /dev/zram device is compressed and stores in a pre-allocated section of RAM. The size listed for the zram is the size pre-allocated in RAM.

The principle of hibernation/suspend-to-disk is that everything in RAM needs to be stored in disk instead. That necessitates the contents of zram being copied out to the swapfile, zram disabled, and then the remaining RAM contents added to the swapfile (effectively). I haven’t heard anyone being capable of writing the compressed zram contents from RAM directly to the swapfile and back.

What may be confusing or misleading is that the default zram and swapfile handling for suspend-to-disk was updated a while ago to transparently handle this zram content copying and disabling (according to something I read somewhere). And if you aren’t using a lot of the zram space, it won’t need that big of a swapfile. However if you don’t make the swapfile big enough for your worst case, you may unexpectedly find hibernate didn’t work or got corrupted (depending on whether you have pre-checking of file size disabled or not).

Maybe I’m mistaken, most of this info is cobbled together and then verified, so it could be there’s no changes for automatic zram disabling and enabling and the swapfile does only need to be the size of RAM. But it would be had to know for sure unless you had a very full zram when trying to hibernate, and the pre-checksnn the swapfile enabled.

I wasn’t aware.of that, though it makes sense. I haven’t encountered anything that mentioned including GPU RAM size in swapfile size calculations before, even when zram isn’t involved.
I don’t have a system with a dGPU, so I don’t have that service. Do you have details on what it’s called that I could look up?

These are the services installed for nvidia GPUs when a user installs the proprietary drivers from rpmfusion. The specific ones I mentioned are the suspend, hibernate, and resume services.

$ ls /lib/systemd/system/nvidia-*
/lib/systemd/system/nvidia-fallback.service      /lib/systemd/system/nvidia-powerd.service
/lib/systemd/system/nvidia-hibernate.service     /lib/systemd/system/nvidia-resume.service
/lib/systemd/system/nvidia-persistenced.service  /lib/systemd/system/nvidia-suspend.service

Digging into what actually happens (I do not use suspend or hibernate) I see that the procedure creates and saves the gpu ram data to a file and restores from the same file so it is not in the swap space. The data is saved, just not to swap.

1 Like

Thanks for the info, I had no idea!

It does sound like it’s unnecessary to include the GPU RAM size in the swapfile calculation though, is already creating it’s own swapfile equivalent just for the GPU.

Awesome tutorial, thanks a lot.

In case this helps anyone, as part of the troubleshooting, I had to do another round of audit2allow to fix systemd sleep not being able to access the swapfile:

#============= systemd_sleep_t ==============
allow systemd_sleep_t swapfile_t:dir search;

I just followed step 12. again with that and used a different module name (systemd_sleep). (Not sure if there’s a way to “amend” the SELinux module created previously, which would be nicer, I guess (?), but it works now, so… :person_shrugging:)

1 Like