Skip to content

Commit

Permalink
Btrfs: fix how we merge extent states and deal with cached states
Browse files Browse the repository at this point in the history
First, we can sometimes free the state we're merging, which means anybody who
calls merge_state() may have the state it passed in free'ed.  This is
problematic because we could end up caching the state, which makes caching
useless as the state will no longer be part of the tree.  So instead of free'ing
the state we passed into merge_state(), set it's end to the other->end and free
the other state.  This way we are sure to cache the correct state.  Also because
we can merge states together, instead of only using the cache'd state if it's
start == the start we are looking for, go ahead and use it if the start we are
looking for is within the range of the cached state.  Thanks,

Signed-off-by: Josef Bacik <josef@redhat.com>
  • Loading branch information
Josef Bacik committed Jul 11, 2011
1 parent 2f35612 commit df98b6e
Showing 1 changed file with 11 additions and 12 deletions.
23 changes: 11 additions & 12 deletions fs/btrfs/extent_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,10 @@ static int merge_state(struct extent_io_tree *tree,
if (other->start == state->end + 1 &&
other->state == state->state) {
merge_cb(tree, state, other);
other->start = state->start;
state->tree = NULL;
rb_erase(&state->rb_node, &tree->state);
free_extent_state(state);
state = NULL;
state->end = other->end;
other->tree = NULL;
rb_erase(&other->rb_node, &tree->state);
free_extent_state(other);
}
}

Expand Down Expand Up @@ -349,7 +348,6 @@ static int insert_state(struct extent_io_tree *tree,
"%llu %llu\n", (unsigned long long)found->start,
(unsigned long long)found->end,
(unsigned long long)start, (unsigned long long)end);
free_extent_state(state);
return -EEXIST;
}
state->tree = tree;
Expand Down Expand Up @@ -498,7 +496,8 @@ int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
cached_state = NULL;
}

if (cached && cached->tree && cached->start == start) {
if (cached && cached->tree && cached->start <= start &&
cached->end > start) {
if (clear)
atomic_dec(&cached->refs);
state = cached;
Expand Down Expand Up @@ -740,7 +739,8 @@ int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
spin_lock(&tree->lock);
if (cached_state && *cached_state) {
state = *cached_state;
if (state->start == start && state->tree) {
if (state->start <= start && state->end > start &&
state->tree) {
node = &state->rb_node;
goto hit_next;
}
Expand Down Expand Up @@ -781,13 +781,13 @@ int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
if (err)
goto out;

next_node = rb_next(node);
cache_state(state, cached_state);
merge_state(tree, state);
if (last_end == (u64)-1)
goto out;

start = last_end + 1;
next_node = rb_next(&state->rb_node);
if (next_node && start < end && prealloc && !need_resched()) {
state = rb_entry(next_node, struct extent_state,
rb_node);
Expand Down Expand Up @@ -860,7 +860,6 @@ int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
* Avoid to free 'prealloc' if it can be merged with
* the later extent.
*/
atomic_inc(&prealloc->refs);
err = insert_state(tree, prealloc, start, this_end,
&bits);
BUG_ON(err == -EEXIST);
Expand All @@ -870,7 +869,6 @@ int set_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
goto out;
}
cache_state(prealloc, cached_state);
free_extent_state(prealloc);
prealloc = NULL;
start = this_end + 1;
goto search_again;
Expand Down Expand Up @@ -1562,7 +1560,8 @@ int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end,
int bitset = 0;

spin_lock(&tree->lock);
if (cached && cached->tree && cached->start == start)
if (cached && cached->tree && cached->start <= start &&
cached->end > start)
node = &cached->rb_node;
else
node = tree_search(tree, start);
Expand Down

0 comments on commit df98b6e

Please sign in to comment.