2021-07-07
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() 2) ioctl(F2FS_IOC_SET_PIN_FILE) 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.rstHowever, once F2FS receives
ioctl(fd, F2FS_IOC_SET_PIN_FILE)
in prior tofallocate(fd, DEFAULT_MODE)
, it allocates on-disk blocks addresses having zero or random data, which is useful to the below scenario where:
create(fd)
ioctl(fd, F2FS_IOC_SET_PIN_FILE)
fallocate(fd, 0, 0, size)
address = fibmap(fd, offset)
open(blkdev)
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:
#!/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
(stderr, "len: %llu\n", len);
fprintf= fallocate(fd, 0, 0, len);
r if (r == -1) { fprintf(stderr, "Error at fallocate: %i\n", errno); return 1; }
= close(fd);
r 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!
Type theorist. Rolling my own crypto.
This page has a markdown version
Public PGP key (6B66 1F36 59D3 BAE7 0561 862E EA8E 9467 5140 F7F4)