On Tue, Apr 16, 2019 at 06:21:43PM +0800, Qu Wenruo wrote: > There is a hidden call loop which can trigger itself: > > 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 special > flag in transaction handler so it new do_chunk_alloc() will refuse to > allocate new chunk until current chunk allocation finishes. > > Signed-off-by: Qu Wenruo <w...@suse.com> > --- > check/main.c | 2 +- > extent-tree.c | 12 ++++++++++++ > transaction.c | 3 ++- > transaction.h | 3 ++- > 4 files changed, 17 insertions(+), 3 deletions(-) > > diff --git a/check/main.c b/check/main.c > index 683c322eba6f..121be4b73c4f 100644 > --- a/check/main.c > +++ b/check/main.c > @@ -10185,7 +10185,7 @@ int cmd_check(int argc, char **argv) > goto close_out; > } > > - trans->reinit_extent_tree = true; > + trans->reinit_extent_tree = 1; > if (init_extent_tree) { > printf("Creating a new extent tree\n"); > ret = reinit_extent_tree(trans, info, > diff --git a/extent-tree.c b/extent-tree.c > index 8c9cdeff3b02..e25ddf02e5cc 100644 > --- a/extent-tree.c > +++ b/extent-tree.c > @@ -1873,10 +1873,21 @@ 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 such loop call > + */ > + 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; > } > > @@ -1885,6 +1896,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; > } > > diff --git a/transaction.c b/transaction.c > index 3a63988b0969..21377282f806 100644 > --- a/transaction.c > +++ b/transaction.c > @@ -46,7 +46,8 @@ struct btrfs_trans_handle* btrfs_start_transaction(struct > btrfs_root *root, > fs_info->generation++; > h->transid = fs_info->generation; > h->blocks_reserved = num_blocks; > - h->reinit_extent_tree = false; > + h->reinit_extent_tree = 0; > + h->allocating_chunk = 0; > root->last_trans = h->transid; > root->commit_root = root->node; > extent_buffer_get(root->node); > diff --git a/transaction.h b/transaction.h > index 34060252dd5c..b426cd112447 100644 > --- a/transaction.h > +++ b/transaction.h > @@ -28,7 +28,8 @@ struct btrfs_trans_handle { > u64 transid; > u64 alloc_exclude_start; > u64 alloc_exclude_nr; > - bool reinit_extent_tree; > + unsigned int reinit_extent_tree:1; > + unsigned int allocating_chunk:1;
Why do you switch reinit_extent_tree to the bit, this is an unrelated change. I'll drop it and update changelog with the 2M preallocation that was discussed.