Skip to content

Commit 5750c37

Browse files
lorddoskiaskdave
authored andcommitted
btrfs: Correctly handle empty trees in find_first_clear_extent_bit
Raviu reported that running his regular fs_trim segfaulted with the following backtrace: [ 237.525947] assertion failed: prev, in ../fs/btrfs/extent_io.c:1595 [ 237.525984] ------------[ cut here ]------------ [ 237.525985] kernel BUG at ../fs/btrfs/ctree.h:3117! [ 237.525992] invalid opcode: 0000 [#1] SMP PTI [ 237.525998] CPU: 4 PID: 4423 Comm: fstrim Tainted: G U OE 5.4.14-8-vanilla #1 [ 237.526001] Hardware name: ASUSTeK COMPUTER INC. [ 237.526044] RIP: 0010:assfail.constprop.58+0x18/0x1a [btrfs] [ 237.526079] Call Trace: [ 237.526120] find_first_clear_extent_bit+0x13d/0x150 [btrfs] [ 237.526148] btrfs_trim_fs+0x211/0x3f0 [btrfs] [ 237.526184] btrfs_ioctl_fitrim+0x103/0x170 [btrfs] [ 237.526219] btrfs_ioctl+0x129a/0x2ed0 [btrfs] [ 237.526227] ? filemap_map_pages+0x190/0x3d0 [ 237.526232] ? do_filp_open+0xaf/0x110 [ 237.526238] ? _copy_to_user+0x22/0x30 [ 237.526242] ? cp_new_stat+0x150/0x180 [ 237.526247] ? do_vfs_ioctl+0xa4/0x640 [ 237.526278] ? btrfs_ioctl_get_supported_features+0x30/0x30 [btrfs] [ 237.526283] do_vfs_ioctl+0xa4/0x640 [ 237.526288] ? __do_sys_newfstat+0x3c/0x60 [ 237.526292] ksys_ioctl+0x70/0x80 [ 237.526297] __x64_sys_ioctl+0x16/0x20 [ 237.526303] do_syscall_64+0x5a/0x1c0 [ 237.526310] entry_SYSCALL_64_after_hwframe+0x49/0xbe That was due to btrfs_fs_device::aloc_tree being empty. Initially I thought this wasn't possible and as a percaution have put the assert in find_first_clear_extent_bit. Turns out this is indeed possible and could happen when a file system with SINGLE data/metadata profile has a 2nd device added. Until balance is run or a new chunk is allocated on this device it will be completely empty. In this case find_first_clear_extent_bit should return the full range [0, -1ULL] and let the caller handle this i.e for trim the end will be capped at the size of actual device. Link: https://lore.kernel.org/linux-btrfs/izW2WNyvy1dEDweBICizKnd2KDwDiDyY2EYQr4YCwk7pkuIpthx-JRn65MPBde00ND6V0_Lh8mW0kZwzDiLDv25pUYWxkskWNJnVP0kgdMA=@protonmail.com/ Fixes: 45bfcfc ("btrfs: Implement find_first_clear_extent_bit") CC: [email protected] # 5.2+ Signed-off-by: Nikolay Borisov <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 42ffb0b commit 5750c37

File tree

2 files changed

+27
-14
lines changed

2 files changed

+27
-14
lines changed

fs/btrfs/extent_io.c

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,21 +1593,25 @@ void find_first_clear_extent_bit(struct extent_io_tree *tree, u64 start,
15931593
/* Find first extent with bits cleared */
15941594
while (1) {
15951595
node = __etree_search(tree, start, &next, &prev, NULL, NULL);
1596-
if (!node) {
1596+
if (!node && !next && !prev) {
1597+
/*
1598+
* Tree is completely empty, send full range and let
1599+
* caller deal with it
1600+
*/
1601+
*start_ret = 0;
1602+
*end_ret = -1;
1603+
goto out;
1604+
} else if (!node && !next) {
1605+
/*
1606+
* We are past the last allocated chunk, set start at
1607+
* the end of the last extent.
1608+
*/
1609+
state = rb_entry(prev, struct extent_state, rb_node);
1610+
*start_ret = state->end + 1;
1611+
*end_ret = -1;
1612+
goto out;
1613+
} else if (!node) {
15971614
node = next;
1598-
if (!node) {
1599-
/*
1600-
* We are past the last allocated chunk,
1601-
* set start at the end of the last extent. The
1602-
* device alloc tree should never be empty so
1603-
* prev is always set.
1604-
*/
1605-
ASSERT(prev);
1606-
state = rb_entry(prev, struct extent_state, rb_node);
1607-
*start_ret = state->end + 1;
1608-
*end_ret = -1;
1609-
goto out;
1610-
}
16111615
}
16121616
/*
16131617
* At this point 'node' either contains 'start' or start is

fs/btrfs/tests/extent-io-tests.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,8 +441,17 @@ static int test_find_first_clear_extent_bit(void)
441441
int ret = -EINVAL;
442442

443443
test_msg("running find_first_clear_extent_bit test");
444+
444445
extent_io_tree_init(NULL, &tree, IO_TREE_SELFTEST, NULL);
445446

447+
/* Test correct handling of empty tree */
448+
find_first_clear_extent_bit(&tree, 0, &start, &end, CHUNK_TRIMMED);
449+
if (start != 0 || end != -1) {
450+
test_err(
451+
"error getting a range from completely empty tree: start %llu end %llu",
452+
start, end);
453+
goto out;
454+
}
446455
/*
447456
* Set 1M-4M alloc/discard and 32M-64M thus leaving a hole between
448457
* 4M-32M

0 commit comments

Comments
 (0)