[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Using a swapfile on btrfs for hibernation

From: Brice Waegeneire
Subject: Using a swapfile on btrfs for hibernation
Date: Sun, 22 Aug 2021 22:36:55 +0200

Hello Guix!

Here is a feedback on enabling hibernation using a swapfile stored on btrfs
filesystem. It wasn 't straight forward as it's not documented and I had to
read the code do make it work.

Note that even tho swpafile on btrfs filesystem are supported, therea are
some limitations⁵:
- the filesystem must be an a single device, ie. no btrfs RAID,
- the swapfile shouldn't be copy-on-write,
- the swapfile shouldn't be compressed.

Let's start with the btrfs specific part first. The following instructions
was compiled from tutorials¹ ² ³ for other Linux distributions. First we
gather the tools we'll need for the job, especially btrfs_map_physical⁴to
find the offset from the start of the btrfs partition where the swapfile is
located, this tool isn't packaged (I don't know if it should) so we'll
build it in the old fashion way.

--8<---------------cut here---------------start------------->8---
guix environment --ad-hoc gcc-toolchain curl gawk e2fsprogs btrfs-progs
curl -OL
gcc -O2 -o ./btrfs_map_physical ./btrfs_map_physical.c
--8<---------------cut here---------------end--------------->8---

Next we create our swapfile in its own subvolume named “swap” on the btrfs
filesystem of device “/dev/sda2” (mounted at “/mnt/rootfs”) and we make
sure our new swapfile isn't COWable nor compressed. To be able to hibernate
in all cironstancese the swapfile need to be at least as big as the total
RAM available.

--8<---------------cut here---------------start------------->8---
cd "$path"
btrfs subvolume create swap
mount -o subvol=swap /dev/sda2 ./swap
truncate -s 0 "$swapfile"
chattr +C "$swapfile"
btrfs property set "$swapfile" compression none
swap_size="$(grep MemTotal /proc/meminfo | awk '{print $2 * 1024}')"
fallocate --length "$swap_size" "$swapfile"
chmod 600 "$swapfile"
mkswap "$swapfile"
--8<---------------cut here---------------end--------------->8---

As we have a working swapfile, we'll genereate the kernel arguments to pass
to the kernel. The less obvious being the offset of the swapfile from the
start of the filesystem, to do so we are using the previously compiled
“btrfs_map_physical” binary and few shell utilities.

--8<---------------cut here---------------start------------->8---
swapon "$swapfile"
swap_physical_offset=$(btrfs_map_physical "$swapfile" | sed -n "2p" | awk 
"{print \$NF}")
swap_offset=$(echo "${swap_physical_offset} / $(getconf PAGESIZE)" | bc)
swap_uuid=$(findmnt -no UUID -T "$swapfile")
resume_args="resume=${swap_uuid} resume_offset=${swap_offset}"
echo "${resume_args}"
--8<---------------cut here---------------end--------------->8---

What's left is just to tweak our “operating-system” to make use of ourr new

--8<---------------cut here---------------start------------->8---
    (cons* ...
            (mount-point "/")
             (uuid "12345678-1234-1234-1234-123456789abc" 'btrfs))
            (type "btrfs")
            (options "compress=zstd,subvol=guix-system"))
            (mount-point "/swap")
             (uuid "12345678-1234-1234-1234-123456789abc" 'btrfs))
            (type "btrfs")
            ;; Needed to access the swapfile? (not sure)
            (needed-for-boot? #t)
            (options "subvol=swap"))

    (swap-devices (list "/swap/swapfile"))

     (cons* "resume="12345678-1234-1234-1234-123456789abc"
--8<---------------cut here---------------end--------------->8---

Now we can reconfigure our system and reboot it. At that point we are able
to use “loginctl hibernate” or “loginctl hybrid-sleep” to save our current
system state to the swapfile and recover it at the next reboot.

As a keen reader may have noticed, enabling hibernation in Guix in this
case has a quirk.  The most the most inconvenient is the Guix's “resume”
kernel argument format being different from Linux's⁶ « {/dev/<dev> |
PARTUUID=<uuid> | <int>:<int> | <hex>} » and other distributions allowing «
UUID=<uuid> »; in Guix the uuid isn't prefixed with “UUID=” or “PARTUUID=”
and this isn't documented anywhere, you are forced to read the code.

Also, having swap related configuration spread between “swap-devices” and
“kernel-arguments” fields make me think having specific records for that
first field would make sense. And that would make the “resume“ format issue
irrelevant. WDYT about adding the following records to be used in
“swap-devices” record in addition to the current types?

--8<---------------cut here---------------start------------->8---
 (file "/swap/swapfile")
 (offset 16400)
 (device (uuid "12345678-1234-1234-1234-123456789abc"))
 (resume? #t)   ; default #f
 (priority 21)) ; swapflags argument passed to swapon(2), default 0

 (device "/dev/sda3") ; any of the usual types allowed in 'swap-devices'
 (resume? #t)   ; default #f
 (priority 42)) ; swapflags argument passed to swapon(2), default 0
--8<---------------cut here---------------end--------------->8---

I wasn't sure were this documentation should go in Guix, except for the
btrfs instrructions which will probably end up in the cookbook.


- Brice

reply via email to

[Prev in Thread] Current Thread [Next in Thread]