]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
btrfs: make sure we cache next state in find_first_extent_bit()
authorFilipe Manana <fdmanana@suse.com>
Fri, 22 Sep 2023 10:39:09 +0000 (11:39 +0100)
committerDavid Sterba <dsterba@suse.com>
Thu, 12 Oct 2023 14:44:15 +0000 (16:44 +0200)
Currently, at find_first_extent_bit(), when we are given a cached extent
state that happens to have its end offset match the desired range start,
we find the next extent state using that cached state, with next_state()
calls, and then return it.

We then try to cache that next state by calling cache_state_if_flags(),
but that will not cache the state because we haven't reset *cached_state
to NULL, so we end up with the cached_state unchanged, and if the caller
is iterating over extent states in the io tree, its next call to
find_first_extent_bit() will not use the current cached state as its end
offset does not match the minimum start range offset, therefore the cached
state is reset and we have to search the rbtree to find the next suitable
extent state record.

So fix this by resetting the cached state to NULL (and dropping our ref
on it) when we have a suitable cached state and we found a next state by
using next_state() starting from the cached state. This makes use cases
of calling find_first_extent_bit() to go over all ranges in the io tree
to do a single rbtree full search, only on the first call, and the next
calls will just do next_state() (rb_next() wrapper) calls, which is more
efficient.

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent-io-tree.c

index f9f7cf028ffbd0e86d7ecc60e9ab7eb9b929a8d1..ea149be28dffeea58f2e435410979c3dc526e3a5 100644 (file)
@@ -885,10 +885,19 @@ bool find_first_extent_bit(struct extent_io_tree *tree, u64 start,
                if (state->end == start - 1 && extent_state_in_tree(state)) {
                        while ((state = next_state(state)) != NULL) {
                                if (state->state & bits)
-                                       goto got_it;
+                                       break;
                        }
+                       /*
+                        * If we found the next extent state, clear cached_state
+                        * so that we can cache the next extent state below and
+                        * avoid future calls going over the same extent state
+                        * again. If we haven't found any, clear as well since
+                        * it's now useless.
+                        */
                        free_extent_state(*cached_state);
                        *cached_state = NULL;
+                       if (state)
+                               goto got_it;
                        goto out;
                }
                free_extent_state(*cached_state);