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

btrfs: move ulists to data extent sharedness check context



When calling btrfs_is_data_extent_shared() we pass two ulists that were
allocated by the caller. This is because the single caller, fiemap, calls
btrfs_is_data_extent_shared() multiple times and the ulists can be reused,
instead of allocating new ones before each call and freeing them after
each call.

Now that we have a context structure/object that we pass to
btrfs_is_data_extent_shared(), we can move those ulists to it, and hide
their allocation and the context's allocation in a helper function, as
well as the freeing of the ulists and the context object. This allows to
reduce the number of parameters passed to btrfs_is_data_extent_shared(),
the need to pass the ulists from extent_fiemap() to fiemap_process_hole()
and having the caller deal with allocating and releasing the ulists.

Also rename one of the ulists from 'tmp' / 'tmp_ulist' to 'refs', since
that's a much better name as it reflects what the list is used for (and
matching the argument name for find_parent_nodes()).

Signed-off-by: default avatarFilipe Manana <fdmanana@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 61dbb952
Loading
Loading
Loading
Loading
+32 −11
Original line number Diff line number Diff line
@@ -1655,6 +1655,30 @@ static void store_backref_shared_cache(struct btrfs_backref_share_check_ctx *ctx
	}
}

struct btrfs_backref_share_check_ctx *btrfs_alloc_backref_share_check_ctx(void)
{
	struct btrfs_backref_share_check_ctx *ctx;

	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
	if (!ctx)
		return NULL;

	ulist_init(&ctx->refs);
	ulist_init(&ctx->roots);

	return ctx;
}

void btrfs_free_backref_share_ctx(struct btrfs_backref_share_check_ctx *ctx)
{
	if (!ctx)
		return;

	ulist_release(&ctx->refs);
	ulist_release(&ctx->roots);
	kfree(ctx);
}

/*
 * Check if a data extent is shared or not.
 *
@@ -1662,8 +1686,6 @@ static void store_backref_shared_cache(struct btrfs_backref_share_check_ctx *ctx
 * @bytenr:      Logical bytenr of the extent we are checking.
 * @extent_gen:  Generation of the extent (file extent item) or 0 if it is
 *               not known.
 * @roots:       List of roots this extent is shared among.
 * @tmp:         Temporary list used for iteration.
 * @ctx:         A backref sharedness check context.
 *
 * btrfs_is_data_extent_shared uses the backref walking code but will short
@@ -1679,7 +1701,6 @@ static void store_backref_shared_cache(struct btrfs_backref_share_check_ctx *ctx
 */
int btrfs_is_data_extent_shared(struct btrfs_inode *inode, u64 bytenr,
				u64 extent_gen,
				struct ulist *roots, struct ulist *tmp,
				struct btrfs_backref_share_check_ctx *ctx)
{
	struct btrfs_root *root = inode->root;
@@ -1697,8 +1718,8 @@ int btrfs_is_data_extent_shared(struct btrfs_inode *inode, u64 bytenr,
	};
	int level;

	ulist_init(roots);
	ulist_init(tmp);
	ulist_init(&ctx->roots);
	ulist_init(&ctx->refs);

	trans = btrfs_join_transaction_nostart(root);
	if (IS_ERR(trans)) {
@@ -1720,8 +1741,8 @@ int btrfs_is_data_extent_shared(struct btrfs_inode *inode, u64 bytenr,
		bool is_shared;
		bool cached;

		ret = find_parent_nodes(trans, fs_info, bytenr, elem.seq, tmp,
					roots, NULL, &shared, false);
		ret = find_parent_nodes(trans, fs_info, bytenr, elem.seq, &ctx->refs,
					&ctx->roots, NULL, &shared, false);
		if (ret == BACKREF_FOUND_SHARED) {
			/* this is the only condition under which we return 1 */
			ret = 1;
@@ -1760,13 +1781,13 @@ int btrfs_is_data_extent_shared(struct btrfs_inode *inode, u64 bytenr,
		 * deal with), we can not use it if we have multiple leaves
		 * (which implies multiple paths).
		 */
		if (level == -1 && tmp->nnodes > 1)
		if (level == -1 && ctx->refs.nnodes > 1)
			ctx->use_path_cache = false;

		if (level >= 0)
			store_backref_shared_cache(ctx, root, bytenr,
						   level, false);
		node = ulist_next(tmp, &uiter);
		node = ulist_next(&ctx->refs, &uiter);
		if (!node)
			break;
		bytenr = node->val;
@@ -1789,8 +1810,8 @@ int btrfs_is_data_extent_shared(struct btrfs_inode *inode, u64 bytenr,
		up_read(&fs_info->commit_root_sem);
	}
out:
	ulist_release(roots);
	ulist_release(tmp);
	ulist_release(&ctx->roots);
	ulist_release(&ctx->refs);
	return ret;
}

+6 −1
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ struct btrfs_backref_shared_cache_entry {
};

struct btrfs_backref_share_check_ctx {
	/* Ulists used during backref walking. */
	struct ulist refs;
	struct ulist roots;
	/*
	 * A path from a root to a leaf that has a file extent item pointing to
	 * a given data extent should never exceed the maximum b+tree height.
@@ -35,6 +38,9 @@ struct btrfs_backref_share_check_ctx {
typedef int (iterate_extent_inodes_t)(u64 inum, u64 offset, u64 root,
		void *ctx);

struct btrfs_backref_share_check_ctx *btrfs_alloc_backref_share_check_ctx(void);
void btrfs_free_backref_share_ctx(struct btrfs_backref_share_check_ctx *ctx);

int extent_from_logical(struct btrfs_fs_info *fs_info, u64 logical,
			struct btrfs_path *path, struct btrfs_key *found_key,
			u64 *flags);
@@ -79,7 +85,6 @@ int btrfs_find_one_extref(struct btrfs_root *root, u64 inode_objectid,
			  u64 *found_off);
int btrfs_is_data_extent_shared(struct btrfs_inode *inode, u64 bytenr,
				u64 extent_gen,
				struct ulist *roots, struct ulist *tmp,
				struct btrfs_backref_share_check_ctx *ctx);

int __init btrfs_prelim_ref_init(void);
+10 −24
Original line number Diff line number Diff line
@@ -3708,7 +3708,6 @@ static int fiemap_process_hole(struct btrfs_inode *inode,
			       struct btrfs_backref_share_check_ctx *backref_ctx,
			       u64 disk_bytenr, u64 extent_offset,
			       u64 extent_gen,
			       struct ulist *roots, struct ulist *tmp_ulist,
			       u64 start, u64 end)
{
	const u64 i_size = i_size_read(&inode->vfs_inode);
@@ -3753,8 +3752,7 @@ static int fiemap_process_hole(struct btrfs_inode *inode,
			if (!checked_extent_shared && fieinfo->fi_extents_max) {
				ret = btrfs_is_data_extent_shared(inode,
								  disk_bytenr,
							  extent_gen, roots,
							  tmp_ulist,
								  extent_gen,
								  backref_ctx);
				if (ret < 0)
					return ret;
@@ -3803,8 +3801,7 @@ static int fiemap_process_hole(struct btrfs_inode *inode,
		if (!checked_extent_shared && fieinfo->fi_extents_max) {
			ret = btrfs_is_data_extent_shared(inode,
							  disk_bytenr,
							  extent_gen, roots,
							  tmp_ulist,
							  extent_gen,
							  backref_ctx);
			if (ret < 0)
				return ret;
@@ -3905,8 +3902,6 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
	struct btrfs_path *path;
	struct fiemap_cache cache = { 0 };
	struct btrfs_backref_share_check_ctx *backref_ctx;
	struct ulist *roots;
	struct ulist *tmp_ulist;
	u64 last_extent_end;
	u64 prev_extent_end;
	u64 lockstart;
@@ -3914,11 +3909,9 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
	bool stopped = false;
	int ret;

	backref_ctx = kzalloc(sizeof(*backref_ctx), GFP_KERNEL);
	backref_ctx = btrfs_alloc_backref_share_check_ctx();
	path = btrfs_alloc_path();
	roots = ulist_alloc(GFP_KERNEL);
	tmp_ulist = ulist_alloc(GFP_KERNEL);
	if (!backref_ctx || !path || !roots || !tmp_ulist) {
	if (!backref_ctx || !path) {
		ret = -ENOMEM;
		goto out;
	}
@@ -3979,7 +3972,6 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,

			ret = fiemap_process_hole(inode, fieinfo, &cache,
						  backref_ctx, 0, 0, 0,
						  roots, tmp_ulist,
						  prev_extent_end, range_end);
			if (ret < 0) {
				goto out_unlock;
@@ -4021,13 +4013,12 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
			ret = fiemap_process_hole(inode, fieinfo, &cache,
						  backref_ctx,
						  disk_bytenr, extent_offset,
						  extent_gen, roots, tmp_ulist,
						  key.offset, extent_end - 1);
						  extent_gen, key.offset,
						  extent_end - 1);
		} else if (disk_bytenr == 0) {
			/* We have an explicit hole. */
			ret = fiemap_process_hole(inode, fieinfo, &cache,
						  backref_ctx, 0, 0, 0,
						  roots, tmp_ulist,
						  key.offset, extent_end - 1);
		} else {
			/* We have a regular extent. */
@@ -4035,8 +4026,6 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
				ret = btrfs_is_data_extent_shared(inode,
								  disk_bytenr,
								  extent_gen,
								  roots,
								  tmp_ulist,
								  backref_ctx);
				if (ret < 0)
					goto out_unlock;
@@ -4087,8 +4076,7 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,

	if (!stopped && prev_extent_end < lockend) {
		ret = fiemap_process_hole(inode, fieinfo, &cache, backref_ctx,
					  0, 0, 0, roots, tmp_ulist,
					  prev_extent_end, lockend - 1);
					  0, 0, 0, prev_extent_end, lockend - 1);
		if (ret < 0)
			goto out_unlock;
		prev_extent_end = lockend;
@@ -4119,10 +4107,8 @@ int extent_fiemap(struct btrfs_inode *inode, struct fiemap_extent_info *fieinfo,
out_unlock:
	unlock_extent(&inode->io_tree, lockstart, lockend, &cached_state);
out:
	kfree(backref_ctx);
	btrfs_free_backref_share_ctx(backref_ctx);
	btrfs_free_path(path);
	ulist_free(roots);
	ulist_free(tmp_ulist);
	return ret;
}