diff options
Diffstat (limited to 'fs/btrfs/raid-stripe-tree.c')
| -rw-r--r-- | fs/btrfs/raid-stripe-tree.c | 146 | 
1 files changed, 130 insertions, 16 deletions
| diff --git a/fs/btrfs/raid-stripe-tree.c b/fs/btrfs/raid-stripe-tree.c index 9ffc79f250fb..1834011ccc49 100644 --- a/fs/btrfs/raid-stripe-tree.c +++ b/fs/btrfs/raid-stripe-tree.c @@ -13,12 +13,13 @@  #include "volumes.h"  #include "print-tree.h" -static void btrfs_partially_delete_raid_extent(struct btrfs_trans_handle *trans, +static int btrfs_partially_delete_raid_extent(struct btrfs_trans_handle *trans,  					       struct btrfs_path *path,  					       const struct btrfs_key *oldkey,  					       u64 newlen, u64 frontpad)  { -	struct btrfs_stripe_extent *extent; +	struct btrfs_root *stripe_root = trans->fs_info->stripe_root; +	struct btrfs_stripe_extent *extent, *newitem;  	struct extent_buffer *leaf;  	int slot;  	size_t item_size; @@ -27,23 +28,39 @@ static void btrfs_partially_delete_raid_extent(struct btrfs_trans_handle *trans,  		.type = BTRFS_RAID_STRIPE_KEY,  		.offset = newlen,  	}; +	int ret; +	ASSERT(newlen > 0);  	ASSERT(oldkey->type == BTRFS_RAID_STRIPE_KEY);  	leaf = path->nodes[0];  	slot = path->slots[0];  	item_size = btrfs_item_size(leaf, slot); + +	newitem = kzalloc(item_size, GFP_NOFS); +	if (!newitem) +		return -ENOMEM; +  	extent = btrfs_item_ptr(leaf, slot, struct btrfs_stripe_extent);  	for (int i = 0; i < btrfs_num_raid_stripes(item_size); i++) {  		struct btrfs_raid_stride *stride = &extent->strides[i];  		u64 phys; -		phys = btrfs_raid_stride_physical(leaf, stride); -		btrfs_set_raid_stride_physical(leaf, stride, phys + frontpad); +		phys = btrfs_raid_stride_physical(leaf, stride) + frontpad; +		btrfs_set_stack_raid_stride_physical(&newitem->strides[i], phys);  	} -	btrfs_set_item_key_safe(trans, path, &newkey); +	ret = btrfs_del_item(trans, stripe_root, path); +	if (ret) +		goto out; + +	btrfs_release_path(path); +	ret = btrfs_insert_item(trans, stripe_root, &newkey, newitem, item_size); + +out: +	kfree(newitem); +	return ret;  }  int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 length) @@ -59,9 +76,22 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le  	int slot;  	int ret; -	if (!stripe_root) +	if (!btrfs_fs_incompat(fs_info, RAID_STRIPE_TREE) || !stripe_root)  		return 0; +	if (!btrfs_is_testing(fs_info)) { +		struct btrfs_chunk_map *map; +		bool use_rst; + +		map = btrfs_find_chunk_map(fs_info, start, length); +		if (!map) +			return -EINVAL; +		use_rst = btrfs_need_stripe_tree_update(fs_info, map->type); +		btrfs_free_chunk_map(map); +		if (!use_rst) +			return 0; +	} +  	path = btrfs_alloc_path();  	if (!path)  		return -ENOMEM; @@ -85,6 +115,37 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le  		found_end = found_start + key.offset;  		ret = 0; +		/* +		 * The stripe extent starts before the range we want to delete, +		 * but the range spans more than one stripe extent: +		 * +		 * |--- RAID Stripe Extent ---||--- RAID Stripe Extent ---| +		 *        |--- keep  ---|--- drop ---| +		 * +		 * This means we have to get the previous item, truncate its +		 * length and then restart the search. +		 */ +		if (found_start > start) { +			if (slot == 0) { +				ret = btrfs_previous_item(stripe_root, path, start, +							  BTRFS_RAID_STRIPE_KEY); +				if (ret) { +					if (ret > 0) +						ret = -ENOENT; +					break; +				} +			} else { +				path->slots[0]--; +			} + +			leaf = path->nodes[0]; +			slot = path->slots[0]; +			btrfs_item_key_to_cpu(leaf, &key, slot); +			found_start = key.objectid; +			found_end = found_start + key.offset; +			ASSERT(found_start <= start); +		} +  		if (key.type != BTRFS_RAID_STRIPE_KEY)  			break; @@ -96,6 +157,54 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le  					       found_start, found_end);  		/* +		 * The stripe extent starts before the range we want to delete +		 * and ends after the range we want to delete, i.e. we're +		 * punching a hole in the stripe extent: +		 * +		 *  |--- RAID Stripe Extent ---| +		 *  | keep |--- drop ---| keep | +		 * +		 * This means we need to a) truncate the existing item and b) +		 * create a second item for the remaining range. +		 */ +		if (found_start < start && found_end > end) { +			size_t item_size; +			u64 diff_start = start - found_start; +			u64 diff_end = found_end - end; +			struct btrfs_stripe_extent *extent; +			struct btrfs_key newkey = { +				.objectid = end, +				.type = BTRFS_RAID_STRIPE_KEY, +				.offset = diff_end, +			}; + +			/* The "right" item. */ +			ret = btrfs_duplicate_item(trans, stripe_root, path, &newkey); +			if (ret) +				break; + +			item_size = btrfs_item_size(leaf, path->slots[0]); +			extent = btrfs_item_ptr(leaf, path->slots[0], +						struct btrfs_stripe_extent); + +			for (int i = 0; i < btrfs_num_raid_stripes(item_size); i++) { +				struct btrfs_raid_stride *stride = &extent->strides[i]; +				u64 phys; + +				phys = btrfs_raid_stride_physical(leaf, stride); +				phys += diff_start + length; +				btrfs_set_raid_stride_physical(leaf, stride, phys); +			} + +			/* The "left" item. */ +			path->slots[0]--; +			btrfs_item_key_to_cpu(leaf, &key, path->slots[0]); +			btrfs_partially_delete_raid_extent(trans, path, &key, +							   diff_start, 0); +			break; +		} + +		/*  		 * The stripe extent starts before the range we want to delete:  		 *  		 * |--- RAID Stripe Extent ---| @@ -105,11 +214,18 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le  		 * length to the new size and then re-insert the item.  		 */  		if (found_start < start) { -			u64 diff = start - found_start; +			u64 diff_start = start - found_start;  			btrfs_partially_delete_raid_extent(trans, path, &key, -							   diff, 0); -			break; +							   diff_start, 0); + +			start += (key.offset - diff_start); +			length -= (key.offset - diff_start); +			if (length == 0) +				break; + +			btrfs_release_path(path); +			continue;  		}  		/* @@ -122,13 +238,16 @@ int btrfs_delete_raid_extent(struct btrfs_trans_handle *trans, u64 start, u64 le  		 * length to the new size and then re-insert the item.  		 */  		if (found_end > end) { -			u64 diff = found_end - end; +			u64 diff_end = found_end - end;  			btrfs_partially_delete_raid_extent(trans, path, &key, -							   diff, diff); +							   key.offset - length, +							   length); +			ASSERT(key.offset - diff_end == length);  			break;  		} +		/* Finally we can delete the whole item, no more special cases. */  		ret = btrfs_del_item(trans, stripe_root, path);  		if (ret)  			break; @@ -169,7 +288,6 @@ static int update_raid_extent_item(struct btrfs_trans_handle *trans,  	write_extent_buffer(leaf, stripe_extent, btrfs_item_ptr_offset(leaf, slot),  			    item_size); -	btrfs_mark_buffer_dirty(trans, leaf);  	btrfs_free_path(path);  	return ret; @@ -199,12 +317,8 @@ int btrfs_insert_one_raid_extent(struct btrfs_trans_handle *trans,  	for (int i = 0; i < num_stripes; i++) {  		u64 devid = bioc->stripes[i].dev->devid;  		u64 physical = bioc->stripes[i].physical; -		u64 length = bioc->stripes[i].length;  		struct btrfs_raid_stride *raid_stride = &stripe_extent->strides[i]; -		if (length == 0) -			length = bioc->size; -  		btrfs_set_stack_raid_stride_devid(raid_stride, devid);  		btrfs_set_stack_raid_stride_physical(raid_stride, physical);  	} | 
