btrfs-progs: Avoid nested chunk allocation call

There is a indirect recursion which can reach the extent reservation:

btrfs_reserve_extent()             <--|
|- do_chunk_alloc()                   |
   |- btrfs_alloc_chunk()             |
      |- btrfs_insert_item()          |
	 |- btrfs_reserve_extent() <--|

Currently, we're using root->ref_cows to determine whether we should do
chunk prealloc to avoid such loop.

But that's still a hidden trap. Instead of solving it using some hidden
tricks, this patch will make chunk/block group allocation exclusive.

Now if do_chunk_alloc() determines to alloc chunk, it will set a flag in
transaction handle so new call of do_chunk_alloc() will refuse to
allocate new chunk until current chunk allocation finishes.

The chunks get over-allocated by 2M so there's enough space in case the
recursive call asks for a different type of blockgroup.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
master
Qu Wenruo 2019-04-16 18:21:43 +08:00 committed by David Sterba
parent ea4d99157c
commit 922a631d50
3 changed files with 13 additions and 0 deletions

View File

@ -1717,10 +1717,20 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
(flags & BTRFS_BLOCK_GROUP_SYSTEM))
return 0;
/*
* We're going to allocate new chunk, during the process, we will
* allocate new tree blocks, which can trigger new chunk allocation
* again. Avoid the recursion.
*/
if (trans->allocating_chunk)
return 0;
trans->allocating_chunk = 1;
ret = btrfs_alloc_chunk(trans, fs_info, &start, &num_bytes,
space_info->flags);
if (ret == -ENOSPC) {
space_info->full = 1;
trans->allocating_chunk = 0;
return 0;
}
@ -1729,6 +1739,7 @@ static int do_chunk_alloc(struct btrfs_trans_handle *trans,
ret = btrfs_make_block_group(trans, fs_info, 0, space_info->flags,
start, num_bytes);
BUG_ON(ret);
trans->allocating_chunk = 0;
return 0;
}

View File

@ -48,6 +48,7 @@ struct btrfs_trans_handle* btrfs_start_transaction(struct btrfs_root *root,
h->transid = fs_info->generation;
h->blocks_reserved = num_blocks;
h->reinit_extent_tree = false;
h->allocating_chunk = 0;
root->last_trans = h->transid;
root->commit_root = root->node;
extent_buffer_get(root->node);

View File

@ -29,6 +29,7 @@ struct btrfs_trans_handle {
u64 alloc_exclude_start;
u64 alloc_exclude_nr;
bool reinit_extent_tree;
unsigned int allocating_chunk:1;
u64 delayed_ref_updates;
unsigned long blocks_reserved;
unsigned long blocks_used;