Commit b3e744fe authored by Filipe Manana's avatar Filipe Manana Committed by David Sterba

btrfs: use cached state when looking for delalloc ranges with fiemap

During fiemap, whenever we find a hole or prealloc extent, we will look
for delalloc in that range, and one of the things we do for that is to
find out ranges in the inode's io_tree marked with EXTENT_DELALLOC, using
calls to count_range_bits().

Since we process file extents from left to right, if we have a file with
several holes or prealloc extents, we benefit from keeping a cached extent
state record for calls to count_range_bits(). Most of the time the last
extent state record we visited in one call to count_range_bits() matches
the first extent state record we will use in the next call to
count_range_bits(), so there's a benefit here. So use an extent state
record to cache results from count_range_bits() calls during fiemap.

This change is part of a patchset that has the goal to make performance
better for applications that use lseek's SEEK_HOLE and SEEK_DATA modes to
iterate over the extents of a file. Two examples are the cp program from
coreutils 9.0+ and the tar program (when using its --sparse / -S option).
A sample test and results are listed in the changelog of the last patch
in the series:

  1/9 btrfs: remove leftover setting of EXTENT_UPTODATE state in an inode's io_tree
  2/9 btrfs: add an early exit when searching for delalloc range for lseek/fiemap
  3/9 btrfs: skip unnecessary delalloc searches during lseek/fiemap
  4/9 btrfs: search for delalloc more efficiently during lseek/fiemap
  5/9 btrfs: remove no longer used btrfs_next_extent_map()
  6/9 btrfs: allow passing a cached state record to count_range_bits()
  7/9 btrfs: update stale comment for count_range_bits()
  8/9 btrfs: use cached state when looking for delalloc ranges with fiemap
  9/9 btrfs: use cached state when looking for delalloc ranges with lseek
Reported-by: default avatarWang Yugui <wangyugui@e16-tech.com>
Link: https://lore.kernel.org/linux-btrfs/20221106073028.71F9.409509F4@e16-tech.com/
Link: https://lore.kernel.org/linux-btrfs/CAL3q7H5NSVicm7nYBJ7x8fFkDpno8z3PYt5aPU43Bajc1H0h1Q@mail.gmail.com/Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 1ee51a06
...@@ -3698,6 +3698,7 @@ static int fiemap_search_slot(struct btrfs_inode *inode, struct btrfs_path *path ...@@ -3698,6 +3698,7 @@ static int fiemap_search_slot(struct btrfs_inode *inode, struct btrfs_path *path
static int fiemap_process_hole(struct btrfs_inode *inode, static int fiemap_process_hole(struct btrfs_inode *inode,
struct fiemap_extent_info *fieinfo, struct fiemap_extent_info *fieinfo,
struct fiemap_cache *cache, struct fiemap_cache *cache,
struct extent_state **delalloc_cached_state,
struct btrfs_backref_share_check_ctx *backref_ctx, struct btrfs_backref_share_check_ctx *backref_ctx,
u64 disk_bytenr, u64 extent_offset, u64 disk_bytenr, u64 extent_offset,
u64 extent_gen, u64 extent_gen,
...@@ -3722,6 +3723,7 @@ static int fiemap_process_hole(struct btrfs_inode *inode, ...@@ -3722,6 +3723,7 @@ static int fiemap_process_hole(struct btrfs_inode *inode,
bool delalloc; bool delalloc;
delalloc = btrfs_find_delalloc_in_range(inode, cur_offset, end, delalloc = btrfs_find_delalloc_in_range(inode, cur_offset, end,
delalloc_cached_state,
&delalloc_start, &delalloc_start,
&delalloc_end); &delalloc_end);
if (!delalloc) if (!delalloc)
...@@ -3892,6 +3894,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -3892,6 +3894,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
{ {
const u64 ino = btrfs_ino(inode); const u64 ino = btrfs_ino(inode);
struct extent_state *cached_state = NULL; struct extent_state *cached_state = NULL;
struct extent_state *delalloc_cached_state = NULL;
struct btrfs_path *path; struct btrfs_path *path;
struct fiemap_cache cache = { 0 }; struct fiemap_cache cache = { 0 };
struct btrfs_backref_share_check_ctx *backref_ctx; struct btrfs_backref_share_check_ctx *backref_ctx;
...@@ -3966,6 +3969,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -3966,6 +3969,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
const u64 range_end = min(key.offset, lockend) - 1; const u64 range_end = min(key.offset, lockend) - 1;
ret = fiemap_process_hole(inode, fieinfo, &cache, ret = fiemap_process_hole(inode, fieinfo, &cache,
&delalloc_cached_state,
backref_ctx, 0, 0, 0, backref_ctx, 0, 0, 0,
prev_extent_end, range_end); prev_extent_end, range_end);
if (ret < 0) { if (ret < 0) {
...@@ -4006,6 +4010,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -4006,6 +4010,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
extent_len, flags); extent_len, flags);
} else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) { } else if (extent_type == BTRFS_FILE_EXTENT_PREALLOC) {
ret = fiemap_process_hole(inode, fieinfo, &cache, ret = fiemap_process_hole(inode, fieinfo, &cache,
&delalloc_cached_state,
backref_ctx, backref_ctx,
disk_bytenr, extent_offset, disk_bytenr, extent_offset,
extent_gen, key.offset, extent_gen, key.offset,
...@@ -4013,6 +4018,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -4013,6 +4018,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
} else if (disk_bytenr == 0) { } else if (disk_bytenr == 0) {
/* We have an explicit hole. */ /* We have an explicit hole. */
ret = fiemap_process_hole(inode, fieinfo, &cache, ret = fiemap_process_hole(inode, fieinfo, &cache,
&delalloc_cached_state,
backref_ctx, 0, 0, 0, backref_ctx, 0, 0, 0,
key.offset, extent_end - 1); key.offset, extent_end - 1);
} else { } else {
...@@ -4070,7 +4076,8 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -4070,7 +4076,8 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
path = NULL; path = NULL;
if (!stopped && prev_extent_end < lockend) { if (!stopped && prev_extent_end < lockend) {
ret = fiemap_process_hole(inode, fieinfo, &cache, backref_ctx, ret = fiemap_process_hole(inode, fieinfo, &cache,
&delalloc_cached_state, backref_ctx,
0, 0, 0, prev_extent_end, lockend - 1); 0, 0, 0, prev_extent_end, lockend - 1);
if (ret < 0) if (ret < 0)
goto out_unlock; goto out_unlock;
...@@ -4088,6 +4095,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -4088,6 +4095,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
delalloc = btrfs_find_delalloc_in_range(inode, delalloc = btrfs_find_delalloc_in_range(inode,
prev_extent_end, prev_extent_end,
i_size - 1, i_size - 1,
&delalloc_cached_state,
&delalloc_start, &delalloc_start,
&delalloc_end); &delalloc_end);
if (!delalloc) if (!delalloc)
...@@ -4102,6 +4110,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -4102,6 +4110,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
out_unlock: out_unlock:
unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state); unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state);
out: out:
free_extent_state(delalloc_cached_state);
btrfs_free_backref_share_ctx(backref_ctx); btrfs_free_backref_share_ctx(backref_ctx);
btrfs_free_path(path); btrfs_free_path(path);
return ret; return ret;
......
...@@ -3214,6 +3214,7 @@ static long btrfs_fallocate(struct file *file, int mode, ...@@ -3214,6 +3214,7 @@ static long btrfs_fallocate(struct file *file, int mode,
* looping while it gets adjacent subranges, and merging them together. * looping while it gets adjacent subranges, and merging them together.
*/ */
static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end, static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end,
struct extent_state **cached_state,
bool *search_io_tree, bool *search_io_tree,
u64 *delalloc_start_ret, u64 *delalloc_end_ret) u64 *delalloc_start_ret, u64 *delalloc_end_ret)
{ {
...@@ -3236,7 +3237,7 @@ static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end ...@@ -3236,7 +3237,7 @@ static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end
delalloc_len = count_range_bits(&inode->io_tree, delalloc_len = count_range_bits(&inode->io_tree,
delalloc_start_ret, end, delalloc_start_ret, end,
len, EXTENT_DELALLOC, 1, len, EXTENT_DELALLOC, 1,
NULL); cached_state);
} else { } else {
spin_unlock(&inode->lock); spin_unlock(&inode->lock);
} }
...@@ -3324,6 +3325,8 @@ static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end ...@@ -3324,6 +3325,8 @@ static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end
* sector size aligned. * sector size aligned.
* @end: The end offset (inclusive value) of the search range. * @end: The end offset (inclusive value) of the search range.
* It does not need to be sector size aligned. * It does not need to be sector size aligned.
* @cached_state: Extent state record used for speeding up delalloc
* searches in the inode's io_tree. Can be NULL.
* @delalloc_start_ret: Output argument, set to the start offset of the * @delalloc_start_ret: Output argument, set to the start offset of the
* subrange found with delalloc (may not be sector size * subrange found with delalloc (may not be sector size
* aligned). * aligned).
...@@ -3335,6 +3338,7 @@ static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end ...@@ -3335,6 +3338,7 @@ static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end
* end offsets of the subrange. * end offsets of the subrange.
*/ */
bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end, bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end,
struct extent_state **cached_state,
u64 *delalloc_start_ret, u64 *delalloc_end_ret) u64 *delalloc_start_ret, u64 *delalloc_end_ret)
{ {
u64 cur_offset = round_down(start, inode->root->fs_info->sectorsize); u64 cur_offset = round_down(start, inode->root->fs_info->sectorsize);
...@@ -3348,7 +3352,7 @@ bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end, ...@@ -3348,7 +3352,7 @@ bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end,
bool delalloc; bool delalloc;
delalloc = find_delalloc_subrange(inode, cur_offset, end, delalloc = find_delalloc_subrange(inode, cur_offset, end,
&search_io_tree, cached_state, &search_io_tree,
&delalloc_start, &delalloc_start,
&delalloc_end); &delalloc_end);
if (!delalloc) if (!delalloc)
...@@ -3400,7 +3404,7 @@ static bool find_desired_extent_in_hole(struct btrfs_inode *inode, int whence, ...@@ -3400,7 +3404,7 @@ static bool find_desired_extent_in_hole(struct btrfs_inode *inode, int whence,
u64 delalloc_end; u64 delalloc_end;
bool delalloc; bool delalloc;
delalloc = btrfs_find_delalloc_in_range(inode, start, end, delalloc = btrfs_find_delalloc_in_range(inode, start, end, NULL,
&delalloc_start, &delalloc_end); &delalloc_start, &delalloc_end);
if (delalloc && whence == SEEK_DATA) { if (delalloc && whence == SEEK_DATA) {
*start_ret = delalloc_start; *start_ret = delalloc_start;
......
...@@ -27,6 +27,7 @@ int btrfs_check_nocow_lock(struct btrfs_inode *inode, loff_t pos, ...@@ -27,6 +27,7 @@ int btrfs_check_nocow_lock(struct btrfs_inode *inode, loff_t pos,
size_t *write_bytes, bool nowait); size_t *write_bytes, bool nowait);
void btrfs_check_nocow_unlock(struct btrfs_inode *inode); void btrfs_check_nocow_unlock(struct btrfs_inode *inode);
bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end, bool btrfs_find_delalloc_in_range(struct btrfs_inode *inode, u64 start, u64 end,
struct extent_state **cached_state,
u64 *delalloc_start_ret, u64 *delalloc_end_ret); u64 *delalloc_start_ret, u64 *delalloc_end_ret);
#endif #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment