Commit a1ba4c08 authored by Filipe Manana's avatar Filipe Manana Committed by David Sterba
Browse files

btrfs: add helper to replace extent map range with a new extent map



We have several places that need to drop all the extent maps in a given
file range and then add a new extent map for that range. Currently they
call btrfs_drop_extent_map_range() to delete all extent maps in the range
and then keep trying to add the new extent map in a loop that keeps
retrying while the insertion of the new extent map fails with -EEXIST.

So instead of repeating this logic, add a helper to extent_map.c that
does these steps and name it btrfs_replace_extent_map_range(). Also add
a comment about why the retry loop is necessary.

Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 9c9d1b4f
Loading
Loading
Loading
Loading
+41 −0
Original line number Diff line number Diff line
@@ -879,3 +879,44 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode, u64 start, u64 end,
	free_extent_map(split);
	free_extent_map(split2);
}

/*
 * Replace a range in the inode's extent map tree with a new extent map.
 *
 * @inode:      The target inode.
 * @new_em:     The new extent map to add to the inode's extent map tree.
 * @modified:   Indicate if the new extent map should be added to the list of
 *              modified extents (for fast fsync tracking).
 *
 * Drops all the extent maps in the inode's extent map tree that intersect the
 * range of the new extent map and adds the new extent map to the tree.
 * The caller should have locked an appropriate file range in the inode's io
 * tree before calling this function.
 */
int btrfs_replace_extent_map_range(struct btrfs_inode *inode,
				   struct extent_map *new_em,
				   bool modified)
{
	const u64 end = new_em->start + new_em->len - 1;
	struct extent_map_tree *tree = &inode->extent_tree;
	int ret;

	ASSERT(!extent_map_in_tree(new_em));

	/*
	 * The caller has locked an appropriate file range in the inode's io
	 * tree, but getting -EEXIST when adding the new extent map can still
	 * happen in case there are extents that partially cover the range, and
	 * this is due to two tasks operating on different parts of the extent.
	 * See commit 18e83ac75bfe67 ("Btrfs: fix unexpected EEXIST from
	 * btrfs_get_extent") for an example and details.
	 */
	do {
		btrfs_drop_extent_map_range(inode, new_em->start, end, false);
		write_lock(&tree->lock);
		ret = add_extent_mapping(tree, new_em, modified);
		write_unlock(&tree->lock);
	} while (ret == -EEXIST);

	return ret;
}
+3 −0
Original line number Diff line number Diff line
@@ -109,5 +109,8 @@ int btrfs_add_extent_mapping(struct btrfs_fs_info *fs_info,
void btrfs_drop_extent_map_range(struct btrfs_inode *inode,
				 u64 start, u64 end,
				 bool skip_pinned);
int btrfs_replace_extent_map_range(struct btrfs_inode *inode,
				   struct extent_map *new_em,
				   bool modified);

#endif
+1 −7
Original line number Diff line number Diff line
@@ -2359,7 +2359,6 @@ static int fill_holes(struct btrfs_trans_handle *trans,
	struct extent_buffer *leaf;
	struct btrfs_file_extent_item *fi;
	struct extent_map *hole_em;
	struct extent_map_tree *em_tree = &inode->extent_tree;
	struct btrfs_key key;
	int ret;

@@ -2440,12 +2439,7 @@ static int fill_holes(struct btrfs_trans_handle *trans,
		hole_em->compress_type = BTRFS_COMPRESS_NONE;
		hole_em->generation = trans->transid;

		do {
			btrfs_drop_extent_map_range(inode, offset, end - 1, false);
			write_lock(&em_tree->lock);
			ret = add_extent_mapping(em_tree, hole_em, 1);
			write_unlock(&em_tree->lock);
		} while (ret == -EEXIST);
		ret = btrfs_replace_extent_map_range(inode, hole_em, true);
		free_extent_map(hole_em);
		if (ret)
			btrfs_set_inode_full_sync(inode);
+8 −42
Original line number Diff line number Diff line
@@ -5051,7 +5051,6 @@ int btrfs_cont_expand(struct btrfs_inode *inode, loff_t oldsize, loff_t size)
	struct extent_io_tree *io_tree = &inode->io_tree;
	struct extent_map *em = NULL;
	struct extent_state *cached_state = NULL;
	struct extent_map_tree *em_tree = &inode->extent_tree;
	u64 hole_start = ALIGN(oldsize, fs_info->sectorsize);
	u64 block_end = ALIGN(size, fs_info->sectorsize);
	u64 last_byte;
@@ -5099,11 +5098,11 @@ int btrfs_cont_expand(struct btrfs_inode *inode, loff_t oldsize, loff_t size)
			if (err)
				break;

			hole_em = alloc_extent_map();
			if (!hole_em) {
				btrfs_drop_extent_map_range(inode, cur_offset,
						    cur_offset + hole_size - 1,
						    false);
			hole_em = alloc_extent_map();
			if (!hole_em) {
				btrfs_set_inode_full_sync(inode);
				goto next;
			}
@@ -5118,16 +5117,7 @@ int btrfs_cont_expand(struct btrfs_inode *inode, loff_t oldsize, loff_t size)
			hole_em->compress_type = BTRFS_COMPRESS_NONE;
			hole_em->generation = fs_info->generation;

			while (1) {
				write_lock(&em_tree->lock);
				err = add_extent_mapping(em_tree, hole_em, 1);
				write_unlock(&em_tree->lock);
				if (err != -EEXIST)
					break;
				btrfs_drop_extent_map_range(inode, cur_offset,
						    cur_offset + hole_size - 1,
						    false);
			}
			err = btrfs_replace_extent_map_range(inode, hole_em, true);
			free_extent_map(hole_em);
		} else {
			err = btrfs_inode_set_file_extent_range(inode,
@@ -7353,7 +7343,6 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
				       u64 ram_bytes, int compress_type,
				       int type)
{
	struct extent_map_tree *em_tree;
	struct extent_map *em;
	int ret;

@@ -7362,7 +7351,6 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
	       type == BTRFS_ORDERED_NOCOW ||
	       type == BTRFS_ORDERED_REGULAR);

	em_tree = &inode->extent_tree;
	em = alloc_extent_map();
	if (!em)
		return ERR_PTR(-ENOMEM);
@@ -7383,18 +7371,7 @@ static struct extent_map *create_io_em(struct btrfs_inode *inode, u64 start,
		em->compress_type = compress_type;
	}

	do {
		btrfs_drop_extent_map_range(inode, em->start,
					    em->start + em->len - 1, false);
		write_lock(&em_tree->lock);
		ret = add_extent_mapping(em_tree, em, 1);
		write_unlock(&em_tree->lock);
		/*
		 * The caller has taken lock_extent(), who could race with us
		 * to add em?
		 */
	} while (ret == -EEXIST);

	ret = btrfs_replace_extent_map_range(inode, em, true);
	if (ret) {
		free_extent_map(em);
		return ERR_PTR(ret);
@@ -9893,7 +9870,6 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
				       struct btrfs_trans_handle *trans)
{
	struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
	struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
	struct extent_map *em;
	struct btrfs_root *root = BTRFS_I(inode)->root;
	struct btrfs_key ins;
@@ -9949,11 +9925,10 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
			break;
		}

		btrfs_drop_extent_map_range(BTRFS_I(inode), cur_offset,
					    cur_offset + ins.offset - 1, false);

		em = alloc_extent_map();
		if (!em) {
			btrfs_drop_extent_map_range(BTRFS_I(inode), cur_offset,
					    cur_offset + ins.offset - 1, false);
			btrfs_set_inode_full_sync(BTRFS_I(inode));
			goto next;
		}
@@ -9968,16 +9943,7 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode,
		set_bit(EXTENT_FLAG_PREALLOC, &em->flags);
		em->generation = trans->transid;

		while (1) {
			write_lock(&em_tree->lock);
			ret = add_extent_mapping(em_tree, em, 1);
			write_unlock(&em_tree->lock);
			if (ret != -EEXIST)
				break;
			btrfs_drop_extent_map_range(BTRFS_I(inode), cur_offset,
						    cur_offset + ins.offset - 1,
						    false);
		}
		ret = btrfs_replace_extent_map_range(BTRFS_I(inode), em, true);
		free_extent_map(em);
next:
		num_bytes -= ins.offset;
+3 −11
Original line number Diff line number Diff line
@@ -2890,7 +2890,6 @@ static noinline_for_stack int prealloc_file_extent_cluster(
static noinline_for_stack int setup_relocation_extent_mapping(struct inode *inode,
				u64 start, u64 end, u64 block_start)
{
	struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
	struct extent_map *em;
	int ret = 0;

@@ -2905,17 +2904,10 @@ static noinline_for_stack int setup_relocation_extent_mapping(struct inode *inod
	set_bit(EXTENT_FLAG_PINNED, &em->flags);

	lock_extent(&BTRFS_I(inode)->io_tree, start, end, NULL);
	while (1) {
		write_lock(&em_tree->lock);
		ret = add_extent_mapping(em_tree, em, 0);
		write_unlock(&em_tree->lock);
		if (ret != -EEXIST) {
			free_extent_map(em);
			break;
		}
		btrfs_drop_extent_map_range(BTRFS_I(inode), start, end, false);
	}
	ret = btrfs_replace_extent_map_range(BTRFS_I(inode), em, false);
	unlock_extent(&BTRFS_I(inode)->io_tree, start, end, NULL);
	free_extent_map(em);

	return ret;
}