diff options
Diffstat (limited to 'fs/btrfs/block-group.c')
| -rw-r--r-- | fs/btrfs/block-group.c | 65 | 
1 files changed, 46 insertions, 19 deletions
diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 48ae509f2ac2..030ab44fce18 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -499,12 +499,16 @@ static void fragment_free_space(struct btrfs_block_group *block_group)   * used yet since their free space will be released as soon as the transaction   * commits.   */ -u64 add_new_free_space(struct btrfs_block_group *block_group, u64 start, u64 end) +int add_new_free_space(struct btrfs_block_group *block_group, u64 start, u64 end, +		       u64 *total_added_ret)  {  	struct btrfs_fs_info *info = block_group->fs_info; -	u64 extent_start, extent_end, size, total_added = 0; +	u64 extent_start, extent_end, size;  	int ret; +	if (total_added_ret) +		*total_added_ret = 0; +  	while (start < end) {  		ret = find_first_extent_bit(&info->excluded_extents, start,  					    &extent_start, &extent_end, @@ -517,10 +521,12 @@ u64 add_new_free_space(struct btrfs_block_group *block_group, u64 start, u64 end  			start = extent_end + 1;  		} else if (extent_start > start && extent_start < end) {  			size = extent_start - start; -			total_added += size;  			ret = btrfs_add_free_space_async_trimmed(block_group,  								 start, size); -			BUG_ON(ret); /* -ENOMEM or logic error */ +			if (ret) +				return ret; +			if (total_added_ret) +				*total_added_ret += size;  			start = extent_end + 1;  		} else {  			break; @@ -529,13 +535,15 @@ u64 add_new_free_space(struct btrfs_block_group *block_group, u64 start, u64 end  	if (start < end) {  		size = end - start; -		total_added += size;  		ret = btrfs_add_free_space_async_trimmed(block_group, start,  							 size); -		BUG_ON(ret); /* -ENOMEM or logic error */ +		if (ret) +			return ret; +		if (total_added_ret) +			*total_added_ret += size;  	} -	return total_added; +	return 0;  }  /* @@ -779,8 +787,13 @@ next:  		if (key.type == BTRFS_EXTENT_ITEM_KEY ||  		    key.type == BTRFS_METADATA_ITEM_KEY) { -			total_found += add_new_free_space(block_group, last, -							  key.objectid); +			u64 space_added; + +			ret = add_new_free_space(block_group, last, key.objectid, +						 &space_added); +			if (ret) +				goto out; +			total_found += space_added;  			if (key.type == BTRFS_METADATA_ITEM_KEY)  				last = key.objectid +  					fs_info->nodesize; @@ -795,11 +808,10 @@ next:  		}  		path->slots[0]++;  	} -	ret = 0; - -	total_found += add_new_free_space(block_group, last, -				block_group->start + block_group->length); +	ret = add_new_free_space(block_group, last, +				 block_group->start + block_group->length, +				 NULL);  out:  	btrfs_free_path(path);  	return ret; @@ -1640,13 +1652,14 @@ void btrfs_mark_bg_unused(struct btrfs_block_group *bg)  {  	struct btrfs_fs_info *fs_info = bg->fs_info; -	trace_btrfs_add_unused_block_group(bg);  	spin_lock(&fs_info->unused_bgs_lock);  	if (list_empty(&bg->bg_list)) {  		btrfs_get_block_group(bg); +		trace_btrfs_add_unused_block_group(bg);  		list_add_tail(&bg->bg_list, &fs_info->unused_bgs); -	} else { +	} else if (!test_bit(BLOCK_GROUP_FLAG_NEW, &bg->runtime_flags)) {  		/* Pull out the block group from the reclaim_bgs list. */ +		trace_btrfs_add_unused_block_group(bg);  		list_move_tail(&bg->bg_list, &fs_info->unused_bgs);  	}  	spin_unlock(&fs_info->unused_bgs_lock); @@ -2087,6 +2100,7 @@ static int exclude_super_stripes(struct btrfs_block_group *cache)  		/* Shouldn't have super stripes in sequential zones */  		if (zoned && nr) { +			kfree(logical);  			btrfs_err(fs_info,  			"zoned: block group %llu must not contain super block",  				  cache->start); @@ -2292,9 +2306,11 @@ static int read_one_block_group(struct btrfs_fs_info *info,  		btrfs_free_excluded_extents(cache);  	} else if (cache->used == 0) {  		cache->cached = BTRFS_CACHE_FINISHED; -		add_new_free_space(cache, cache->start, -				   cache->start + cache->length); +		ret = add_new_free_space(cache, cache->start, +					 cache->start + cache->length, NULL);  		btrfs_free_excluded_extents(cache); +		if (ret) +			goto error;  	}  	ret = btrfs_add_block_group_cache(info, cache); @@ -2668,6 +2684,7 @@ void btrfs_create_pending_block_groups(struct btrfs_trans_handle *trans)  next:  		btrfs_delayed_refs_rsv_release(fs_info, 1);  		list_del_init(&block_group->bg_list); +		clear_bit(BLOCK_GROUP_FLAG_NEW, &block_group->runtime_flags);  	}  	btrfs_trans_release_chunk_metadata(trans);  } @@ -2707,6 +2724,13 @@ struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *tran  	if (!cache)  		return ERR_PTR(-ENOMEM); +	/* +	 * Mark it as new before adding it to the rbtree of block groups or any +	 * list, so that no other task finds it and calls btrfs_mark_bg_unused() +	 * before the new flag is set. +	 */ +	set_bit(BLOCK_GROUP_FLAG_NEW, &cache->runtime_flags); +  	cache->length = size;  	set_free_space_tree_thresholds(cache);  	cache->flags = type; @@ -2730,9 +2754,12 @@ struct btrfs_block_group *btrfs_make_block_group(struct btrfs_trans_handle *tran  		return ERR_PTR(ret);  	} -	add_new_free_space(cache, chunk_offset, chunk_offset + size); - +	ret = add_new_free_space(cache, chunk_offset, chunk_offset + size, NULL);  	btrfs_free_excluded_extents(cache); +	if (ret) { +		btrfs_put_block_group(cache); +		return ERR_PTR(ret); +	}  	/*  	 * Ensure the corresponding space_info object is created and  |