F2FS swap files broken and the arcane ritual to fix them

Las Safin


Jump to the fix

A few days ago, after updating nixpkgs on my ODROID N2 to a reivision with Linux 5.13, it began mysteriously crashing. Eventually I figured out that it happens when it starts swapping to the swap file, and I noticed I even get an error when I enable the swap file:

    [...] F2FS-fs (dm-0): Swapfile does not align to section

What the fuck does this mean? - I thought then.

I began searching the internet, but unfortunately, very little comes up, except some mails from lkml.org:

In the second link, something that seems like a solution is described:

Subject: Re: [PATCH v2] f2fs: avoid swapon failure by giving a warning first … The final solution can be migrating blocks to form a section-aligned file internally. Meanwhile, let’s ask users to do that when preparing the swap file initially like:

1) create()
3) fallocate()

The message isn’t very understandable, but thankfully we have a clue: F2FS_IOC_SET_PIN_FILE. Looking at the source code for F2FS, and finding this, we finally have a real explanation of what it does:

Yes, this is persistent. F2FS_IOC_SET_PIN_FILE ioctl is to prevent file data from moving and being garbage collected, and further update to the file will be handled in in-place update manner. I don’t see any document on this, but you can find the below in Documentation/filesystems/f2fs.rst

However, once F2FS receives ioctl(fd, F2FS_IOC_SET_PIN_FILE) in prior to fallocate(fd, DEFAULT_MODE), it allocates on-disk blocks addresses having zero or random data, which is useful to the below scenario where:

  1. create(fd)
  2. ioctl(fd, F2FS_IOC_SET_PIN_FILE)
  3. fallocate(fd, 0, 0, size)
  4. address = fibmap(fd, offset)
  5. open(blkdev)
  6. write(blkdev, address)

F2FS is a log-structured file system, which means that writes are generally written sequentially in a log-like structure. When the kernel uses the file as swap, it updates it in-place. By marking the file as pinned in F2FS, F2FS will make sure that it’s updated in place. This doesn’t explain why it worked fine before, but let’s try marking our swap file with this ioctl:

The fix

#!/usr/bin/env -S tcc -run

#define _GNU_SOURCE

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/f2fs.h>
#include <stdint.h>

int main() {
    int fd = creat("./theswapfile", S_IRUSR | S_IWUSR);
    uint32_t pin = 1;
    if (fd == -1) { fprintf(stderr, "Error at creat: %i\n", errno); return 1; }
    int r = ioctl(fd, F2FS_IOC_SET_PIN_FILE, &pin);
    if (r == -1) { fprintf(stderr, "Error at ioctl: %i\n", errno); return 1; }
    uint64_t len = 8LLU * 1024LLU * 1024LLU * 1024LLU; // 8 GiB
    fprintf(stderr, "len: %llu\n", len);
    r = fallocate(fd, 0, 0, len);
    if (r == -1) { fprintf(stderr, "Error at fallocate: %i\n", errno); return 1; }
    r = close(fd);
    if (r == -1) { fprintf(stderr, "Error at close: %i\n", errno); return 1; }
    return 0;

Success! The uint32_t pin parameter for F2FS_IOC_SET_PIN_FILE seems to act as a boolean, judging from this. When 0, it seems to set it to unpinned, and otherwise it seems to set it to pinned.

Unfortunately for me though, my machine still crashes occasionally, but it doesn’t seem to be related to swap anymore at least!

About me

Type theorist. Rolling my own crypto.


This page has a markdown version

Atom Feed

Public PGP key (6B66 1F36 59D3 BAE7 0561 862E EA8E 9467 5140 F7F4)