Commit 1eaebb34 authored by Nikolay Borisov's avatar Nikolay Borisov Committed by David Sterba

btrfs: Don't trim returned range based on input value in find_first_clear_extent_bit

Currently find_first_clear_extent_bit always returns a range whose
starting value is >= passed 'start'. This implicit trimming behavior is
somewhat subtle and an implementation detail.

Instead, this patch modifies the function such that now it always
returns the range which contains passed 'start' and has the given bits
unset. This range could either be due to presence of existing records
which contains 'start' but have the bits unset or because there are no
records that contain the given starting offset.

This patch also adds test cases which cover find_first_clear_extent_bit
since they were missing up until now.
Reviewed-by: default avatarQu Wenruo <wqu@suse.com>
Signed-off-by: default avatarNikolay Borisov <nborisov@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 53460a45
...@@ -1537,8 +1537,8 @@ int find_first_extent_bit(struct extent_io_tree *tree, u64 start, ...@@ -1537,8 +1537,8 @@ int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
} }
/** /**
* find_first_clear_extent_bit - finds the first range that has @bits not set * find_first_clear_extent_bit - find the first range that has @bits not set.
* and that starts after @start * This range could start before @start.
* *
* @tree - the tree to search * @tree - the tree to search
* @start - the offset at/after which the found extent should start * @start - the offset at/after which the found extent should start
...@@ -1578,12 +1578,52 @@ void find_first_clear_extent_bit(struct extent_io_tree *tree, u64 start, ...@@ -1578,12 +1578,52 @@ void find_first_clear_extent_bit(struct extent_io_tree *tree, u64 start,
goto out; goto out;
} }
} }
/*
* At this point 'node' either contains 'start' or start is
* before 'node'
*/
state = rb_entry(node, struct extent_state, rb_node); state = rb_entry(node, struct extent_state, rb_node);
if (in_range(start, state->start, state->end - state->start + 1) &&
(state->state & bits)) { if (in_range(start, state->start, state->end - state->start + 1)) {
if (state->state & bits) {
/*
* |--range with bits sets--|
* |
* start
*/
start = state->end + 1; start = state->end + 1;
} else { } else {
*start_ret = start; /*
* 'start' falls within a range that doesn't
* have the bits set, so take its start as
* the beginning of the desired range
*
* |--range with bits cleared----|
* |
* start
*/
*start_ret = state->start;
break;
}
} else {
/*
* |---prev range---|---hole/unset---|---node range---|
* |
* start
*
* or
*
* |---hole/unset--||--first node--|
* 0 |
* start
*/
if (prev) {
state = rb_entry(prev, struct extent_state,
rb_node);
*start_ret = state->end + 1;
} else {
*start_ret = 0;
}
break; break;
} }
} }
......
...@@ -432,6 +432,89 @@ static int test_eb_bitmaps(u32 sectorsize, u32 nodesize) ...@@ -432,6 +432,89 @@ static int test_eb_bitmaps(u32 sectorsize, u32 nodesize)
return ret; return ret;
} }
static int test_find_first_clear_extent_bit(void)
{
struct extent_io_tree tree;
u64 start, end;
test_msg("running find_first_clear_extent_bit test");
extent_io_tree_init(NULL, &tree, IO_TREE_SELFTEST, NULL);
/*
* Set 1M-4M alloc/discard and 32M-64M thus leaving a hole between
* 4M-32M
*/
set_extent_bits(&tree, SZ_1M, SZ_4M - 1,
CHUNK_TRIMMED | CHUNK_ALLOCATED);
find_first_clear_extent_bit(&tree, SZ_512K, &start, &end,
CHUNK_TRIMMED | CHUNK_ALLOCATED);
if (start != 0 || end != SZ_1M -1)
test_err("error finding beginning range: start %llu end %llu",
start, end);
/* Now add 32M-64M so that we have a hole between 4M-32M */
set_extent_bits(&tree, SZ_32M, SZ_64M - 1,
CHUNK_TRIMMED | CHUNK_ALLOCATED);
/*
* Request first hole starting at 12M, we should get 4M-32M
*/
find_first_clear_extent_bit(&tree, 12 * SZ_1M, &start, &end,
CHUNK_TRIMMED | CHUNK_ALLOCATED);
if (start != SZ_4M || end != SZ_32M - 1)
test_err("error finding trimmed range: start %llu end %llu",
start, end);
/*
* Search in the middle of allocated range, should get the next one
* available, which happens to be unallocated -> 4M-32M
*/
find_first_clear_extent_bit(&tree, SZ_2M, &start, &end,
CHUNK_TRIMMED | CHUNK_ALLOCATED);
if (start != SZ_4M || end != SZ_32M -1)
test_err("error finding next unalloc range: start %llu end %llu",
start, end);
/*
* Set 64M-72M with CHUNK_ALLOC flag, then search for CHUNK_TRIMMED flag
* being unset in this range, we should get the entry in range 64M-72M
*/
set_extent_bits(&tree, SZ_64M, SZ_64M + SZ_8M - 1, CHUNK_ALLOCATED);
find_first_clear_extent_bit(&tree, SZ_64M + SZ_1M, &start, &end,
CHUNK_TRIMMED);
if (start != SZ_64M || end != SZ_64M + SZ_8M - 1)
test_err("error finding exact range: start %llu end %llu",
start, end);
find_first_clear_extent_bit(&tree, SZ_64M - SZ_8M, &start, &end,
CHUNK_TRIMMED);
/*
* Search in the middle of set range whose immediate neighbour doesn't
* have the bits set so it must be returned
*/
if (start != SZ_64M || end != SZ_64M + SZ_8M - 1)
test_err("error finding next alloc range: start %llu end %llu",
start, end);
/*
* Search beyond any known range, shall return after last known range
* and end should be -1
*/
find_first_clear_extent_bit(&tree, -1, &start, &end, CHUNK_TRIMMED);
if (start != SZ_64M + SZ_8M || end != -1)
test_err(
"error handling beyond end of range search: start %llu end %llu",
start, end);
return 0;
}
int btrfs_test_extent_io(u32 sectorsize, u32 nodesize) int btrfs_test_extent_io(u32 sectorsize, u32 nodesize)
{ {
int ret; int ret;
...@@ -442,6 +525,10 @@ int btrfs_test_extent_io(u32 sectorsize, u32 nodesize) ...@@ -442,6 +525,10 @@ int btrfs_test_extent_io(u32 sectorsize, u32 nodesize)
if (ret) if (ret)
goto out; goto out;
ret = test_find_first_clear_extent_bit();
if (ret)
goto out;
ret = test_eb_bitmaps(sectorsize, nodesize); ret = test_eb_bitmaps(sectorsize, nodesize);
out: out:
return ret; return ret;
......
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