]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: factor out root promotion logic into promote_child_to_root()
authorSun YangKai <sunk67188@gmail.com>
Fri, 14 Nov 2025 07:24:45 +0000 (15:24 +0800)
committerDavid Sterba <dsterba@suse.com>
Tue, 25 Nov 2025 00:50:56 +0000 (01:50 +0100)
The balance_level() function is overly long and contains a cold code path
that handles promoting a child node to root when the root has only one item.
This code has distinct logic that is clearer and more maintainable when
isolated in its own function.

Signed-off-by: Sun YangKai <sunk67188@gmail.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/ctree.c

index 51dc8e0bc9c1b58065d71d0c85c2f96acb012e3b..3abddd2cdfd38463360e8cb88b1455c2671b0c9e 100644 (file)
@@ -861,6 +861,75 @@ struct extent_buffer *btrfs_read_node_slot(struct extent_buffer *parent,
        return eb;
 }
 
+/*
+ * Promote a child node to become the new tree root.
+ *
+ * @trans:   Transaction handle
+ * @root:    Tree root structure to update
+ * @path:    Path holding nodes and locks
+ * @level:   Level of the parent (old root)
+ * @parent:  The parent (old root) with exactly one item
+ *
+ * This helper is called during rebalancing when the root node contains only
+ * a single item (nritems == 1).  We can reduce the tree height by promoting
+ * that child to become the new root and freeing the old root node.  The path
+ * locks and references are updated accordingly.
+ *
+ * Return: 0 on success, negative errno on failure.  The transaction is aborted
+ * on critical errors.
+ */
+static int promote_child_to_root(struct btrfs_trans_handle *trans,
+                                struct btrfs_root *root, struct btrfs_path *path,
+                                int level, struct extent_buffer *parent)
+{
+       struct extent_buffer *child;
+       int ret;
+
+       ASSERT(btrfs_header_nritems(parent) == 1);
+
+       child = btrfs_read_node_slot(parent, 0);
+       if (IS_ERR(child))
+               return PTR_ERR(child);
+
+       btrfs_tree_lock(child);
+       ret = btrfs_cow_block(trans, root, child, parent, 0, &child, BTRFS_NESTING_COW);
+       if (ret) {
+               btrfs_tree_unlock(child);
+               free_extent_buffer(child);
+               return ret;
+       }
+
+       ret = btrfs_tree_mod_log_insert_root(root->node, child, true);
+       if (unlikely(ret < 0)) {
+               btrfs_tree_unlock(child);
+               free_extent_buffer(child);
+               btrfs_abort_transaction(trans, ret);
+               return ret;
+       }
+       rcu_assign_pointer(root->node, child);
+
+       add_root_to_dirty_list(root);
+       btrfs_tree_unlock(child);
+
+       path->locks[level] = 0;
+       path->nodes[level] = NULL;
+       btrfs_clear_buffer_dirty(trans, parent);
+       btrfs_tree_unlock(parent);
+       /* Once for the path. */
+       free_extent_buffer(parent);
+
+       root_sub_used_bytes(root);
+       ret = btrfs_free_tree_block(trans, btrfs_root_id(root), parent, 0, 1);
+       /* Once for the root ptr. */
+       free_extent_buffer_stale(parent);
+       if (unlikely(ret < 0)) {
+               btrfs_abort_transaction(trans, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 /*
  * node level balancing, used to make sure nodes are in proper order for
  * item deletion.  We balance from the top down, so we have to make sure
@@ -900,55 +969,10 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
         * by promoting the node below to a root
         */
        if (!parent) {
-               struct extent_buffer *child;
-
                if (btrfs_header_nritems(mid) != 1)
                        return 0;
 
-               /* promote the child to a root */
-               child = btrfs_read_node_slot(mid, 0);
-               if (IS_ERR(child)) {
-                       ret = PTR_ERR(child);
-                       goto out;
-               }
-
-               btrfs_tree_lock(child);
-               ret = btrfs_cow_block(trans, root, child, mid, 0, &child,
-                                     BTRFS_NESTING_COW);
-               if (ret) {
-                       btrfs_tree_unlock(child);
-                       free_extent_buffer(child);
-                       goto out;
-               }
-
-               ret = btrfs_tree_mod_log_insert_root(root->node, child, true);
-               if (unlikely(ret < 0)) {
-                       btrfs_tree_unlock(child);
-                       free_extent_buffer(child);
-                       btrfs_abort_transaction(trans, ret);
-                       goto out;
-               }
-               rcu_assign_pointer(root->node, child);
-
-               add_root_to_dirty_list(root);
-               btrfs_tree_unlock(child);
-
-               path->locks[level] = 0;
-               path->nodes[level] = NULL;
-               btrfs_clear_buffer_dirty(trans, mid);
-               btrfs_tree_unlock(mid);
-               /* once for the path */
-               free_extent_buffer(mid);
-
-               root_sub_used_bytes(root);
-               ret = btrfs_free_tree_block(trans, btrfs_root_id(root), mid, 0, 1);
-               /* once for the root ptr */
-               free_extent_buffer_stale(mid);
-               if (unlikely(ret < 0)) {
-                       btrfs_abort_transaction(trans, ret);
-                       goto out;
-               }
-               return 0;
+               return promote_child_to_root(trans, root, path, level, mid);
        }
        if (btrfs_header_nritems(mid) >
            BTRFS_NODEPTRS_PER_BLOCK(fs_info) / 4)