aboutsummaryrefslogtreecommitdiff
path: root/fs/btrfs/shutdown
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mssola@mssola.com>2026-02-24 00:17:26 +0100
committerMiquel Sabaté Solà <mssola@mssola.com>2026-02-24 00:17:26 +0100
commita597bc04de8990dc66e9641c782d6a9fc80a5abe (patch)
tree75b3e4c47aabfc3a3d3c49db26f5010c911ba37e /fs/btrfs/shutdown
parent7a721c4374f192ba025b27f790cf14339ca2088e (diff)
downloadfarga-a597bc04de8990dc66e9641c782d6a9fc80a5abe.tar.gz
farga-a597bc04de8990dc66e9641c782d6a9fc80a5abe.zip
btrfs: add examples on the shutdown experimental feature
Signed-off-by: Miquel Sabaté Solà <mssola@mssola.com>
Diffstat (limited to 'fs/btrfs/shutdown')
-rw-r--r--fs/btrfs/shutdown/Makefile56
-rw-r--r--fs/btrfs/shutdown/README.md93
-rw-r--r--fs/btrfs/shutdown/fserror.c77
-rw-r--r--fs/btrfs/shutdown/shutdown.c68
4 files changed, 294 insertions, 0 deletions
diff --git a/fs/btrfs/shutdown/Makefile b/fs/btrfs/shutdown/Makefile
new file mode 100644
index 0000000..8871afd
--- /dev/null
+++ b/fs/btrfs/shutdown/Makefile
@@ -0,0 +1,56 @@
+##
+# By default everything is silent. If you want to change this behavior, simply
+# assign V=1 when calling make.
+
+V =
+ifeq ($(strip $(V)),)
+ E = @echo
+ Q = @
+else
+ E = @\#
+ Q =
+endif
+
+##
+# Compile options. You can use CROSS_COMPILE just like on the Linux Kernel.
+
+CC = $(CROSS_COMPILE)gcc
+LD = $(CROSS_COMPILE)ld
+CCFLAGS = -Werror -Wpedantic -Wall -Wextra -Wcast-qual -Winit-self \
+ -Wmissing-include-dirs -Wredundant-decls -Wshadow -Wsign-conversion \
+ -Wswitch-default -Wundef -Wunreachable-code -Wmissing-noreturn \
+ -D_FORTIFY_SOURCE=3 -fstrict-flex-arrays=3 -fsanitize=bounds-strict
+ # NOTE: last line: special flags for the examples :)
+LDFLAGS =
+
+
+# You can pass an optional `DEBUG` variable to manipulate the build type.
+DEBUG =
+ifeq ($(strip $(DEBUG)),)
+ CCFLAGS += -O3
+else
+ CCFLAGS += -g
+endif
+
+##
+# Paths.
+
+SRC = $(wildcard *.c)
+EXES = $(SRC:.c=)
+
+##
+# Targets
+
+.PHONY: all
+all: clean $(EXES)
+
+.PHONY: build
+build: $(EXES)
+
+.c:
+ $(E) " CC " $(*F)
+ $(Q) $(CC) $(CCFLAGS) $< -o $@
+
+.PHONY: clean
+clean:
+ $(Q) rm -f $(EXES)
diff --git a/fs/btrfs/shutdown/README.md b/fs/btrfs/shutdown/README.md
new file mode 100644
index 0000000..332eaf4
--- /dev/null
+++ b/fs/btrfs/shutdown/README.md
@@ -0,0 +1,93 @@
+This example contains a set of files which showcase how the shutdown ioctl works
+on BTRFS. This ioctl was introduced in Linux 6.19 and is marked as an
+experimental feature.
+
+BTRFS has been able to freeze the filesystem for a long time, which might sound
+similar to shutting it down. The main difference is that shutdown is meant for a
+complete, persistent power-off; while freezing can be undone, it's temporary. As
+it stands in Linux kernel 6.19, BTRFS shutdown comes in two flavors:
+
+1. _With_ log flushing (default): commits the current transaction, leaving the
+ file system into a consistent state, and shuts down the filesystem right
+ after freezing it. Then it's marked as thawed, but the shutdown state is
+ preserved. This is a bit of trickery to get all logs flushed, the filesystem
+ consistent, but with the proper flag set to shutdown.
+2. _Without_ log flushing: discards any current transaction to keep the
+ filesystem into a consistent state and shuts down the filesystem right
+ away. This is probably the fastest and most aggresive route.
+
+## Requirements
+
+In order to be able to run this example as intended you need:
+
+- Linux kernel 6.19+ with BTRFS experimental features enabled.
+- A Linux kernel built with the following patch applied: (["btrfs: report filesystem shutdown via
+ fserror"](https://lore.kernel.org/linux-btrfs/20260216002806.3831884-1-mssola@mssola.com)).
+
+## Listening for filesystem errors
+
+BTRFS will notify user-space whenever a shutdown happens. This way user-space
+can mark this device as not available and hence can avoid writing into it. The
+`fserror.c` file contains a small program that does just that. It receives a
+mount point as an argument, and then waits for events on that filesystem,
+filtering for the shutdown error. Run this program and then go onto the next
+section. Whenever the shutdown happens, the terminal from where you executed
+`fserror.c` will print the following:
+
+```
+Shutdown detected!
+```
+
+If you don't see it, either the kernel you have does not support BTRFS shutdown
+yet, or some requirements are missing.
+
+## Actually shutting down
+
+The `shutdown.c` file contains a small program which accepts a mount point as an
+argument. It then checks that it's a BTRFS mount point, and afterwards it calls
+the shutdown ioctl with the default flags (i.e. with log flushing). Note that it
+does not do any validation on the kernel you are using. Hence, if you are using
+a Linux kernel version which has no support for this (e.g. previous than 6.19 or
+6.19 without BTRFS experimental features), you will get the following message:
+
+```
+Shutdown failed: Inappropriate ioctl for device
+```
+
+This is the `ETTY` errno message, which just means that the ioctl support for
+this filesystem does not implement this function. This will also happen in
+virtualized environments, as the virtualized filesystem (e.g. overlayfs) will be
+on the receiving end of this ioctl and, hence, they will return the same errno.
+
+But if you are running on a Linux kernel which does support this, then running
+this program will give you the following message:
+
+```
+Success: filesystem shut down.
+```
+
+Then you will see the following message on `dmesg`:
+
+```
+[ 201.048599][ T995] BTRFS critical (device sda state E): emergency shutdown
+```
+
+From now on you will receive `EROFS` errors whenever you try to write into this
+device. For example:
+
+```
+# echo "hello" >> mnt/lala.txt
+bash: mnt/lala.txt: Read-only file system
+```
+
+## Patches sent to the Linux kernel as a result
+
+As a result of looking into this I have sent two patches to the Linux kernel:
+
+1. ["btrfs: report filesystem shutdown via
+ fserror"](https://lore.kernel.org/linux-btrfs/20260216002806.3831884-1-mssola@mssola.com),
+ which adds the 'fanotify' event for filesystem shutdown in BTRFS.
+2. ["btrfs: don't commit the super block when unmounting a shutdown
+ filesystem"](https://lore.kernel.org/linux-btrfs/20260216002252.3831277-1-mssola@mssola.com),
+ which prevents BTRFS from trying to commit the super block on filesystem
+ unmount after a filesystem was shutdown.
diff --git a/fs/btrfs/shutdown/fserror.c b/fs/btrfs/shutdown/fserror.c
new file mode 100644
index 0000000..f06521c
--- /dev/null
+++ b/fs/btrfs/shutdown/fserror.c
@@ -0,0 +1,77 @@
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/fanotify.h>
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ char buf[4096];
+ ssize_t len;
+ struct fanotify_event_metadata *metadata;
+ struct fanotify_event_info_error *error_info;
+
+ if (argc < 2) {
+ fprintf(stderr, "Usage: %s <mount point>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Initialize 'fanotify' with the default value and `FAN_REPORT_FID`, which
+ * will allow use to filter by `FAN_FS_ERROR` down the line.
+ */
+ fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_FID, O_RDONLY);
+ if (fd < 0) {
+ perror("fanotify_init");
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Add a 'fanotify' mark to monitor all files from the filesystem pointed by
+ * 'fd'. Note that this general monitoring via 'FAN_MARK_FILESYSTEM' is the
+ * thing that makes this program to need root permission as documented in
+ * the manpage: "Use of this flag requires the CAP_SYS_ADMIN capability".
+ */
+ if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_FILESYSTEM,
+ FAN_FS_ERROR, AT_FDCWD, argv[1]) < 0) {
+ perror("fanotify_mark");
+ exit(EXIT_FAILURE);
+ }
+
+ printf("Listening for filesystem errors on '%s'\n", argv[1]);
+
+ // Event Loop
+ while ((len = read(fd, buf, sizeof(buf))) > 0) {
+ metadata = (struct fanotify_event_metadata *)buf;
+
+ while (FAN_EVENT_OK(metadata, len)) {
+ /*
+ * We only care about filesystem errors, which is the only thing we
+ * passed to 'fanotify_mark'. That being said, just to be sure,
+ * let's filter this out.
+ */
+ if (metadata->mask & FAN_FS_ERROR) {
+ // fancy...
+ error_info = (struct fanotify_event_info_error *)
+ ((char *)metadata + metadata->event_len - sizeof(*error_info));
+
+ if (error_info->error == ESHUTDOWN) {
+ printf("Shutdown detected!\n");
+ goto done;
+ } else {
+ printf("Filesystem error detected with code: %d\n", error_info->error);
+ }
+ }
+
+ metadata = FAN_EVENT_NEXT(metadata, len);
+ }
+ }
+
+ done:
+ close(fd);
+ return 0;
+}
diff --git a/fs/btrfs/shutdown/shutdown.c b/fs/btrfs/shutdown/shutdown.c
new file mode 100644
index 0000000..46a5731
--- /dev/null
+++ b/fs/btrfs/shutdown/shutdown.c
@@ -0,0 +1,68 @@
+#define _GNU_SOURCE
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <linux/btrfs.h>
+#include <linux/magic.h>
+#include <linux/types.h>
+#include <sys/ioctl.h>
+#include <sys/statfs.h>
+
+/*
+ * Constants defined in BTRFS' experimental shutdown support
+ * starting from Linux 6.19.
+ */
+
+#ifndef BTRFS_SHUTDOWN_FLAGS_DEFAULT
+#define BTRFS_SHUTDOWN_FLAGS_DEFAULT 0x0
+#endif
+
+#ifndef BTRFS_IOC_SHUTDOWN
+#define BTRFS_IOC_SHUTDOWN _IOR('X', 125, __u32)
+#endif
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ __u32 flags;
+ struct statfs info;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s <mount point>\n", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+
+ if (statfs(argv[1], &info) == 0) {
+ /*
+ * We could certainly use this ioctl against EXT4 or XFS, but that's not
+ * the point we want to illustrate here.
+ */
+ if (info.f_type != BTRFS_SUPER_MAGIC) {
+ printf("error: you are supposed to be running this against BTRFS;"
+ " %lx magic detected instead\n", info.f_type);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ perror("statfs failed");
+ exit(EXIT_FAILURE);
+ }
+
+ fd = open(argv[1], O_RDONLY | O_DIRECTORY);
+ if (fd < 0) {
+ perror("open");
+ exit(EXIT_FAILURE);
+ }
+
+ flags = BTRFS_SHUTDOWN_FLAGS_DEFAULT;
+ if (ioctl(fd, BTRFS_IOC_SHUTDOWN, &flags) == 0) {
+ printf("Success: filesystem shut down.\n");
+ } else {
+ perror("Shutdown failed");
+ exit(EXIT_FAILURE);
+ }
+
+ close(fd);
+ return 0;
+}