[HowTo] Enable and configure hibernation with BTRFS

Ah well thanks, you are right. A string is not an array. I guess arrays are now default on new installations. I fixed it.

1 Like

Thanks, @megavolt, for the guide, as this helped me get started.

Here are a few observations, which are most likely relevant in time, i.e., 6.1.12-1-MANJARO and systemd 252 (252.5-1-manjaro).

  1. Steps to create swap file on BTRFS work flawlessly
  2. echo -e "GRUB_CMDLINE_LINUX_DEFAULT+=\" resume=UUID=$swp_uuid resume_offset=$swp_offset \"" | sudo tee -a /etc/default/grub appends the changes to the end of /etc/default/grub
  3. echo -e "HOOKS+=( resume )" | sudo tee -a /etc/mkinitcpio.conf appends the changes to the end of /etc/.conf

@zilexa, the additional changes you allude to are mostly unnecessary now. Except for HandlePowerKey=hibernate, the other settings are default values, and compiled into the kernel. Likewise, the symbolic link breaks hibernation. Indeed, it is my understanding that systemd 252 introduced some changes to power management*, and this will change again in 253.

The only addition I made was to add the Hibernate Status Button extension to GNOME.

In summary, systemctl suspend, closing the lid, or using the updated power menu will enter deep sleep. systemctl hibernate suspends to disk immediately, as does the hibernate menu option.

Finally, systemctl suspend-then-hibernate will write data to disk and NVM, and put the laptop to sleep. I also observe that with very low battery, GNOME will initiate suspend, but this action implements suspend-then-hibernate. I’ve not had the patience or time to fully test, but I understand that a timer (defined by HibernateDelaySec=120min) will check power, and then hibernate.

If power is lost beforehand, the state is already saved to disk. This seems to work when I initiate shutdown using the power button.

I trust this information is useful.

*systemd-sleep.conf

Be careful, disabling CoW for Swapfile will be broken by running Btrfs balance.

I have already tested Swapfile on Btrfs with NoCOW in VM a few days ago, there are some issues:

  • The physical offset of Swapfile is changed after Btrfs balance:

Before balance:

$ sudo filefrag -v /swap/swapfile
Filesystem type is: 9123683e
File size of /swap/swapfile is 2147483648 (524288 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:    6241536..   6241536:      1:            
   1:        1..  262143:    6241537..   6503679: 262143:             unwritten
   2:   262144..  524287:    5979392..   6241535: 262144:    6503680: last,unwritten,eof
/swap/swapfile: 2 extents found

After balance:

$ sudo filefrag -v /swap/swapfile
Filesystem type is: 9123683e
File size of /swap/swapfile is 2147483648 (524288 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:    9338112..   9338112:      1:            
   1:        1..  524287:    9338113..   9862399: 524287:             last,unwritten,eof
/swap/swapfile: 1 extent found

See physical_offset of Swapfile does not match resume_offset for hibernation in kernel parameter.

  • Try to enable swapon but it fails:
$ sudo swapon /swap/swapfile
swapon: /swap/swapfile: swapon failed: Invalid argument

Because I got a warning dmesg-message by the automatic notification popup (btrfs-desktop-notification-git):

kernel: BTRFS warning (device dm-0): swapfile must not be copy-on-write

It seems that the swapfile is still running in CoW after disabling CoW and running balance.

The current kernels 5.15, 6.1 and 6.2 are also affected.

I’ve read some messages from Reddit, some Btrfs users don’t recommend using NoCOW on Btrfs.

@tristanw thanks! I have to look into this because my laptop is no longer hibernating since a few updates ago. So something in my original steps combined with the last few updates caused this.

@Zesko Does this mean we simply cannot use hibernation with BTRFS anymore? That would be weird. Or simply do not use Balance on the drive that contains the hibernation file? I also understand balance should not be needed by default unless you have a very clear reason to do it.

You can continue to use the hibernation, but do not use Balance, otherwise Balance breaks the swapfile as NoCOW.

Personally, I always use Swap partition. Btrfs lets you shrink Btrfs partition and you can create a new partition for Swap or EXT4 with Swapfile.

Ok, you mean the nodatacow option? I just added it, just to be sure. Normally setting

sudo chattr +C /swap/swapfile

is quite enough. The file will not be compressed and not COW, so that file should be excluded when balance is running. Actually, I balance my SSD/NVME sometimes and didn’t see your problem. Hibernation works as expected.

Did you both? Set +C on the file and added nodatacow to options fsttab? Were your observations made with these assumptions?

Yeah I know, it is kinda ugly to add it at the end of the file, but is functional, since it expands the variable. My idea was to create easy steps and avoid being very complicated.

I already added nodatacow for the subvolume @swap in fstab and created a swapfile with the commandline:

$ sudo btrfs filesystem mkswapfile /swap/swapfile

See the Btrfs document: Swapfile — BTRFS documentation

If a btrfs balance changes the offset and therefore hibernation, then I would assume that there is a bug when calling this mkswapfile function. I did it recently again on a Mini-PC from HP like explained on top and a balance does not lead to malfunctions.

Possible that the attribute +C was set after creating the file and filling the file with dd, then there will be COW extents and therefore changes will occur on btrfs balance.

I tried to use chattr +C swapfile in VM today, the problem also appeared when swapoff first, then balance, then swapon, it fails.

It has nothing to do with the activation of then swapfile. Look, to make more understandable:

Don’t do:

truncate -s 0 swapfile
fallocate -l 10G swapfile
chattr +C swapfile
touch swapfile
dd if=/dev/zero of=/swapfile bs=1M count=10000 status=progress
chattr +C swapfile

But do:

truncate -s 0 swapfile
chattr +C swapfile
fallocate -l 10G swapfile
touch swapfile
chattr +C swapfile
dd if=/dev/zero of=/swapfile bs=1M count=10000 status=progress

Gotcha? That was also one of my traps at the beginning.

[root@test-btrfs /]# mkdir swap
[root@test-btrfs /]# mount /dev/sda2 swap/ -o subvol=/@swap,nodatacow,noatime
[root@test-btrfs /]# truncate -s 0 swap/swapfile
[root@test-btrfs /]# chattr +C swap/swapfile 
[root@test-btrfs /]# fallocate -l 2G swap/swapfile 
[root@test-btrfs /]# chmod 600 swap/swapfile 
[root@test-btrfs /]# swapon /swaps/swapfile
swapon: cannot open /swaps/swapfile: No such file or directory
[root@test-btrfs /]# mkswap swap/swapfile 
Setting up swapspace version 1, size = 2 GiB (2147479552 bytes)
[root@test-btrfs /]# cd swap
[root@test-btrfs swap]# swapon swapfile
[root@test-btrfs swap]# btrfs balance start / --full-balance
[root@test-btrfs swap]# swapoff swapfile
[root@test-btrfs swap]# swapon swapfile
swapon: /swap/swapfile: swapon failed: Invalid argument

1 Like

Alright, tested it on my system and get the same result. So --full-balance also moves files regardless of the attribute, which are marked as nodatacow +C and yeah it remains as marked, but does not work.

Very interesting. Also noted in the manual:

       start [options] <path>
              start  the  balance  operation according to the specified filters, without any filters the data and metadata from the whole filesystem are
              moved. The process runs in the foreground.

              NOTE:
                 The balance command without filters will basically move everything in the filesystem to a new physical location  on  devices  (i.e.  it
                 does not affect the logical properties of file extents like offsets within files and extent sharing).  The run time is potentially very
                 long, depending on the filesystem size. To prevent starting a full balance by accident, the user is warned and has  a  few  seconds  to
                 cancel the operation before it starts.  The warning and delay can be skipped with --full-balance option.

              Please  note  that  the  filters must be written together with the -d, -m and -s options, because they're optional and bare -d and -m also
              work and mean no filters.

              NOTE:
                 When the target profile for conversion filter is raid5 or raid6, there's a safety timeout of 10 seconds to warn users about the  status
                 of the feature

I use normally: btrfs balance start -dusage=90 / and that has zero effect on the swapfile.

I conclude: Never use a full-balance on a machine, since it will ignore +C Attributes.

Note: should be added as a note in the wiki.

That’s interesting, btrfs-assistant GUI uses --full-balance to break swapfile, that is why other Btrfs users got this issue.

1 Like

@megavolt

Btrfs developer David wrote:

Btrfs balance with the option “–full-balance” essentially breaks all NoCOW and Swapfile.

That’s not true, balance does not break NOCOW (defrag does) and the swapfile extents must be NOCOW but are skipped during balance.

Use “btrfs balance -dusage=90 /” that respects the NoCOW and Swapfile with +C attribute.

That’s not true either but maybe as a side efect of the 90% filter the balance does not reach to the swapfile.

You’ve closed this as documented, if you actually written the above to any documentation please remove it ASAP before this incorrect information spreads.

There’s probably a bug that you found with swapon/swapoff/balance/swapon that should be fixed.

You can fix the wrong info of “Tips & Tricks” if you trust what the Btrfs developer said.

The source: https://bugzilla.kernel.org/show_bug.cgi?id=217066

@Zesko Thank you! I corrected it based on the bug report. Is it ok that way? If not, please correct it yourself. Thanks.

:footprints:

I’ve tried to follow this guide again now on my new laptop, after 2 years I’ve last used it.

Sadly, in the last set of commands, mkinitcpio -P throws this error:

==> ERROR: Hook 'base udev autodetect modconf block keyboard keymap consolefont plymouth encrypt filesystems' cannot be found

I don’t understand where he get’s that hook list from and why he interprets it as a single hook instead of a list. In my /etc/mkinitcpio.conf all lines setting HOOKS are commented out, only the newly added line

HOOKS+=( resume )

is active. Is that a problem? The /etc/mkinitcpio.conf.d/ directory is empty. Do I need to un-comment one of the sample lines?

UPDATE: Ah, I just didn’t see the active HOOKS= line on my first go. It doesn’t use that array syntax, no brackets. Removing the brackets from the added line to instead now read:

HOOKS+=" resume "

made it work.

@megavolt have you considered using btrfs inspect-internal map-swapfile instead of bmp c? Seems like much easier way to get the offset info?

@zilexa Did you test it? Does it work?

Due this problem:

I switched to a swap partition. Hope it will be fixed soon.

1 Like

Thanks this saves me some time :slight_smile: