Skip to content

Commit 5db7ccf

Browse files
fcmaplefdmanana
authored andcommitted
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>
1 parent 436ac81 commit 5db7ccf

1 file changed

Lines changed: 32 additions & 17 deletions

File tree

fs/btrfs/file.c

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2067,6 +2067,10 @@ static int fill_holes(struct btrfs_trans_handle *trans,
20672067
struct btrfs_file_extent_item *fi;
20682068
struct extent_map *hole_em;
20692069
struct btrfs_key key;
2070+
int modify_slot = -1;
2071+
int del_slot = -1;
2072+
bool update_offset = false;
2073+
u64 num_bytes = 0;
20702074
int ret;
20712075

20722076
if (btrfs_fs_incompat(fs_info, NO_HOLES))
@@ -2076,7 +2080,7 @@ static int fill_holes(struct btrfs_trans_handle *trans,
20762080
key.type = BTRFS_EXTENT_DATA_KEY;
20772081
key.offset = offset;
20782082

2079-
ret = btrfs_search_slot(trans, root, &key, path, 0, 1);
2083+
ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
20802084
if (ret <= 0) {
20812085
/*
20822086
* We should have dropped this offset, so if we find it then
@@ -2089,33 +2093,44 @@ static int fill_holes(struct btrfs_trans_handle *trans,
20892093

20902094
leaf = path->nodes[0];
20912095
if (hole_mergeable(inode, leaf, path->slots[0] - 1, offset, end)) {
2092-
u64 num_bytes;
2093-
2094-
path->slots[0]--;
2095-
fi = btrfs_item_ptr(leaf, path->slots[0],
2096+
fi = btrfs_item_ptr(leaf, path->slots[0] - 1,
20962097
struct btrfs_file_extent_item);
20972098
num_bytes = btrfs_file_extent_num_bytes(leaf, fi) +
20982099
end - offset;
2099-
btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
2100-
btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
2101-
btrfs_set_file_extent_offset(leaf, fi, 0);
2102-
btrfs_set_file_extent_generation(leaf, fi, trans->transid);
2103-
goto out;
2100+
modify_slot = path->slots[0] - 1;
21042101
}
2105-
21062102
if (hole_mergeable(inode, leaf, path->slots[0], offset, end)) {
2107-
u64 num_bytes;
2108-
2109-
key.offset = offset;
2110-
btrfs_set_item_key_safe(trans, path, &key);
21112103
fi = btrfs_item_ptr(leaf, path->slots[0],
21122104
struct btrfs_file_extent_item);
2113-
num_bytes = btrfs_file_extent_num_bytes(leaf, fi) + end -
2114-
offset;
2105+
if (modify_slot != -1) {
2106+
num_bytes += btrfs_file_extent_num_bytes(leaf, fi);
2107+
del_slot = path->slots[0];
2108+
} else {
2109+
num_bytes = btrfs_file_extent_num_bytes(leaf, fi) +
2110+
end - offset;
2111+
modify_slot = path->slots[0];
2112+
update_offset = true;
2113+
}
2114+
}
2115+
if (modify_slot >= 0) {
2116+
fi = btrfs_item_ptr(leaf, modify_slot,
2117+
struct btrfs_file_extent_item);
21152118
btrfs_set_file_extent_num_bytes(leaf, fi, num_bytes);
21162119
btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);
2120+
if (update_offset) {
2121+
key.offset = offset;
2122+
btrfs_set_item_key_safe(trans, path, &key);
2123+
}
21172124
btrfs_set_file_extent_offset(leaf, fi, 0);
21182125
btrfs_set_file_extent_generation(leaf, fi, trans->transid);
2126+
if (del_slot >= 0) {
2127+
ret = btrfs_del_items(trans, root, path, del_slot, 1);
2128+
if (ret) {
2129+
btrfs_abort_transaction(trans, ret);
2130+
btrfs_release_path(path);
2131+
return ret;
2132+
}
2133+
}
21192134
goto out;
21202135
}
21212136
btrfs_release_path(path);

0 commit comments

Comments
 (0)