]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bcachefs: bcachefs_metadata_version_snapshot_deletion_v2
authorKent Overstreet <kent.overstreet@linux.dev>
Fri, 2 May 2025 16:37:36 +0000 (12:37 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 22 May 2025 00:14:45 +0000 (20:14 -0400)
We're going to be speeding up snapshot deletion, by only having it
process the extents/dirents/xattrs btrees if an inode of a given
snapshot ID was present.

This raises the possibility of 'bkey_in_missing_snapshot' errors popping
up, if we ever accidentally don't do the corresponding inode update, or
if the new algorithm has bugs.

So instead of deleting snapshot IDs, add a new deleted flag, so that
'key in missing snapshot' errors can more definitively tell what
happened and automatically repair.

Signed-off-by: Kent Overstreet <kent.overstreet@linux.dev>
fs/bcachefs/bcachefs_format.h
fs/bcachefs/snapshot.c
fs/bcachefs/snapshot.h
fs/bcachefs/snapshot_format.h
fs/bcachefs/snapshot_types.h
fs/bcachefs/subvolume_types.h

index 7ce475c565b53de9af069e4950661de2d0653021..0beff6af7ecf28a074fc46d89c9f0882a016164f 100644 (file)
@@ -695,7 +695,8 @@ struct bch_sb_field_ext {
        x(stripe_backpointers,          BCH_VERSION(1, 22))             \
        x(stripe_lru,                   BCH_VERSION(1, 23))             \
        x(casefolding,                  BCH_VERSION(1, 24))             \
-       x(extent_flags,                 BCH_VERSION(1, 25))
+       x(extent_flags,                 BCH_VERSION(1, 25))             \
+       x(snapshot_deletion_v2,         BCH_VERSION(1, 26))
 
 enum bcachefs_metadata_version {
        bcachefs_metadata_version_min = 9,
index 87c8aead86105e07efc90766551437ba44e4900b..a16fa0d8a27470d4123fdc2cbdef5d7adfe432f9 100644 (file)
@@ -211,9 +211,14 @@ void bch2_snapshot_to_text(struct printbuf *out, struct bch_fs *c,
 {
        struct bkey_s_c_snapshot s = bkey_s_c_to_snapshot(k);
 
-       prt_printf(out, "is_subvol %llu deleted %llu parent %10u children %10u %10u subvol %u tree %u",
-              BCH_SNAPSHOT_SUBVOL(s.v),
-              BCH_SNAPSHOT_WILL_DELETE(s.v),
+       if (BCH_SNAPSHOT_SUBVOL(s.v))
+               prt_str(out, "subvol ");
+       if (BCH_SNAPSHOT_WILL_DELETE(s.v))
+               prt_str(out, "will_delete ");
+       if (BCH_SNAPSHOT_DELETED(s.v))
+               prt_str(out, "deleted ");
+
+       prt_printf(out, "parent %10u children %10u %10u subvol %u tree %u",
               le32_to_cpu(s.v->parent),
               le32_to_cpu(s.v->children[0]),
               le32_to_cpu(s.v->children[1]),
@@ -314,7 +319,9 @@ static int __bch2_mark_snapshot(struct btree_trans *trans,
        if (new.k->type == KEY_TYPE_snapshot) {
                struct bkey_s_c_snapshot s = bkey_s_c_to_snapshot(new);
 
-               t->live         = true;
+               t->state        = !BCH_SNAPSHOT_DELETED(s.v)
+                       ? SNAPSHOT_ID_live
+                       : SNAPSHOT_ID_deleted;
                t->parent       = le32_to_cpu(s.v->parent);
                t->children[0]  = le32_to_cpu(s.v->children[0]);
                t->children[1]  = le32_to_cpu(s.v->children[1]);
@@ -711,6 +718,9 @@ static int check_snapshot(struct btree_trans *trans,
        memset(&s, 0, sizeof(s));
        memcpy(&s, k.v, min(sizeof(s), bkey_val_bytes(k.k)));
 
+       if (BCH_SNAPSHOT_DELETED(&s))
+               return 0;
+
        id = le32_to_cpu(s.parent);
        if (id) {
                ret = bch2_snapshot_lookup(trans, id, &v);
@@ -998,7 +1008,7 @@ int bch2_reconstruct_snapshots(struct bch_fs *c)
                snapshot_id_list_to_text(&buf, t);
 
                darray_for_each(*t, id) {
-                       if (fsck_err_on(!bch2_snapshot_exists(c, *id),
+                       if (fsck_err_on(bch2_snapshot_id_state(c, *id) == SNAPSHOT_ID_empty,
                                        trans, snapshot_node_missing,
                                        "snapshot node %u from tree %s missing, recreate?", *id, buf.buf)) {
                                if (t->nr > 1) {
@@ -1023,22 +1033,38 @@ err:
        return ret;
 }
 
-int bch2_check_key_has_snapshot(struct btree_trans *trans,
-                               struct btree_iter *iter,
-                               struct bkey_s_c k)
+int __bch2_check_key_has_snapshot(struct btree_trans *trans,
+                                 struct btree_iter *iter,
+                                 struct bkey_s_c k)
 {
        struct bch_fs *c = trans->c;
        struct printbuf buf = PRINTBUF;
        int ret = 0;
+       enum snapshot_id_state state = bch2_snapshot_id_state(c, k.k->p.snapshot);
 
-       if (fsck_err_on(!bch2_snapshot_exists(c, k.k->p.snapshot),
+       /* Snapshot was definitively deleted, this error is marked autofix */
+       if (fsck_err_on(state == SNAPSHOT_ID_deleted,
+                       trans, bkey_in_deleted_snapshot,
+                       "key in deleted snapshot %s, delete?",
+                       (bch2_btree_id_to_text(&buf, iter->btree_id),
+                        prt_char(&buf, ' '),
+                        bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
+               ret = bch2_btree_delete_at(trans, iter,
+                                          BTREE_UPDATE_internal_snapshot_node) ?: 1;
+
+       /*
+        * Snapshot missing: we should have caught this with btree_lost_data and
+        * kicked off reconstruct_snapshots, so if we end up here we have no
+        * idea what happened:
+        */
+       if (fsck_err_on(state == SNAPSHOT_ID_empty,
                        trans, bkey_in_missing_snapshot,
                        "key in missing snapshot %s, delete?",
                        (bch2_btree_id_to_text(&buf, iter->btree_id),
                         prt_char(&buf, ' '),
                         bch2_bkey_val_to_text(&buf, c, k), buf.buf)))
                ret = bch2_btree_delete_at(trans, iter,
-                                           BTREE_UPDATE_internal_snapshot_node) ?: 1;
+                                          BTREE_UPDATE_internal_snapshot_node) ?: 1;
 fsck_err:
        printbuf_exit(&buf);
        return ret;
@@ -1085,24 +1111,25 @@ static int bch2_snapshot_node_delete(struct btree_trans *trans, u32 id)
        struct btree_iter iter, p_iter = {};
        struct btree_iter c_iter = {};
        struct btree_iter tree_iter = {};
-       struct bkey_s_c_snapshot s;
        u32 parent_id, child_id;
        unsigned i;
        int ret = 0;
 
-       s = bch2_bkey_get_iter_typed(trans, &iter, BTREE_ID_snapshots, POS(0, id),
-                                    BTREE_ITER_intent, snapshot);
-       ret = bkey_err(s);
+       struct bkey_i_snapshot *s =
+               bch2_bkey_get_mut_typed(trans, &iter, BTREE_ID_snapshots, POS(0, id),
+                                       BTREE_ITER_intent, snapshot);
+       ret = PTR_ERR_OR_ZERO(s);
        bch2_fs_inconsistent_on(bch2_err_matches(ret, ENOENT), c,
                                "missing snapshot %u", id);
 
        if (ret)
                goto err;
 
-       BUG_ON(s.v->children[1]);
+       BUG_ON(BCH_SNAPSHOT_DELETED(&s->v));
+       BUG_ON(s->v.children[1]);
 
-       parent_id = le32_to_cpu(s.v->parent);
-       child_id = le32_to_cpu(s.v->children[0]);
+       parent_id = le32_to_cpu(s->v.parent);
+       child_id = le32_to_cpu(s->v.children[0]);
 
        if (parent_id) {
                struct bkey_i_snapshot *parent;
@@ -1160,24 +1187,38 @@ static int bch2_snapshot_node_delete(struct btree_trans *trans, u32 id)
                 */
                struct bkey_i_snapshot_tree *s_t;
 
-               BUG_ON(s.v->children[1]);
+               BUG_ON(s->v.children[1]);
 
                s_t = bch2_bkey_get_mut_typed(trans, &tree_iter,
-                               BTREE_ID_snapshot_trees, POS(0, le32_to_cpu(s.v->tree)),
+                               BTREE_ID_snapshot_trees, POS(0, le32_to_cpu(s->v.tree)),
                                0, snapshot_tree);
                ret = PTR_ERR_OR_ZERO(s_t);
                if (ret)
                        goto err;
 
-               if (s.v->children[0]) {
-                       s_t->v.root_snapshot = s.v->children[0];
+               if (s->v.children[0]) {
+                       s_t->v.root_snapshot = s->v.children[0];
                } else {
                        s_t->k.type = KEY_TYPE_deleted;
                        set_bkey_val_u64s(&s_t->k, 0);
                }
        }
 
-       ret = bch2_btree_delete_at(trans, &iter, 0);
+       if (!bch2_request_incompat_feature(c, bcachefs_metadata_version_snapshot_deletion_v2)) {
+               SET_BCH_SNAPSHOT_DELETED(&s->v, true);
+               s->v.parent             = 0;
+               s->v.children[0]        = 0;
+               s->v.children[1]        = 0;
+               s->v.subvol             = 0;
+               s->v.tree               = 0;
+               s->v.depth              = 0;
+               s->v.skip[0]            = 0;
+               s->v.skip[1]            = 0;
+               s->v.skip[2]            = 0;
+       } else {
+               s->k.type = KEY_TYPE_deleted;
+               set_bkey_val_u64s(&s->k, 0);
+       }
 err:
        bch2_trans_iter_exit(trans, &tree_iter);
        bch2_trans_iter_exit(trans, &p_iter);
@@ -1478,6 +1519,9 @@ static int check_should_delete_snapshot(struct btree_trans *trans, struct bkey_s
        if (BCH_SNAPSHOT_SUBVOL(s.v))
                return 0;
 
+       if (BCH_SNAPSHOT_DELETED(s.v))
+               return 0;
+
        mutex_lock(&d->progress_lock);
        for (unsigned i = 0; i < 2; i++) {
                u32 child = le32_to_cpu(s.v->children[i]);
@@ -1536,6 +1580,9 @@ static int bch2_fix_child_of_deleted_snapshot(struct btree_trans *trans,
        struct bkey_i_snapshot *s;
        int ret;
 
+       if (!bch2_snapshot_exists(c, k.k->p.offset))
+               return 0;
+
        if (k.k->type != KEY_TYPE_snapshot)
                return 0;
 
index 24a451bb7024f53285683e425b7f1ec7367965ad..69c484b77729e8de40592a7797fe751a93eb87d3 100644 (file)
@@ -120,21 +120,26 @@ static inline u32 bch2_snapshot_root(struct bch_fs *c, u32 id)
        return id;
 }
 
-static inline bool __bch2_snapshot_exists(struct bch_fs *c, u32 id)
+static inline enum snapshot_id_state __bch2_snapshot_id_state(struct bch_fs *c, u32 id)
 {
        const struct snapshot_t *s = snapshot_t(c, id);
-       return s ? s->live : 0;
+       return s ? s->state : SNAPSHOT_ID_empty;
 }
 
-static inline bool bch2_snapshot_exists(struct bch_fs *c, u32 id)
+static inline enum snapshot_id_state bch2_snapshot_id_state(struct bch_fs *c, u32 id)
 {
        rcu_read_lock();
-       bool ret = __bch2_snapshot_exists(c, id);
+       enum snapshot_id_state ret = __bch2_snapshot_id_state(c, id);
        rcu_read_unlock();
 
        return ret;
 }
 
+static inline bool bch2_snapshot_exists(struct bch_fs *c, u32 id)
+{
+       return bch2_snapshot_id_state(c, id) == SNAPSHOT_ID_live;
+}
+
 static inline int bch2_snapshot_is_internal_node(struct bch_fs *c, u32 id)
 {
        rcu_read_lock();
@@ -241,7 +246,17 @@ int bch2_snapshot_node_create(struct btree_trans *, u32,
 int bch2_check_snapshot_trees(struct bch_fs *);
 int bch2_check_snapshots(struct bch_fs *);
 int bch2_reconstruct_snapshots(struct bch_fs *);
-int bch2_check_key_has_snapshot(struct btree_trans *, struct btree_iter *, struct bkey_s_c);
+
+int __bch2_check_key_has_snapshot(struct btree_trans *, struct btree_iter *, struct bkey_s_c);
+
+static inline int bch2_check_key_has_snapshot(struct btree_trans *trans,
+                                             struct btree_iter *iter,
+                                             struct bkey_s_c k)
+{
+       return likely(bch2_snapshot_exists(trans->c, k.k->p.snapshot))
+               ? 0
+               : __bch2_check_key_has_snapshot(trans, iter, k);
+}
 
 int bch2_snapshot_node_set_deleted(struct btree_trans *, u32);
 
index 685a9fe209ab2cbda2661591e3ebb6c725ea8225..9bccae1f3590ad3e215ff41fbb7399aefa7dcb42 100644 (file)
@@ -16,9 +16,9 @@ struct bch_snapshot {
 };
 
 LE32_BITMASK(BCH_SNAPSHOT_WILL_DELETE, struct bch_snapshot, flags,  0,  1)
-
 /* True if a subvolume points to this snapshot node: */
 LE32_BITMASK(BCH_SNAPSHOT_SUBVOL,      struct bch_snapshot, flags,  1,  2)
+LE32_BITMASK(BCH_SNAPSHOT_DELETED,     struct bch_snapshot, flags,  2,  3)
 
 /*
  * Snapshot trees:
index 6a969996e68f42db98667a6272b71b9ef624950e..1aa7a58442ae6a6bc7e4e806daa39b655b694823 100644 (file)
@@ -3,8 +3,38 @@
 #define _BCACHEFS_SNAPSHOT_TYPES_H
 
 #include "bbpos_types.h"
+#include "darray.h"
 #include "subvolume_types.h"
 
+typedef DARRAY(u32) snapshot_id_list;
+
+#define IS_ANCESTOR_BITMAP     128
+
+struct snapshot_t {
+       enum snapshot_id_state {
+               SNAPSHOT_ID_empty,
+               SNAPSHOT_ID_live,
+               SNAPSHOT_ID_deleted,
+       }                       state;
+       u32                     parent;
+       u32                     skip[3];
+       u32                     depth;
+       u32                     children[2];
+       u32                     subvol; /* Nonzero only if a subvolume points to this node: */
+       u32                     tree;
+       unsigned long           is_ancestor[BITS_TO_LONGS(IS_ANCESTOR_BITMAP)];
+};
+
+struct snapshot_table {
+       struct rcu_head         rcu;
+       size_t                  nr;
+#ifndef RUST_BINDGEN
+       DECLARE_FLEX_ARRAY(struct snapshot_t, s);
+#else
+       struct snapshot_t       s[0];
+#endif
+};
+
 struct snapshot_interior_delete {
        u32     id;
        u32     live_child;
index 1549d6daf7af5b78e4b7449a49ef292a2e529de1..9d634b906dcda3086f7b4d0025c091f1acc7b15e 100644 (file)
@@ -2,33 +2,6 @@
 #ifndef _BCACHEFS_SUBVOLUME_TYPES_H
 #define _BCACHEFS_SUBVOLUME_TYPES_H
 
-#include "darray.h"
-
-typedef DARRAY(u32) snapshot_id_list;
-
-#define IS_ANCESTOR_BITMAP     128
-
-struct snapshot_t {
-       bool                    live;
-       u32                     parent;
-       u32                     skip[3];
-       u32                     depth;
-       u32                     children[2];
-       u32                     subvol; /* Nonzero only if a subvolume points to this node: */
-       u32                     tree;
-       unsigned long           is_ancestor[BITS_TO_LONGS(IS_ANCESTOR_BITMAP)];
-};
-
-struct snapshot_table {
-       struct rcu_head         rcu;
-       size_t                  nr;
-#ifndef RUST_BINDGEN
-       DECLARE_FLEX_ARRAY(struct snapshot_t, s);
-#else
-       struct snapshot_t       s[0];
-#endif
-};
-
 typedef struct {
        /* we can't have padding in this struct: */
        u64             subvol;