There isn’t a single place for all the adjustments that are needed to get this to work so I am putting it all here in one place:
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
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. Repeat 10-14 for this SELinux policy:
#============= systemd_sleep_t ==============
allow systemd_sleep_t self:capability sys_admin;
16. Create script to disable/enable wifi with hibernate
Save this script to: /usr/lib/systemd/system-sleep/hibernate-pre-post.sh
#!/bin/bash
WiFiModule=mt7921e
case "$1 $2" in
"pre hibernate" | "pre suspend-then-hibernate")
modprobe -r $WiFiModule
;;
"post hibernate" | "post suspend-then-hibernate")
modprobe $WiFiModule
;;
*)
:
;;
esac
Make sure it is executable -
sudo chmod +x hibernate-pre-post.sh
reference - [Guide] Solution for WiFi problems after hibernate (non-systemd)
17. 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 includes more debug data in the journalctl
should almost always be your first step.
- 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.
- Populate the override file
Add this to the file and save it:
[Service]
Environment=SYSTEMD_LOG_LEVEL=debug
- Reboot your system.
Restarting the service logs you out and causes weird bugs in the service, so just reboot.
- View the logs for the service
sudo journalctl -u systemd-logind.service
# scroll to the bottom with 'GG' to see the most recent logs
references: Setup hibernation on Silverblue / Kinoite? - #8 - Fedora Discussion
P.S.
You can use this script to diagnose sleep issues on your device: scripts/amd_s2idle.py · master · drm / amd · GitLab