]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
bcachefs: bcachefs_metadata_version_fast_device_removal
authorKent Overstreet <kent.overstreet@linux.dev>
Sun, 4 May 2025 01:26:04 +0000 (21:26 -0400)
committerKent Overstreet <kent.overstreet@linux.dev>
Thu, 22 May 2025 00:14:49 +0000 (20:14 -0400)
Fast device removal, that uses backpointers to find pointers to the
device being removed instead of a full metadata scan.

This requires BCH_SB_MEMBER_DELETED_UUID, which is an incompatible
change - hence the version number bump. We don't fully trust
backpointers, so we don't want to reuse device indexes until after a
fsck has verified that there aren't any pointers to removed devices.

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

index 061fa2666f35b495ff828ee6dd8089a46d12ea67..a483d440fa39dd4c99192de6dc938fc1171e7d4a 100644 (file)
@@ -696,7 +696,8 @@ struct bch_sb_field_ext {
        x(stripe_lru,                   BCH_VERSION(1, 23))             \
        x(casefolding,                  BCH_VERSION(1, 24))             \
        x(extent_flags,                 BCH_VERSION(1, 25))             \
-       x(snapshot_deletion_v2,         BCH_VERSION(1, 26))
+       x(snapshot_deletion_v2,         BCH_VERSION(1, 26))             \
+       x(fast_device_removal,          BCH_VERSION(1, 27))
 
 enum bcachefs_metadata_version {
        bcachefs_metadata_version_min = 9,
index bf5f4f6283a436921fa4e1ce7854087d1629935f..c581426e3894a391ff8e22d043329bddeea23f3b 100644 (file)
@@ -2197,13 +2197,15 @@ static int bch2_invalidate_stripe_to_dev_from_alloc(struct btree_trans *trans, s
 
 int bch2_dev_remove_stripes(struct bch_fs *c, unsigned dev_idx, unsigned flags)
 {
-       return bch2_trans_run(c,
+       int ret = bch2_trans_run(c,
                for_each_btree_key_max_commit(trans, iter,
                                  BTREE_ID_alloc, POS(dev_idx, 0), POS(dev_idx, U64_MAX),
                                  BTREE_ITER_intent, k,
                                  NULL, NULL, 0, ({
                        bch2_invalidate_stripe_to_dev_from_alloc(trans, k, flags);
        })));
+       bch_err_fn(c, ret);
+       return ret;
 }
 
 /* startup/shutdown */
index faa012107a97b70e113ce795eaa4f26c4c78d1cc..dc8189f9d2f175b1a3e81dab3aec95840b314817 100644 (file)
@@ -1726,6 +1726,8 @@ int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags)
 {
        struct bch_member *m;
        unsigned dev_idx = ca->dev_idx, data;
+       bool fast_device_removal = !bch2_request_incompat_feature(c,
+                                       bcachefs_metadata_version_fast_device_removal);
        int ret;
 
        down_write(&c->state_lock);
@@ -1744,12 +1746,25 @@ int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags)
 
        __bch2_dev_read_only(c, ca);
 
-       ret = bch2_dev_data_drop(c, ca->dev_idx, flags) ?:
-               bch2_dev_remove_stripes(c, ca->dev_idx, flags);
-       bch_err_msg(ca, ret, "bch2_dev_data_drop()");
+       ret = fast_device_removal
+               ? bch2_dev_data_drop_by_backpointers(c, ca->dev_idx, flags)
+               : (bch2_dev_data_drop(c, ca->dev_idx, flags) ?:
+                  bch2_dev_remove_stripes(c, ca->dev_idx, flags));
        if (ret)
                goto err;
 
+       /* Check if device still has data before blowing away alloc info */
+       struct bch_dev_usage usage = bch2_dev_usage_read(ca);
+       for (unsigned i = 0; i < BCH_DATA_NR; i++)
+               if (!data_type_is_empty(i) &&
+                   !data_type_is_hidden(i) &&
+                   usage.buckets[i]) {
+                       bch_err(ca, "Remove failed: still has data (%s, %llu buckets)",
+                               __bch2_data_types[i], usage.buckets[i]);
+                       ret = -EBUSY;
+                       goto err;
+               }
+
        ret = bch2_dev_remove_alloc(c, ca);
        bch_err_msg(ca, ret, "bch2_dev_remove_alloc()");
        if (ret)
@@ -1813,7 +1828,11 @@ int bch2_dev_remove(struct bch_fs *c, struct bch_dev *ca, int flags)
         */
        mutex_lock(&c->sb_lock);
        m = bch2_members_v2_get_mut(c->disk_sb.sb, dev_idx);
-       memset(&m->uuid, 0, sizeof(m->uuid));
+
+       if (fast_device_removal)
+               m->uuid = BCH_SB_MEMBER_DELETED_UUID;
+       else
+               memset(&m->uuid, 0, sizeof(m->uuid));
 
        bch2_write_super(c);