Commit 42daec29 authored by Chris Mason's avatar Chris Mason

Btrfs: fix errors handling cached state in set/clear_extent_bit

Both set and clear_extent_bit allow passing a cached
state struct to reduce rbtree search times.  clear_extent_bit
was improperly bypassing some of the checks around making sure
the extent state fields were correct for a given operation.

The fix used here (from Yan Zheng) is to use the hit_next
goto target instead of jumping all the way down to start clearing
bits without making sure the cached state was exactly correct
for the operation we were doing.

This also fixes up the setting of the start variable for both
ops in the case where we find an overlapping extent that
begins before the range we want to change.  In both cases
we were incorrectly going backwards from the original
requested change.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 7ce618db
...@@ -495,11 +495,11 @@ again: ...@@ -495,11 +495,11 @@ again:
if (cached_state) { if (cached_state) {
cached = *cached_state; cached = *cached_state;
*cached_state = NULL; *cached_state = NULL;
if (cached->tree && cached->start == start) { cached_state = NULL;
if (cached && cached->tree && cached->start == start) {
atomic_dec(&cached->refs); atomic_dec(&cached->refs);
state = cached; state = cached;
last_end = state->end; goto hit_next;
goto found;
} }
free_extent_state(cached); free_extent_state(cached);
} }
...@@ -547,8 +547,6 @@ hit_next: ...@@ -547,8 +547,6 @@ hit_next:
if (last_end == (u64)-1) if (last_end == (u64)-1)
goto out; goto out;
start = last_end + 1; start = last_end + 1;
} else {
start = state->start;
} }
goto search_again; goto search_again;
} }
...@@ -566,16 +564,18 @@ hit_next: ...@@ -566,16 +564,18 @@ hit_next:
if (wake) if (wake)
wake_up(&state->wq); wake_up(&state->wq);
set |= clear_state_bit(tree, prealloc, bits, set |= clear_state_bit(tree, prealloc, bits,
wake, delete); wake, delete);
prealloc = NULL; prealloc = NULL;
goto out; goto out;
} }
found:
if (state->end < end && prealloc && !need_resched()) if (state->end < end && prealloc && !need_resched())
next_node = rb_next(&state->rb_node); next_node = rb_next(&state->rb_node);
else else
next_node = NULL; next_node = NULL;
set |= clear_state_bit(tree, state, bits, wake, delete); set |= clear_state_bit(tree, state, bits, wake, delete);
if (last_end == (u64)-1) if (last_end == (u64)-1)
goto out; goto out;
...@@ -712,6 +712,7 @@ static int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -712,6 +712,7 @@ static int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
int err = 0; int err = 0;
u64 last_start; u64 last_start;
u64 last_end; u64 last_end;
again: again:
if (!prealloc && (mask & __GFP_WAIT)) { if (!prealloc && (mask & __GFP_WAIT)) {
prealloc = alloc_extent_state(mask); prealloc = alloc_extent_state(mask);
...@@ -756,6 +757,7 @@ hit_next: ...@@ -756,6 +757,7 @@ hit_next:
err = -EEXIST; err = -EEXIST;
goto out; goto out;
} }
set_state_bits(tree, state, bits); set_state_bits(tree, state, bits);
cache_state(state, cached_state); cache_state(state, cached_state);
merge_state(tree, state); merge_state(tree, state);
...@@ -809,8 +811,6 @@ hit_next: ...@@ -809,8 +811,6 @@ hit_next:
if (last_end == (u64)-1) if (last_end == (u64)-1)
goto out; goto out;
start = last_end + 1; start = last_end + 1;
} else {
start = state->start;
} }
goto search_again; goto search_again;
} }
......
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