Commit 40daf3e0 authored by Filipe Manana's avatar Filipe Manana Committed by David Sterba

btrfs: add an early exit when searching for delalloc range for lseek/fiemap

During fiemap and lseek (SEEK_HOLE/DATA), when looking for delalloc in a
range corresponding to a hole or a prealloc extent, if we found the whole
range marked as delalloc in the inode's io_tree, then we can terminate
immediately and avoid searching the extent map tree. If not, and if the
found delalloc starts at the same offset of our search start but ends
before our search range's end, then we can adjust the search range for
the search in the extent map tree. So implement those changes.

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 2c8f5e8c
...@@ -3216,7 +3216,7 @@ static long btrfs_fallocate(struct file *file, int mode, ...@@ -3216,7 +3216,7 @@ static long btrfs_fallocate(struct file *file, int mode,
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,
u64 *delalloc_start_ret, u64 *delalloc_end_ret) u64 *delalloc_start_ret, u64 *delalloc_end_ret)
{ {
const u64 len = end + 1 - start; u64 len = end + 1 - start;
struct extent_map_tree *em_tree = &inode->extent_tree; struct extent_map_tree *em_tree = &inode->extent_tree;
struct extent_map *em; struct extent_map *em;
u64 em_end; u64 em_end;
...@@ -3242,13 +3242,23 @@ static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end ...@@ -3242,13 +3242,23 @@ static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end
delalloc_len = 0; delalloc_len = 0;
} }
/* if (delalloc_len > 0) {
* If delalloc was found then *delalloc_start_ret has a sector size /*
* aligned value (rounded down). * If delalloc was found then *delalloc_start_ret has a sector size
*/ * aligned value (rounded down).
if (delalloc_len > 0) */
*delalloc_end_ret = *delalloc_start_ret + delalloc_len - 1; *delalloc_end_ret = *delalloc_start_ret + delalloc_len - 1;
if (*delalloc_start_ret == start) {
/* Delalloc for the whole range, nothing more to do. */
if (*delalloc_end_ret == end)
return true;
/* Else trim our search range for extent maps. */
start = *delalloc_end_ret + 1;
len = end + 1 - start;
}
}
/* /*
* No outstanding extents means we don't have any delalloc that is * No outstanding extents means we don't have any delalloc that is
* flushing, so return the unflushed range found in the io tree (if any). * flushing, so return the unflushed range found in the io tree (if any).
......
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