bcachefs: BCH_IOCTL_FSCK_ONLINE
authorKent Overstreet <kent.overstreet@linux.dev>
Mon, 4 Dec 2023 18:45:33 +0000 (13:45 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Mon, 1 Jan 2024 16:47:40 +0000 (11:47 -0500)
This adds a new ioctl for running fsck on a mounted, in use filesystem.

This reuses the fsck_thread code from the previous patch for running
fsck on an offline, unmounted filesystem, so that log messages for the
fsck thread are redirected to userspace.

Only one running fsck instance is allowed at a time; a new semaphore
(since the lock will be taken by one thread and released by another) is
added for this.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs.h
fs/bcachefs/bcachefs_ioctl.h
fs/bcachefs/chardev.c
fs/bcachefs/super.c

index 18bc1bb..b42e385 100644 (file)
@@ -1058,6 +1058,7 @@ struct bch_fs {
        /* bitmap of explicitly enabled recovery passes: */
        u64                     recovery_passes_explicit;
        u64                     recovery_passes_complete;
+       struct semaphore        online_fsck_mutex;
 
        /* DEBUG JUNK */
        struct dentry           *fs_debug_dir;
index 07c4908..21f81b1 100644 (file)
@@ -83,7 +83,8 @@ struct bch_ioctl_incremental {
 
 #define BCH_IOCTL_DEV_USAGE_V2 _IOWR(0xbc,     18, struct bch_ioctl_dev_usage_v2)
 
-#define BCH_IOCTL_FSCK_OFFLINE         _IOW(0xbc,      19,  struct bch_ioctl_fsck_offline)
+#define BCH_IOCTL_FSCK_OFFLINE _IOW(0xbc,      19,  struct bch_ioctl_fsck_offline)
+#define BCH_IOCTL_FSCK_ONLINE  _IOW(0xbc,      20,  struct bch_ioctl_fsck_online)
 
 /* ioctl below act on a particular file, not the filesystem as a whole: */
 
@@ -399,4 +400,13 @@ struct bch_ioctl_fsck_offline {
        __u64                   devs[0];
 };
 
+/*
+ * BCH_IOCTL_FSCK_ONLINE: run fsck from the 'bcachefs fsck' userspace command,
+ * but with the kernel's implementation of fsck:
+ */
+struct bch_ioctl_fsck_online {
+       __u64                   flags;
+       __u64                   opts;           /* string */
+};
+
 #endif /* _BCACHEFS_IOCTL_H */
index 78bcfa4..08922f7 100644 (file)
@@ -7,6 +7,7 @@
 #include "chardev.h"
 #include "journal.h"
 #include "move.h"
+#include "recovery.h"
 #include "replicas.h"
 #include "super.h"
 #include "super-io.h"
@@ -201,6 +202,7 @@ static long bch2_ioctl_incremental(struct bch_ioctl_incremental __user *user_arg
 struct fsck_thread {
        struct thread_with_file thr;
        struct printbuf         buf;
+       struct bch_fs           *c;
        char                    **devs;
        size_t                  nr_devs;
        struct bch_opts         opts;
@@ -350,6 +352,7 @@ static long bch2_ioctl_fsck_offline(struct bch_ioctl_fsck_offline __user *user_a
                goto err;
        }
 
+       thr->opts = bch2_opts_empty();
        thr->nr_devs = arg.nr_devs;
        thr->output.buf = PRINTBUF;
        thr->output.buf.atomic++;
@@ -929,6 +932,95 @@ static long bch2_ioctl_disk_resize_journal(struct bch_fs *c,
        return ret;
 }
 
+static int bch2_fsck_online_thread_fn(void *arg)
+{
+       struct fsck_thread *thr = container_of(arg, struct fsck_thread, thr);
+       struct bch_fs *c = thr->c;
+
+       c->output_filter = current;
+       c->output = &thr->output;
+
+       /*
+        * XXX: can we figure out a way to do this without mucking with c->opts?
+        */
+       if (opt_defined(thr->opts, fix_errors))
+               c->opts.fix_errors = thr->opts.fix_errors;
+       c->opts.fsck = true;
+
+       c->curr_recovery_pass = BCH_RECOVERY_PASS_check_alloc_info;
+       bch2_run_online_recovery_passes(c);
+
+       c->output = NULL;
+       c->output_filter = NULL;
+
+       thr->thr.done = true;
+       wake_up(&thr->output.wait);
+
+       up(&c->online_fsck_mutex);
+       bch2_ro_ref_put(c);
+       return 0;
+}
+
+static long bch2_ioctl_fsck_online(struct bch_fs *c,
+                                  struct bch_ioctl_fsck_online arg)
+{
+       struct fsck_thread *thr = NULL;
+       long ret = 0;
+
+       if (arg.flags)
+               return -EINVAL;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       if (!bch2_ro_ref_tryget(c))
+               return -EROFS;
+
+       if (down_trylock(&c->online_fsck_mutex)) {
+               bch2_ro_ref_put(c);
+               return -EAGAIN;
+       }
+
+       thr = kzalloc(sizeof(*thr), GFP_KERNEL);
+       if (!thr) {
+               ret = -ENOMEM;
+               goto err;
+       }
+
+       thr->c = c;
+       thr->opts = bch2_opts_empty();
+       thr->output.buf = PRINTBUF;
+       thr->output.buf.atomic++;
+       spin_lock_init(&thr->output.lock);
+       init_waitqueue_head(&thr->output.wait);
+       darray_init(&thr->output2);
+
+       if (arg.opts) {
+               char *optstr = strndup_user((char __user *)(unsigned long) arg.opts, 1 << 16);
+
+               ret =   PTR_ERR_OR_ZERO(optstr) ?:
+                       bch2_parse_mount_opts(c, &thr->opts, optstr);
+               kfree(optstr);
+
+               if (ret)
+                       goto err;
+       }
+
+       ret = run_thread_with_file(&thr->thr,
+                                  &fsck_thread_ops,
+                                  bch2_fsck_online_thread_fn,
+                                  "bch-fsck");
+err:
+       if (ret < 0) {
+               bch_err_fn(c, ret);
+               if (thr)
+                       bch2_fsck_thread_free(thr);
+               up(&c->online_fsck_mutex);
+               bch2_ro_ref_put(c);
+       }
+       return ret;
+}
+
 #define BCH_IOCTL(_name, _argtype)                                     \
 do {                                                                   \
        _argtype i;                                                     \
@@ -984,7 +1076,8 @@ long bch2_fs_ioctl(struct bch_fs *c, unsigned cmd, void __user *arg)
                BCH_IOCTL(disk_resize, struct bch_ioctl_disk_resize);
        case BCH_IOCTL_DISK_RESIZE_JOURNAL:
                BCH_IOCTL(disk_resize_journal, struct bch_ioctl_disk_resize_journal);
-
+       case BCH_IOCTL_FSCK_ONLINE:
+               BCH_IOCTL(fsck_online, struct bch_ioctl_fsck_online);
        default:
                return -ENOTTY;
        }
index 83e5423..c7c7d4a 100644 (file)
@@ -762,6 +762,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
 
        refcount_set(&c->ro_ref, 1);
        init_waitqueue_head(&c->ro_ref_wait);
+       sema_init(&c->online_fsck_mutex, 1);
 
        init_rwsem(&c->gc_lock);
        mutex_init(&c->gc_gens_lock);