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.

Reply via email to