mirror of
https://github.com/torvalds/linux.git
synced 2026-07-29 02:31:27 +02:00
btrfs: optimize fill_holes() to merge a new hole with both adjacent items
fill_holes() currently merges a punched hole with either the previous
or the next file extent item, but never both in the same call. When
holes are punched in a non-sequential order this leaves consecutive
hole items in the inode's subvolume tree that should have been collapsed
into a single one.
This is a minor metadata optimization that reduces the number of file
extent items when holes are punched in non-sequential order. While
having extra file extent items is harmless and has no functional
impact, reducing metadata overhead can benefit workloads with heavily
fragmented hole patterns.
For example:
fallocate -p -o 4K -l 4K ${FILE}
fallocate -p -o 12K -l 4K ${FILE}
fallocate -p -o 8K -l 4K ${FILE}
After the third punch the [4K, 8K) and [12K, 16K) holes become
adjacent to the new [8K, 12K) hole, but fill_holes() merges only one
side and leaves two separate hole items ([4K, 12K) and [12K, 16K))
instead of the expected single [4K, 16K) hole item.
Fix this by checking both path->slots[0] - 1 and path->slots[0] in one
pass:
- If only the previous slot is mergeable, extend it forward as
before.
- If only the next slot is mergeable, extend it backward and update
its key offset as before.
- If both are mergeable, extend the previous item to cover the new
hole plus the next item, and remove the redundant next item with
btrfs_del_items().
Because the merge path may now delete an item, switch the initial
btrfs_search_slot() call from a plain lookup (ins_len = 0) to a
search-for-deletion (ins_len = -1), so the leaf is prepared for a
possible item removal.
Note: This optimization only applies to filesystems without the
NO_HOLES feature enabled. Since NO_HOLES is now the default, this
primarily benefits older filesystems or those explicitly created with
NO_HOLES disabled.
Signed-off-by: Dave Chen <davechen@synology.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
83f7e52b7e
commit
022568f8d5
|
|
@ -2072,6 +2072,10 @@ static int fill_holes(struct btrfs_trans_handle *trans,
|
|||
struct btrfs_file_extent_item *fi;
|
||||
struct extent_map *hole_em;
|
||||
struct btrfs_key key;
|
||||
int modify_slot = -1;
|
||||
int del_slot = -1;
|
||||
bool update_offset = false;
|
||||
u64 num_bytes = 0;
|
||||
int ret;
|
||||
|
||||
if (btrfs_fs_incompat(fs_info, NO_HOLES))
|
||||
|
|
@ -2081,7 +2085,7 @@ static int fill_holes(struct btrfs_trans_handle *trans,
|
|||
key.type = BTRFS_EXTENT_DATA_KEY;
|
||||
key.offset = offset;
|
||||
|
||||
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
|
||||
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
|
||||
if (ret <= 0) {
|
||||
/*
|
||||
* We should have dropped this offset, so if we find it then
|
||||
|
|
@ -2094,33 +2098,44 @@ static int fill_holes(struct btrfs_trans_handle *trans,
|
|||
|
||||
leaf = path->nodes[0];
|
||||
if (hole_mergeable(inode, leaf, path->slots[0] - 1, offset, end)) {
|
||||
u64 num_bytes;
|
||||
|
||||
path->slots[0]--;
|
||||
fi = btrfs_item_ptr(leaf, path->slots[0],
|
||||
fi = btrfs_item_ptr(leaf, path->slots[0] - 1,
|
||||
struct btrfs_file_extent_item);
|
||||
num_bytes = btrfs_file_extent_num_bytes(leaf, fi) +
|
||||
end - offset;
|
||||
btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
|
||||
btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
|
||||
btrfs_set_file_extent_offset(leaf, fi, 0);
|
||||
btrfs_set_file_extent_generation(leaf, fi, trans->transid);
|
||||
goto out;
|
||||
modify_slot = path->slots[0] - 1;
|
||||
}
|
||||
|
||||
if (hole_mergeable(inode, leaf, path->slots[0], offset, end)) {
|
||||
u64 num_bytes;
|
||||
|
||||
key.offset = offset;
|
||||
btrfs_set_item_key_safe(trans, path, &key);
|
||||
fi = btrfs_item_ptr(leaf, path->slots[0],
|
||||
struct btrfs_file_extent_item);
|
||||
num_bytes = btrfs_file_extent_num_bytes(leaf, fi) + end -
|
||||
offset;
|
||||
if (modify_slot != -1) {
|
||||
num_bytes += btrfs_file_extent_num_bytes(leaf, fi);
|
||||
del_slot = path->slots[0];
|
||||
} else {
|
||||
num_bytes = btrfs_file_extent_num_bytes(leaf, fi) +
|
||||
end - offset;
|
||||
modify_slot = path->slots[0];
|
||||
update_offset = true;
|
||||
}
|
||||
}
|
||||
if (modify_slot >= 0) {
|
||||
fi = btrfs_item_ptr(leaf, modify_slot,
|
||||
struct btrfs_file_extent_item);
|
||||
btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
|
||||
btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
|
||||
if (update_offset) {
|
||||
key.offset = offset;
|
||||
btrfs_set_item_key_safe(trans, path, &key);
|
||||
}
|
||||
btrfs_set_file_extent_offset(leaf, fi, 0);
|
||||
btrfs_set_file_extent_generation(leaf, fi, trans->transid);
|
||||
if (del_slot >= 0) {
|
||||
ret = btrfs_del_items(trans, root, path, del_slot, 1);
|
||||
if (ret) {
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
btrfs_release_path(path);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
goto out;
|
||||
}
|
||||
btrfs_release_path(path);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user