mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 12:28:41 +08:00
btrfs: get the next extent map during fiemap/lseek more efficiently
commit d47704bd1c
upstream.
At find_delalloc_subrange(), when we need to get the next extent map, we
do a full search on the extent map tree (a red black tree). This is fine
but it's a lot more efficient to simply use rb_next(), which typically
requires iterating over less nodes of the tree and never needs to compare
the ranges of nodes with the one we are looking for.
So add a public helper to extent_map.{h,c} to get the extent map that
immediately follows another extent map, using rb_next(), and use that
helper at find_delalloc_subrange().
Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
b28def6ed9
commit
e19ebc5f9a
@ -523,7 +523,7 @@ void replace_extent_mapping(struct extent_map_tree *tree,
|
||||
setup_extent_mapping(tree, new, modified);
|
||||
}
|
||||
|
||||
static struct extent_map *next_extent_map(struct extent_map *em)
|
||||
static struct extent_map *next_extent_map(const struct extent_map *em)
|
||||
{
|
||||
struct rb_node *next;
|
||||
|
||||
@ -533,6 +533,35 @@ static struct extent_map *next_extent_map(struct extent_map *em)
|
||||
return container_of(next, struct extent_map, rb_node);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the extent map that immediately follows another one.
|
||||
*
|
||||
* @tree: The extent map tree that the extent map belong to.
|
||||
* Holding read or write access on the tree's lock is required.
|
||||
* @em: An extent map from the given tree. The caller must ensure that
|
||||
* between getting @em and between calling this function, the
|
||||
* extent map @em is not removed from the tree - for example, by
|
||||
* holding the tree's lock for the duration of those 2 operations.
|
||||
*
|
||||
* Returns the extent map that immediately follows @em, or NULL if @em is the
|
||||
* last extent map in the tree.
|
||||
*/
|
||||
struct extent_map *btrfs_next_extent_map(const struct extent_map_tree *tree,
|
||||
const struct extent_map *em)
|
||||
{
|
||||
struct extent_map *next;
|
||||
|
||||
/* The lock must be acquired either in read mode or write mode. */
|
||||
lockdep_assert_held(&tree->lock);
|
||||
ASSERT(extent_map_in_tree(em));
|
||||
|
||||
next = next_extent_map(em);
|
||||
if (next)
|
||||
refcount_inc(&next->refs);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
static struct extent_map *prev_extent_map(struct extent_map *em)
|
||||
{
|
||||
struct rb_node *prev;
|
||||
|
@ -87,6 +87,8 @@ static inline u64 extent_map_block_end(struct extent_map *em)
|
||||
void extent_map_tree_init(struct extent_map_tree *tree);
|
||||
struct extent_map *lookup_extent_mapping(struct extent_map_tree *tree,
|
||||
u64 start, u64 len);
|
||||
struct extent_map *btrfs_next_extent_map(const struct extent_map_tree *tree,
|
||||
const struct extent_map *em);
|
||||
int add_extent_mapping(struct extent_map_tree *tree,
|
||||
struct extent_map *em, int modified);
|
||||
void remove_extent_mapping(struct extent_map_tree *tree, struct extent_map *em);
|
||||
|
@ -3248,40 +3248,50 @@ static bool find_delalloc_subrange(struct btrfs_inode *inode, u64 start, u64 end
|
||||
*/
|
||||
read_lock(&em_tree->lock);
|
||||
em = lookup_extent_mapping(em_tree, start, len);
|
||||
read_unlock(&em_tree->lock);
|
||||
if (!em) {
|
||||
read_unlock(&em_tree->lock);
|
||||
return (delalloc_len > 0);
|
||||
}
|
||||
|
||||
/* extent_map_end() returns a non-inclusive end offset. */
|
||||
em_end = em ? extent_map_end(em) : 0;
|
||||
em_end = extent_map_end(em);
|
||||
|
||||
/*
|
||||
* If we have a hole/prealloc extent map, check the next one if this one
|
||||
* ends before our range's end.
|
||||
*/
|
||||
if (em && (em->block_start == EXTENT_MAP_HOLE ||
|
||||
test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) && em_end < end) {
|
||||
if ((em->block_start == EXTENT_MAP_HOLE ||
|
||||
test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) && em_end < end) {
|
||||
struct extent_map *next_em;
|
||||
|
||||
read_lock(&em_tree->lock);
|
||||
next_em = lookup_extent_mapping(em_tree, em_end, len - em_end);
|
||||
read_unlock(&em_tree->lock);
|
||||
|
||||
next_em = btrfs_next_extent_map(em_tree, em);
|
||||
free_extent_map(em);
|
||||
em_end = next_em ? extent_map_end(next_em) : 0;
|
||||
|
||||
/*
|
||||
* There's no next extent map or the next one starts beyond our
|
||||
* range, return the range found in the io tree (if any).
|
||||
*/
|
||||
if (!next_em || next_em->start > end) {
|
||||
read_unlock(&em_tree->lock);
|
||||
free_extent_map(next_em);
|
||||
return (delalloc_len > 0);
|
||||
}
|
||||
|
||||
em_end = extent_map_end(next_em);
|
||||
em = next_em;
|
||||
}
|
||||
|
||||
if (em && (em->block_start == EXTENT_MAP_HOLE ||
|
||||
test_bit(EXTENT_FLAG_PREALLOC, &em->flags))) {
|
||||
free_extent_map(em);
|
||||
em = NULL;
|
||||
}
|
||||
read_unlock(&em_tree->lock);
|
||||
|
||||
/*
|
||||
* No extent map or one for a hole or prealloc extent. Use the delalloc
|
||||
* range we found in the io tree if we have one.
|
||||
* We have a hole or prealloc extent that ends at or beyond our range's
|
||||
* end, return the range found in the io tree (if any).
|
||||
*/
|
||||
if (!em)
|
||||
if (em->block_start == EXTENT_MAP_HOLE ||
|
||||
test_bit(EXTENT_FLAG_PREALLOC, &em->flags)) {
|
||||
free_extent_map(em);
|
||||
return (delalloc_len > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't have any range as EXTENT_DELALLOC in the io tree, so the
|
||||
|
Loading…
Reference in New Issue
Block a user