bcachefs: BTREE_ID_logged_ops
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 27 Aug 2023 22:27:41 +0000 (18:27 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Sun, 22 Oct 2023 21:10:12 +0000 (17:10 -0400)
Add a new btree for long running logged operations - i.e. for logging
operations that we can't do within a single btree transaction, so that
they can be resumed if we crash.

Keys in the logged operations btree will represent operations in
progress, with the state of the operation stored in the value.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/Makefile
fs/bcachefs/bcachefs.h
fs/bcachefs/bcachefs_format.h
fs/bcachefs/btree_update.c
fs/bcachefs/btree_update.h
fs/bcachefs/logged_ops.c [new file with mode: 0644]
fs/bcachefs/logged_ops.h [new file with mode: 0644]
fs/bcachefs/recovery.c
fs/bcachefs/recovery_types.h
fs/bcachefs/super.c

index 9c00dabb26ac3e20454810be0458038f2ac4ddaa..0749731b9072aec5219b1d7724e1b7cd5c3cf04f 100644 (file)
@@ -55,6 +55,7 @@ bcachefs-y            :=      \
        journal_sb.o            \
        journal_seq_blacklist.o \
        keylist.o               \
+       logged_ops.o            \
        lru.o                   \
        mean_and_variance.o     \
        migrate.o               \
index 30b3d7b9f9dc14de467685e653a2624201a6cf5a..e80fef1537c9d40bcc82f491dbf9714216414c76 100644 (file)
@@ -454,6 +454,7 @@ enum gc_phase {
        GC_PHASE_BTREE_bucket_gens,
        GC_PHASE_BTREE_snapshot_trees,
        GC_PHASE_BTREE_deleted_inodes,
+       GC_PHASE_BTREE_logged_ops,
 
        GC_PHASE_PENDING_DELETE,
 };
index 1cce2504bca67b9842f83b639e95dd6b590878bf..31efa9e381cea379e771d1f179d44ac7b159ea6a 100644 (file)
@@ -2249,7 +2249,9 @@ enum btree_id_flags {
        x(snapshot_trees,       15,     0,                                      \
          BIT_ULL(KEY_TYPE_snapshot_tree))                                      \
        x(deleted_inodes,       16,     BTREE_ID_SNAPSHOTS,                     \
-         BIT_ULL(KEY_TYPE_set))
+         BIT_ULL(KEY_TYPE_set))                                                \
+       x(logged_ops,           17,     0,                                      \
+         0)
 
 enum btree_id {
 #define x(name, nr, ...) BTREE_ID_##name = nr,
index 606e7050a84adec40dbe38edc0212576f6d87969..823f0da2f502d29752cc0a1ee03b039c2ecdfbfc 100644 (file)
@@ -653,6 +653,7 @@ int bch2_btree_insert_nonextent(struct btree_trans *trans,
        int ret;
 
        bch2_trans_iter_init(trans, &iter, btree, k->k.p,
+                            BTREE_ITER_CACHED|
                             BTREE_ITER_NOT_EXTENTS|
                             BTREE_ITER_INTENT);
        ret   = bch2_btree_iter_traverse(&iter) ?:
@@ -727,6 +728,23 @@ int bch2_btree_delete_at_buffered(struct btree_trans *trans,
        return bch2_trans_update_buffered(trans, btree, k);
 }
 
+int bch2_btree_delete(struct btree_trans *trans,
+                     enum btree_id btree, struct bpos pos,
+                     unsigned update_flags)
+{
+       struct btree_iter iter;
+       int ret;
+
+       bch2_trans_iter_init(trans, &iter, btree, pos,
+                            BTREE_ITER_CACHED|
+                            BTREE_ITER_INTENT);
+       ret   = bch2_btree_iter_traverse(&iter) ?:
+               bch2_btree_delete_at(trans, &iter, update_flags);
+       bch2_trans_iter_exit(trans, &iter);
+
+       return ret;
+}
+
 int bch2_btree_delete_range_trans(struct btree_trans *trans, enum btree_id id,
                                  struct bpos start, struct bpos end,
                                  unsigned update_flags,
index 78a92a1cfb477a874ea935be99fbf358cfc278b4..0596c5e73a3e446c9fee9e21a60b44d3ee2834bb 100644 (file)
@@ -58,6 +58,7 @@ int bch2_btree_delete_extent_at(struct btree_trans *, struct btree_iter *,
                                unsigned, unsigned);
 int bch2_btree_delete_at(struct btree_trans *, struct btree_iter *, unsigned);
 int bch2_btree_delete_at_buffered(struct btree_trans *, enum btree_id, struct bpos);
+int bch2_btree_delete(struct btree_trans *, enum btree_id, struct bpos, unsigned);
 
 int bch2_btree_insert_nonextent(struct btree_trans *, enum btree_id,
                                struct bkey_i *, enum btree_update_flags);
diff --git a/fs/bcachefs/logged_ops.c b/fs/bcachefs/logged_ops.c
new file mode 100644 (file)
index 0000000..28a0e7b
--- /dev/null
@@ -0,0 +1,111 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "bcachefs.h"
+#include "bkey_buf.h"
+#include "btree_update.h"
+#include "error.h"
+#include "logged_ops.h"
+#include "super.h"
+
+struct bch_logged_op_fn {
+       u8              type;
+       int             (*resume)(struct btree_trans *, struct bkey_i *);
+};
+
+static const struct bch_logged_op_fn logged_op_fns[] = {
+#define x(n)           {                                       \
+       .type           = KEY_TYPE_logged_op_##n,               \
+       .resume         = bch2_resume_logged_op_##n,            \
+},
+       BCH_LOGGED_OPS()
+#undef x
+};
+
+static const struct bch_logged_op_fn *logged_op_fn(enum bch_bkey_type type)
+{
+       for (unsigned i = 0; i < ARRAY_SIZE(logged_op_fns); i++)
+               if (logged_op_fns[i].type == type)
+                       return logged_op_fns + i;
+       return NULL;
+}
+
+static int resume_logged_op(struct btree_trans *trans, struct btree_iter *iter,
+                           struct bkey_s_c k)
+{
+       struct bch_fs *c = trans->c;
+       const struct bch_logged_op_fn *fn = logged_op_fn(k.k->type);
+       struct bkey_buf sk;
+       u32 restart_count = trans->restart_count;
+       int ret;
+
+       if (!fn)
+               return 0;
+
+       bch2_bkey_buf_init(&sk);
+       bch2_bkey_buf_reassemble(&sk, c, k);
+
+       ret =   drop_locks_do(trans, (bch2_fs_lazy_rw(c), 0)) ?:
+               fn->resume(trans, sk.k) ?: trans_was_restarted(trans, restart_count);
+
+       bch2_bkey_buf_exit(&sk, c);
+       return ret;
+}
+
+int bch2_resume_logged_ops(struct bch_fs *c)
+{
+       struct btree_iter iter;
+       struct bkey_s_c k;
+       int ret;
+
+       ret = bch2_trans_run(c,
+               for_each_btree_key2(&trans, iter,
+                               BTREE_ID_logged_ops, POS_MIN, BTREE_ITER_PREFETCH, k,
+                       resume_logged_op(&trans, &iter, k)));
+       if (ret)
+               bch_err_fn(c, ret);
+       return ret;
+}
+
+static int __bch2_logged_op_start(struct btree_trans *trans, struct bkey_i *k)
+{
+       struct btree_iter iter;
+       int ret;
+
+       ret = bch2_bkey_get_empty_slot(trans, &iter, BTREE_ID_logged_ops, POS_MAX);
+       if (ret)
+               return ret;
+
+       k->k.p = iter.pos;
+
+       ret = bch2_trans_update(trans, &iter, k, 0);
+       bch2_trans_iter_exit(trans, &iter);
+       return ret;
+}
+
+int bch2_logged_op_start(struct btree_trans *trans, struct bkey_i *k)
+{
+       return commit_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL,
+                        __bch2_logged_op_start(trans, k));
+}
+
+void bch2_logged_op_finish(struct btree_trans *trans, struct bkey_i *k)
+{
+       int ret = commit_do(trans, NULL, NULL, BTREE_INSERT_NOFAIL,
+                           bch2_btree_delete(trans, BTREE_ID_logged_ops, k->k.p, 0));
+       /*
+        * This needs to be a fatal error because we've left an unfinished
+        * operation in the logged ops btree.
+        *
+        * We should only ever see an error here if the filesystem has already
+        * been shut down, but make sure of that here:
+        */
+       if (ret) {
+               struct bch_fs *c = trans->c;
+               struct printbuf buf = PRINTBUF;
+
+               bch2_bkey_val_to_text(&buf, c, bkey_i_to_s_c(k));
+               bch2_fs_fatal_error(c, "%s: error deleting logged operation %s: %s",
+                                    __func__, buf.buf, bch2_err_str(ret));
+               printbuf_exit(&buf);
+       }
+}
diff --git a/fs/bcachefs/logged_ops.h b/fs/bcachefs/logged_ops.h
new file mode 100644 (file)
index 0000000..9b75800
--- /dev/null
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _BCACHEFS_LOGGED_OPS_H
+#define _BCACHEFS_LOGGED_OPS_H
+
+#include "bkey.h"
+
+#define BCH_LOGGED_OPS()
+
+static inline int bch2_logged_op_update(struct btree_trans *trans, struct bkey_i *op)
+{
+       return bch2_btree_insert_nonextent(trans, BTREE_ID_logged_ops, op, 0);
+}
+
+int bch2_resume_logged_ops(struct bch_fs *);
+int bch2_logged_op_start(struct btree_trans *, struct bkey_i *);
+void bch2_logged_op_finish(struct btree_trans *, struct bkey_i *);
+
+#endif /* _BCACHEFS_LOGGED_OPS_H */
index 30efb3c9056009f930bbe0da6b9fde6cd0255c07..f5f6eea2cbaebd44b91ab0ac749c4febb95ef26c 100644 (file)
@@ -20,6 +20,7 @@
 #include "journal_reclaim.h"
 #include "journal_seq_blacklist.h"
 #include "lru.h"
+#include "logged_ops.h"
 #include "move.h"
 #include "quota.h"
 #include "recovery.h"
index abf1f834ec7a86434b42b3d46ba43fe5a7102242..fbfa9d831d6f2382647c77dbcd0c6c15e82976f7 100644 (file)
@@ -29,6 +29,7 @@
        x(check_subvols,                PASS_FSCK)                                              \
        x(delete_dead_snapshots,        PASS_FSCK|PASS_UNCLEAN)                                 \
        x(fs_upgrade_for_subvolumes,    0)                                                      \
+       x(resume_logged_ops,            PASS_ALWAYS)                                            \
        x(check_inodes,                 PASS_FSCK)                                              \
        x(check_extents,                PASS_FSCK)                                              \
        x(check_dirents,                PASS_FSCK)                                              \
index 55176023f15bb7bac971332f8b6bc0b4203d9f61..ef11cede1dba0a57c6eee319f683f40af801a08d 100644 (file)
@@ -791,6 +791,7 @@ static struct bch_fs *bch2_fs_alloc(struct bch_sb *sb, struct bch_opts opts)
        c->btree_key_cache_btrees |= 1U << BTREE_ID_alloc;
        if (c->opts.inodes_use_key_cache)
                c->btree_key_cache_btrees |= 1U << BTREE_ID_inodes;
+       c->btree_key_cache_btrees |= 1U << BTREE_ID_logged_ops;
 
        c->block_bits           = ilog2(block_sectors(c));
        c->btree_foreground_merge_threshold = BTREE_FOREGROUND_MERGE_THRESHOLD(c);