]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
bcachefs: BTREE_ID_subvolume_children
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 21 Jan 2024 11:00:07 +0000 (06:00 -0500)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 14 Mar 2024 01:22:24 +0000 (21:22 -0400)
Add a btree to record a parent -> child subvolume relationships,
according to the filesystem heirarchy.

The subvolume_children btree is a bitset btree: if a bit is set at pos
p, that means p.offset is a child of subvolume p.inode.

This will be used for efficiently listing subvolumes, as well as
recursive deletion.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs.h
fs/bcachefs/bcachefs_format.h
fs/bcachefs/btree_types.h
fs/bcachefs/recovery_types.h
fs/bcachefs/sb-downgrade.c
fs/bcachefs/subvolume.c
fs/bcachefs/subvolume.h

index 69d0d60d50e366edf9e56ba101dda047536f5338..1391c530d8d2bd8ebc2ac41113ded46a95e4f17d 100644 (file)
@@ -504,6 +504,7 @@ enum gc_phase {
        GC_PHASE_BTREE_deleted_inodes,
        GC_PHASE_BTREE_logged_ops,
        GC_PHASE_BTREE_rebalance_work,
+       GC_PHASE_BTREE_subvolume_children,
 
        GC_PHASE_PENDING_DELETE,
 };
index 772eff5555f716da716ca40d76e71675c9d9128f..1bb24aa7352800a9660c513c028a865055e61ab0 100644 (file)
@@ -841,7 +841,8 @@ struct bch_sb_field_downgrade {
        x(deleted_inodes,               BCH_VERSION(1,  2))             \
        x(rebalance_work,               BCH_VERSION(1,  3))             \
        x(member_seq,                   BCH_VERSION(1,  4))             \
-       x(subvolume_fs_parent,          BCH_VERSION(1,  5))
+       x(subvolume_fs_parent,          BCH_VERSION(1,  5))             \
+       x(btree_subvolume_children,     BCH_VERSION(1,  6))
 
 enum bcachefs_metadata_version {
        bcachefs_metadata_version_min = 9,
@@ -1489,7 +1490,9 @@ enum btree_id_flags {
          BIT_ULL(KEY_TYPE_logged_op_truncate)|                                 \
          BIT_ULL(KEY_TYPE_logged_op_finsert))                                  \
        x(rebalance_work,       18,     BTREE_ID_SNAPSHOT_FIELD,                \
-         BIT_ULL(KEY_TYPE_set)|BIT_ULL(KEY_TYPE_cookie))
+         BIT_ULL(KEY_TYPE_set)|BIT_ULL(KEY_TYPE_cookie))                       \
+       x(subvolume_children,   19,     0,                                      \
+         BIT_ULL(KEY_TYPE_set))
 
 enum btree_id {
 #define x(name, nr, ...) BTREE_ID_##name = nr,
index 94f996ef5d2b9ce1840d49dbf78ffd697a742345..d4f4e52ee6ede2ce119eecc549a0485efdec38c6 100644 (file)
@@ -654,6 +654,7 @@ const char *bch2_btree_node_type_str(enum btree_node_type);
         BIT_ULL(BKEY_TYPE_inodes)|                     \
         BIT_ULL(BKEY_TYPE_stripes)|                    \
         BIT_ULL(BKEY_TYPE_reflink)|                    \
+        BIT_ULL(BKEY_TYPE_subvolumes)|                 \
         BIT_ULL(BKEY_TYPE_btree))
 
 #define BTREE_NODE_TYPE_HAS_ATOMIC_TRIGGERS            \
index fa0c8efd2a1b42450535474079b791aa2e6e9938..f0fc1dbb7239296af6712bb0ecc6cc666cf9d36b 100644 (file)
@@ -34,6 +34,7 @@
        x(check_snapshot_trees,                 18, PASS_ONLINE|PASS_FSCK)      \
        x(check_snapshots,                      19, PASS_ONLINE|PASS_FSCK)      \
        x(check_subvols,                        20, PASS_ONLINE|PASS_FSCK)      \
+       x(check_subvol_children,                35, PASS_ONLINE|PASS_FSCK)      \
        x(delete_dead_snapshots,                21, PASS_ONLINE|PASS_FSCK)      \
        x(fs_upgrade_for_subvolumes,            22, 0)                          \
        x(resume_logged_ops,                    23, PASS_ALWAYS)                \
index 12ee83e9e2518ebeacd44c49496a2de1a92be91e..49bc4951572c81bf2943efebc75a145606c3b9ca 100644 (file)
          BIT_ULL(BCH_RECOVERY_PASS_set_fs_needs_rebalance))    \
        x(subvolume_fs_parent,                                  \
          BIT_ULL(BCH_RECOVERY_PASS_check_dirents),             \
-         BCH_FSCK_ERR_subvol_fs_path_parent_wrong)
+         BCH_FSCK_ERR_subvol_fs_path_parent_wrong)             \
+       x(btree_subvolume_children,                             \
+         BIT_ULL(BCH_RECOVERY_PASS_check_subvols),             \
+         BCH_FSCK_ERR_subvol_children_not_set)
 
 #define DOWNGRADE_TABLE()
 
index d365e84367a3d8c675acd05821961dd3189320f6..68be3a450ca12f2ebdd4e7f152a6a85e1758e482 100644 (file)
 
 static int bch2_subvolume_delete(struct btree_trans *, u32);
 
+static struct bpos subvolume_children_pos(struct bkey_s_c k)
+{
+       if (k.k->type != KEY_TYPE_subvolume)
+               return POS_MIN;
+
+       struct bkey_s_c_subvolume s = bkey_s_c_to_subvolume(k);
+       if (!s.v->fs_path_parent)
+               return POS_MIN;
+       return POS(le32_to_cpu(s.v->fs_path_parent), s.k->p.offset);
+}
+
 static int check_subvol(struct btree_trans *trans,
                        struct btree_iter *iter,
                        struct bkey_s_c k)
 {
        struct bch_fs *c = trans->c;
        struct bkey_s_c_subvolume subvol;
+       struct btree_iter subvol_children_iter = {};
        struct bch_snapshot snapshot;
        struct printbuf buf = PRINTBUF;
        unsigned snapid;
@@ -57,6 +69,28 @@ static int check_subvol(struct btree_trans *trans,
                n->v.fs_path_parent = 0;
        }
 
+       if (subvol.v->fs_path_parent) {
+               struct bpos pos = subvolume_children_pos(k);
+
+               struct bkey_s_c subvol_children_k =
+                       bch2_bkey_get_iter(trans, &subvol_children_iter,
+                                          BTREE_ID_subvolume_children, pos, 0);
+               ret = bkey_err(subvol_children_k);
+               if (ret)
+                       goto err;
+
+               if (fsck_err_on(subvol_children_k.k->type != KEY_TYPE_set,
+                               c, subvol_children_not_set,
+                               "subvolume not set in subvolume_children btree at %llu:%llu\n%s",
+                               pos.inode, pos.offset,
+                               (printbuf_reset(&buf),
+                                bch2_bkey_val_to_text(&buf, c, k), buf.buf))) {
+                       ret = bch2_btree_bit_mod(trans, BTREE_ID_subvolume_children, pos, true);
+                       if (ret)
+                               goto err;
+               }
+       }
+
        struct bch_inode_unpacked inode;
        struct btree_iter inode_iter = {};
        ret = bch2_inode_peek_nowarn(trans, &inode_iter, &inode,
@@ -119,6 +153,7 @@ static int check_subvol(struct btree_trans *trans,
        }
 err:
 fsck_err:
+       bch2_trans_iter_exit(trans, &subvol_children_iter);
        printbuf_exit(&buf);
        return ret;
 }
@@ -134,6 +169,42 @@ int bch2_check_subvols(struct bch_fs *c)
        return ret;
 }
 
+static int check_subvol_child(struct btree_trans *trans,
+                             struct btree_iter *child_iter,
+                             struct bkey_s_c child_k)
+{
+       struct bch_fs *c = trans->c;
+       struct bch_subvolume s;
+       int ret = bch2_bkey_get_val_typed(trans, BTREE_ID_subvolumes, POS(0, child_k.k->p.offset),
+                                         0, subvolume, &s);
+       if (ret && !bch2_err_matches(ret, ENOENT))
+               return ret;
+
+       if (fsck_err_on(ret ||
+                       le32_to_cpu(s.fs_path_parent) != child_k.k->p.inode,
+                       c, subvol_children_bad,
+                       "incorrect entry in subvolume_children btree %llu:%llu",
+                       child_k.k->p.inode, child_k.k->p.offset)) {
+               ret = bch2_btree_delete_at(trans, child_iter, 0);
+               if (ret)
+                       goto err;
+       }
+err:
+fsck_err:
+       return ret;
+}
+
+int bch2_check_subvol_children(struct bch_fs *c)
+{
+       int ret = bch2_trans_run(c,
+               for_each_btree_key_commit(trans, iter,
+                               BTREE_ID_subvolume_children, POS_MIN, BTREE_ITER_PREFETCH, k,
+                               NULL, NULL, BCH_TRANS_COMMIT_no_enospc,
+                       check_subvol_child(trans, &iter, k)));
+       bch_err_fn(c, ret);
+       return 0;
+}
+
 /* Subvolumes: */
 
 int bch2_subvolume_invalid(struct bch_fs *c, struct bkey_s_c k,
@@ -164,6 +235,33 @@ void bch2_subvolume_to_text(struct printbuf *out, struct bch_fs *c,
        }
 }
 
+static int subvolume_children_mod(struct btree_trans *trans, struct bpos pos, bool set)
+{
+       return !bpos_eq(pos, POS_MIN)
+               ? bch2_btree_bit_mod(trans, BTREE_ID_subvolume_children, pos, set)
+               : 0;
+}
+
+int bch2_subvolume_trigger(struct btree_trans *trans,
+                          enum btree_id btree_id, unsigned level,
+                          struct bkey_s_c old, struct bkey_s new,
+                          unsigned flags)
+{
+       if (flags & BTREE_TRIGGER_TRANSACTIONAL) {
+               struct bpos children_pos_old = subvolume_children_pos(old);
+               struct bpos children_pos_new = subvolume_children_pos(new.s_c);
+
+               if (!bpos_eq(children_pos_old, children_pos_new)) {
+                       int ret = subvolume_children_mod(trans, children_pos_old, false) ?:
+                                 subvolume_children_mod(trans, children_pos_new, true);
+                       if (ret)
+                               return ret;
+               }
+       }
+
+       return 0;
+}
+
 static __always_inline int
 bch2_subvolume_get_inlined(struct btree_trans *trans, unsigned subvol,
                           bool inconsistent_if_not_found,
index a2b0dc1303c2551cd98f31e7af52ad9af3dc1463..f0979ab56a47d06e6ac9aa58e8b8b2bf2a9d9e5e 100644 (file)
@@ -8,14 +8,18 @@
 enum bkey_invalid_flags;
 
 int bch2_check_subvols(struct bch_fs *);
+int bch2_check_subvol_children(struct bch_fs *);
 
 int bch2_subvolume_invalid(struct bch_fs *, struct bkey_s_c,
                           enum bkey_invalid_flags, struct printbuf *);
 void bch2_subvolume_to_text(struct printbuf *, struct bch_fs *, struct bkey_s_c);
+int bch2_subvolume_trigger(struct btree_trans *, enum btree_id, unsigned,
+                          struct bkey_s_c, struct bkey_s, unsigned);
 
 #define bch2_bkey_ops_subvolume ((struct bkey_ops) {           \
        .key_invalid    = bch2_subvolume_invalid,               \
        .val_to_text    = bch2_subvolume_to_text,               \
+       .trigger        = bch2_subvolume_trigger,               \
        .min_val_size   = 16,                                   \
 })