btrfs: reuse cloned extent buffer during fiemap to avoid re-allocations

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>
This commit is contained in:
Filipe Manana 2024-02-28 14:36:33 +00:00 committed by David Sterba
parent 978b63f746
commit 1cab1375ba

View 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) 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; struct btrfs_key key;
int slot; int slot;
int ret; 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])) if (path->slots[0] < btrfs_header_nritems(path->nodes[0]))
return 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); ret = btrfs_next_leaf(inode->root, path);
if (ret != 0) if (ret != 0)
return ret; goto out;
/* /*
* Don't bother with cloning if there are no more file extent items for * Don't bother with cloning if there are no more file extent items for
* our inode. * our inode.
*/ */
btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]); btrfs_item_key_to_cpu(path->nodes[0], &key, path->slots[0]);
if (key.objectid != btrfs_ino(inode) || key.type != BTRFS_EXTENT_DATA_KEY) if (key.objectid != btrfs_ino(inode) || key.type != BTRFS_EXTENT_DATA_KEY) {
return 1; ret = 1;
goto out;
}
/* See the comment at fiemap_search_slot() about why we clone. */ /* See the comment at fiemap_search_slot() about why we clone. */
clone = btrfs_clone_extent_buffer(path->nodes[0]); copy_extent_buffer_full(clone, path->nodes[0]);
if (!clone) /*
return -ENOMEM; * 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]; slot = path->slots[0];
btrfs_release_path(path); btrfs_release_path(path);
path->nodes[0] = clone; path->nodes[0] = clone;
path->slots[0] = slot; path->slots[0] = slot;
out:
if (ret)
free_extent_buffer(clone);
return 0; return ret;
} }
/* /*