diff options
Diffstat (limited to 'fs/btrfs/inode.c')
| -rw-r--r-- | fs/btrfs/inode.c | 147 | 
1 files changed, 118 insertions, 29 deletions
| diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index e6eb20987351..8f60314c36c5 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -2271,13 +2271,127 @@ static blk_status_t btrfs_submit_bio_start(struct inode *inode, struct bio *bio,  	return btrfs_csum_one_bio(BTRFS_I(inode), bio, 0, 0);  } +/* + * Split an extent_map at [start, start + len] + * + * This function is intended to be used only for extract_ordered_extent(). + */ +static int split_zoned_em(struct btrfs_inode *inode, u64 start, u64 len, +			  u64 pre, u64 post) +{ +	struct extent_map_tree *em_tree = &inode->extent_tree; +	struct extent_map *em; +	struct extent_map *split_pre = NULL; +	struct extent_map *split_mid = NULL; +	struct extent_map *split_post = NULL; +	int ret = 0; +	int modified; +	unsigned long flags; + +	/* Sanity check */ +	if (pre == 0 && post == 0) +		return 0; + +	split_pre = alloc_extent_map(); +	if (pre) +		split_mid = alloc_extent_map(); +	if (post) +		split_post = alloc_extent_map(); +	if (!split_pre || (pre && !split_mid) || (post && !split_post)) { +		ret = -ENOMEM; +		goto out; +	} + +	ASSERT(pre + post < len); + +	lock_extent(&inode->io_tree, start, start + len - 1); +	write_lock(&em_tree->lock); +	em = lookup_extent_mapping(em_tree, start, len); +	if (!em) { +		ret = -EIO; +		goto out_unlock; +	} + +	ASSERT(em->len == len); +	ASSERT(!test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)); +	ASSERT(em->block_start < EXTENT_MAP_LAST_BYTE); + +	flags = em->flags; +	clear_bit(EXTENT_FLAG_PINNED, &em->flags); +	clear_bit(EXTENT_FLAG_LOGGING, &flags); +	modified = !list_empty(&em->list); + +	/* First, replace the em with a new extent_map starting from * em->start */ +	split_pre->start = em->start; +	split_pre->len = (pre ? pre : em->len - post); +	split_pre->orig_start = split_pre->start; +	split_pre->block_start = em->block_start; +	split_pre->block_len = split_pre->len; +	split_pre->orig_block_len = split_pre->block_len; +	split_pre->ram_bytes = split_pre->len; +	split_pre->flags = flags; +	split_pre->compress_type = em->compress_type; +	split_pre->generation = em->generation; + +	replace_extent_mapping(em_tree, em, split_pre, modified); + +	/* +	 * Now we only have an extent_map at: +	 *     [em->start, em->start + pre] if pre != 0 +	 *     [em->start, em->start + em->len - post] if pre == 0 +	 */ + +	if (pre) { +		/* Insert the middle extent_map */ +		split_mid->start = em->start + pre; +		split_mid->len = em->len - pre - post; +		split_mid->orig_start = split_mid->start; +		split_mid->block_start = em->block_start + pre; +		split_mid->block_len = split_mid->len; +		split_mid->orig_block_len = split_mid->block_len; +		split_mid->ram_bytes = split_mid->len; +		split_mid->flags = flags; +		split_mid->compress_type = em->compress_type; +		split_mid->generation = em->generation; +		add_extent_mapping(em_tree, split_mid, modified); +	} + +	if (post) { +		split_post->start = em->start + em->len - post; +		split_post->len = post; +		split_post->orig_start = split_post->start; +		split_post->block_start = em->block_start + em->len - post; +		split_post->block_len = split_post->len; +		split_post->orig_block_len = split_post->block_len; +		split_post->ram_bytes = split_post->len; +		split_post->flags = flags; +		split_post->compress_type = em->compress_type; +		split_post->generation = em->generation; +		add_extent_mapping(em_tree, split_post, modified); +	} + +	/* Once for us */ +	free_extent_map(em); +	/* Once for the tree */ +	free_extent_map(em); + +out_unlock: +	write_unlock(&em_tree->lock); +	unlock_extent(&inode->io_tree, start, start + len - 1); +out: +	free_extent_map(split_pre); +	free_extent_map(split_mid); +	free_extent_map(split_post); + +	return ret; +} +  static blk_status_t extract_ordered_extent(struct btrfs_inode *inode,  					   struct bio *bio, loff_t file_offset)  {  	struct btrfs_ordered_extent *ordered; -	struct extent_map *em = NULL, *em_new = NULL; -	struct extent_map_tree *em_tree = &inode->extent_tree;  	u64 start = (u64)bio->bi_iter.bi_sector << SECTOR_SHIFT; +	u64 file_len;  	u64 len = bio->bi_iter.bi_size;  	u64 end = start + len;  	u64 ordered_end; @@ -2317,41 +2431,16 @@ static blk_status_t extract_ordered_extent(struct btrfs_inode *inode,  		goto out;  	} +	file_len = ordered->num_bytes;  	pre = start - ordered->disk_bytenr;  	post = ordered_end - end;  	ret = btrfs_split_ordered_extent(ordered, pre, post);  	if (ret)  		goto out; - -	read_lock(&em_tree->lock); -	em = lookup_extent_mapping(em_tree, ordered->file_offset, len); -	if (!em) { -		read_unlock(&em_tree->lock); -		ret = -EIO; -		goto out; -	} -	read_unlock(&em_tree->lock); - -	ASSERT(!test_bit(EXTENT_FLAG_COMPRESSED, &em->flags)); -	/* -	 * We cannot reuse em_new here but have to create a new one, as -	 * unpin_extent_cache() expects the start of the extent map to be the -	 * logical offset of the file, which does not hold true anymore after -	 * splitting. -	 */ -	em_new = create_io_em(inode, em->start + pre, len, -			      em->start + pre, em->block_start + pre, len, -			      len, len, BTRFS_COMPRESS_NONE, -			      BTRFS_ORDERED_REGULAR); -	if (IS_ERR(em_new)) { -		ret = PTR_ERR(em_new); -		goto out; -	} -	free_extent_map(em_new); +	ret = split_zoned_em(inode, file_offset, file_len, pre, post);  out: -	free_extent_map(em);  	btrfs_put_ordered_extent(ordered);  	return errno_to_blk_status(ret); |