]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: don't add metadata items for the remap tree to the extent tree
authorMark Harmstone <mark@harmstone.com>
Wed, 7 Jan 2026 14:09:05 +0000 (14:09 +0000)
committerDavid Sterba <dsterba@suse.com>
Tue, 3 Feb 2026 06:54:34 +0000 (07:54 +0100)
There is the following potential problem with the remap tree and delayed refs:

* Remapped extent freed in a delayed ref, which removes an entry from the
  remap tree
* Remap tree now small enough to fit in a single leaf
* Corruption as we now have a level-0 block with a level-1 metadata item
  in the extent tree

One solution to this would be to rework the remap tree code so that it operates
via delayed refs. But as we're hoping to remove cow-only metadata items in the
future anyway, change things so that the remap tree doesn't have any entries in
the extent tree. This also has the benefit of reducing write amplification.

We also make it so that the clear_cache mount option is a no-op, as with the
extent tree v2, as the free-space tree can no longer be recreated from the
extent tree.

Finally disable relocating the remap tree itself, which is added back in
a later patch. As it is we would get corruption as the traditional
relocation method walks the extent tree, and we're removing its metadata
items.

Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Mark Harmstone <mark@harmstone.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/disk-io.c
fs/btrfs/extent-tree.c
fs/btrfs/volumes.c

index 922e69038d8106afcb634c275b86a525810b7441..cd46b9d85880a4b68db54b69b11fb4a2b1a56dba 100644 (file)
@@ -2985,6 +2985,8 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info)
                if (btrfs_fs_incompat(fs_info, EXTENT_TREE_V2))
                        btrfs_warn(fs_info,
                                   "'clear_cache' option is ignored with extent tree v2");
+               else if (btrfs_fs_incompat(fs_info, REMAP_TREE))
+                       btrfs_warn(fs_info, "'clear_cache' option is ignored with remap tree");
                else
                        rebuild_free_space_tree = true;
        } else if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
index 5e3877a42ee686b1a329e4b6f002b09962b7d80f..48a453fa3063c7c39c314a6ce76120e43411caed 100644 (file)
@@ -1553,6 +1553,28 @@ static void free_head_ref_squota_rsv(struct btrfs_fs_info *fs_info,
                                  BTRFS_QGROUP_RSV_DATA);
 }
 
+static int drop_remap_tree_ref(struct btrfs_trans_handle *trans,
+                              const struct btrfs_delayed_ref_node *node)
+{
+       u64 bytenr = node->bytenr;
+       u64 num_bytes = node->num_bytes;
+       int ret;
+
+       ret = btrfs_add_to_free_space_tree(trans, bytenr, num_bytes);
+       if (unlikely(ret)) {
+               btrfs_abort_transaction(trans, ret);
+               return ret;
+       }
+
+       ret = btrfs_update_block_group(trans, bytenr, num_bytes, false);
+       if (unlikely(ret)) {
+               btrfs_abort_transaction(trans, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 static int run_delayed_data_ref(struct btrfs_trans_handle *trans,
                                struct btrfs_delayed_ref_head *href,
                                const struct btrfs_delayed_ref_node *node,
@@ -1747,7 +1769,10 @@ static int run_delayed_tree_ref(struct btrfs_trans_handle *trans,
        } else if (node->action == BTRFS_ADD_DELAYED_REF) {
                ret = __btrfs_inc_extent_ref(trans, node, extent_op);
        } else if (node->action == BTRFS_DROP_DELAYED_REF) {
-               ret = __btrfs_free_extent(trans, href, node, extent_op);
+               if (node->ref_root == BTRFS_REMAP_TREE_OBJECTID)
+                       ret = drop_remap_tree_ref(trans, node);
+               else
+                       ret = __btrfs_free_extent(trans, href, node, extent_op);
        } else {
                BUG();
        }
@@ -4890,6 +4915,9 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
        int level = btrfs_delayed_ref_owner(node);
        bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
 
+       if (unlikely(node->ref_root == BTRFS_REMAP_TREE_OBJECTID))
+               goto skip;
+
        extent_key.objectid = node->bytenr;
        if (skinny_metadata) {
                /* The owner of a tree block is the level. */
@@ -4942,6 +4970,7 @@ static int alloc_reserved_tree_block(struct btrfs_trans_handle *trans,
 
        btrfs_free_path(path);
 
+skip:
        return alloc_reserved_extent(trans, node->bytenr, fs_info->nodesize);
 }
 
index 2c9b55f66cc39c8215508a2a8839ffaeb58c7b08..6280a1a4c407e8d80d31b686505f4a29bd2eb092 100644 (file)
@@ -3972,6 +3972,9 @@ static bool should_balance_chunk(struct extent_buffer *leaf, struct btrfs_chunk
        struct btrfs_balance_args *bargs = NULL;
        u64 chunk_type = btrfs_chunk_type(leaf, chunk);
 
+       if (chunk_type & BTRFS_BLOCK_GROUP_METADATA_REMAP)
+               return false;
+
        /* type filter */
        if (!((chunk_type & BTRFS_BLOCK_GROUP_TYPE_MASK) &
              (bctl->flags & BTRFS_BALANCE_TYPE_MASK))) {