]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: reuse cloned extent buffer during fiemap to avoid re-allocations
authorFilipe Manana <fdmanana@suse.com>
Wed, 28 Feb 2024 14:36:33 +0000 (14:36 +0000)
committerDavid Sterba <dsterba@suse.com>
Tue, 5 Mar 2024 17:14:19 +0000 (18:14 +0100)
During fiemap we may have to visit multiple leaves of the subvolume's
inode tree, and each time we are freeing and allocating an extent buffer
to use as a clone of each visited leaf. Optimize this by reusing cloned
extent buffers, to avoid the freeing and re-allocation both of the extent
buffer structure itself and more importantly of the pages attached to the
extent buffer.

Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent_io.c

index fbb05b0f7ebc5c6bc75781b12d0cc6a322ea99cc..7441245b1ceb1508558782a88759fd34ddb84818 100644 (file)
@@ -2752,7 +2752,7 @@ static int emit_last_fiemap_cache(struct fiemap_extent_info *fieinfo,
 
 static int fiemap_next_leaf_item(struct btrfs_inode *inode, struct btrfs_path *path)
 {
-       struct extent_buffer *clone;
+       struct extent_buffer *clone = path->nodes[0];
        struct btrfs_key key;
        int slot;
        int ret;
@@ -2761,29 +2761,45 @@ static int fiemap_next_leaf_item(struct btrfs_inode *inode, struct btrfs_path *p
        if (path->slots[0] < btrfs_header_nritems(path->nodes[0]))
                return 0;
 
+       /*
+        * Add a temporary extra ref to an already cloned extent buffer to
+        * prevent btrfs_next_leaf() freeing it, we want to reuse it to avoid
+        * the cost of allocating a new one.
+        */
+       ASSERT(test_bit(EXTENT_BUFFER_UNMAPPED, &clone->bflags));
+       atomic_inc(&clone->refs);
+
        ret = btrfs_next_leaf(inode->root, path);
        if (ret != 0)
-               return ret;
+               goto out;
 
        /*
         * Don't bother with cloning if there are no more file extent items for
         * our inode.
         */
        btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
-       if (key.objectid != btrfs_ino(inode) || key.type != BTRFS_EXTENT_DATA_KEY)
-               return 1;
+       if (key.objectid != btrfs_ino(inode) || key.type != BTRFS_EXTENT_DATA_KEY) {
+               ret = 1;
+               goto out;
+       }
 
        /* See the comment at fiemap_search_slot() about why we clone. */
-       clone = btrfs_clone_extent_buffer(path->nodes[0]);
-       if (!clone)
-               return -ENOMEM;
+       copy_extent_buffer_full(clone, path->nodes[0]);
+       /*
+        * Important to preserve the start field, for the optimizations when
+        * checking if extents are shared (see extent_fiemap()).
+        */
+       clone->start = path->nodes[0]->start;
 
        slot = path->slots[0];
        btrfs_release_path(path);
        path->nodes[0] = clone;
        path->slots[0] = slot;
+out:
+       if (ret)
+               free_extent_buffer(clone);
 
-       return 0;
+       return ret;
 }
 
 /*