]> www.infradead.org Git - users/hch/block.git/commitdiff
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 18bc1bbef918896145623a2fd8af2c228693086a..b42e3854c51f77a899816106f5a07f4fd025b8e9 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 07c490851742bf611497fe115d3202f3bff44dda..21f81b16f24eaa13b723f228cd62731783881b42 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 78bcfa4cb48f088a11a5fda0578b8e7481962b3a..08922f7e380a9920fc1377ee1fd1e53dcaadb058 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 83e5423fd00597c4e4dcf3050375206f6e16c118..c7c7d4a11eb9602ff4d52f98b33ea68a732e9b2d 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);