diff options
Diffstat (limited to 'fs/btrfs/extent-tree.c')
| -rw-r--r-- | fs/btrfs/extent-tree.c | 97 | 
1 files changed, 62 insertions, 35 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c index 6e1d36702ff..3cde907a25a 100644 --- a/fs/btrfs/extent-tree.c +++ b/fs/btrfs/extent-tree.c @@ -3728,6 +3728,60 @@ commit:  	return btrfs_commit_transaction(trans, root);  } +enum flush_state { +	FLUSH_DELALLOC		=	1, +	FLUSH_DELALLOC_WAIT	=	2, +	FLUSH_DELAYED_ITEMS_NR	=	3, +	FLUSH_DELAYED_ITEMS	=	4, +	COMMIT_TRANS		=	5, +}; + +static int flush_space(struct btrfs_root *root, +		       struct btrfs_space_info *space_info, u64 num_bytes, +		       u64 orig_bytes, int state) +{ +	struct btrfs_trans_handle *trans; +	int nr; +	int ret; + +	switch (state) { +	case FLUSH_DELALLOC: +	case FLUSH_DELALLOC_WAIT: +		ret = shrink_delalloc(root, num_bytes, +				      state == FLUSH_DELALLOC_WAIT); +		if (ret > 0) +			ret = 0; +		break; +	case FLUSH_DELAYED_ITEMS_NR: +	case FLUSH_DELAYED_ITEMS: +		if (state == FLUSH_DELAYED_ITEMS_NR) { +			u64 bytes = btrfs_calc_trans_metadata_size(root, 1); + +			nr = (int)div64_u64(num_bytes, bytes); +			if (!nr) +				nr = 1; +			nr *= 2; +		} else { +			nr = -1; +		} +		trans = btrfs_join_transaction(root); +		if (IS_ERR(trans)) { +			ret = PTR_ERR(trans); +			break; +		} +		ret = btrfs_run_delayed_items_nr(trans, root, nr); +		btrfs_end_transaction(trans, root); +		break; +	case COMMIT_TRANS: +		ret = may_commit_transaction(root, space_info, orig_bytes, 0); +		break; +	default: +		ret = -ENOSPC; +		break; +	} + +	return ret; +}  /**   * reserve_metadata_bytes - try to reserve bytes from the block_rsv's space   * @root - the root we're allocating for @@ -3749,11 +3803,10 @@ static int reserve_metadata_bytes(struct btrfs_root *root,  	struct btrfs_space_info *space_info = block_rsv->space_info;  	u64 used;  	u64 num_bytes = orig_bytes; -	int retries = 0; +	int flush_state = FLUSH_DELALLOC;  	int ret = 0; -	bool committed = false;  	bool flushing = false; -	bool wait_ordered = false; +	bool committed = false;  again:  	ret = 0; @@ -3812,9 +3865,8 @@ again:  		 * amount plus the amount of bytes that we need for this  		 * reservation.  		 */ -		wait_ordered = true;  		num_bytes = used - space_info->total_bytes + -			(orig_bytes * (retries + 1)); +			(orig_bytes * 2);  	}  	if (ret) { @@ -3867,8 +3919,6 @@ again:  			trace_btrfs_space_reservation(root->fs_info,  				"space_info", space_info->flags, orig_bytes, 1);  			ret = 0; -		} else { -			wait_ordered = true;  		}  	} @@ -3887,36 +3937,13 @@ again:  	if (!ret || !flush)  		goto out; -	/* -	 * We do synchronous shrinking since we don't actually unreserve -	 * metadata until after the IO is completed. -	 */ -	ret = shrink_delalloc(root, num_bytes, wait_ordered); -	if (ret < 0) -		goto out; - -	ret = 0; - -	/* -	 * So if we were overcommitted it's possible that somebody else flushed -	 * out enough space and we simply didn't have enough space to reclaim, -	 * so go back around and try again. -	 */ -	if (retries < 2) { -		wait_ordered = true; -		retries++; +	ret = flush_space(root, space_info, num_bytes, orig_bytes, +			  flush_state); +	flush_state++; +	if (!ret)  		goto again; -	} - -	ret = -ENOSPC; -	if (committed) -		goto out; - -	ret = may_commit_transaction(root, space_info, orig_bytes, 0); -	if (!ret) { -		committed = true; +	else if (flush_state <= COMMIT_TRANS)  		goto again; -	}  out:  	if (flushing) {  |