Commit 4addc1ff authored by David Sterba's avatar David Sterba
Browse files

btrfs: qgroup: preallocate memory before adding a relation



There's a transaction joined in the qgroup relation add/remove ioctl and
any error will lead to abort/error. We could lift the allocation from
btrfs_add_qgroup_relation() and move it outside of the transaction
context. The relation deletion does not need that.

The ownership of the structure is moved to the add relation handler.

Reviewed-by: default avatarQu Wenruo <wqu@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 7733b8dd
Loading
Loading
Loading
Loading
+16 −1
Original line number Diff line number Diff line
@@ -3829,6 +3829,7 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
	struct btrfs_fs_info *fs_info = inode_to_fs_info(inode);
	struct btrfs_root *root = BTRFS_I(inode)->root;
	struct btrfs_ioctl_qgroup_assign_args *sa;
	struct btrfs_qgroup_list *prealloc = NULL;
	struct btrfs_trans_handle *trans;
	int ret;
	int err;
@@ -3849,14 +3850,27 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
		goto drop_write;
	}

	if (sa->assign) {
		prealloc = kzalloc(sizeof(*prealloc), GFP_KERNEL);
		if (!prealloc) {
			ret = -ENOMEM;
			goto drop_write;
		}
	}

	trans = btrfs_join_transaction(root);
	if (IS_ERR(trans)) {
		ret = PTR_ERR(trans);
		goto out;
	}

	/*
	 * Prealloc ownership is moved to the relation handler, there it's used
	 * or freed on error.
	 */
	if (sa->assign) {
		ret = btrfs_add_qgroup_relation(trans, sa->src, sa->dst);
		ret = btrfs_add_qgroup_relation(trans, sa->src, sa->dst, prealloc);
		prealloc = NULL;
	} else {
		ret = btrfs_del_qgroup_relation(trans, sa->src, sa->dst);
	}
@@ -3873,6 +3887,7 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
		ret = err;

out:
	kfree(prealloc);
	kfree(sa);
drop_write:
	mnt_drop_write_file(file);
+8 −17
Original line number Diff line number Diff line
@@ -155,16 +155,6 @@ static inline u64 btrfs_qgroup_get_new_refcnt(const struct btrfs_qgroup *qg, u64
	return qg->new_refcnt - seq;
}

/*
 * glue structure to represent the relations between qgroups.
 */
struct btrfs_qgroup_list {
	struct list_head next_group;
	struct list_head next_member;
	struct btrfs_qgroup *group;
	struct btrfs_qgroup *member;
};

static int
qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
		   int init_flags);
@@ -1569,15 +1559,21 @@ static int quick_update_accounting(struct btrfs_fs_info *fs_info,
	return ret;
}

int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst)
/*
 * Add relation between @src and @dst qgroup. The @prealloc is allocated by the
 * callers and transferred here (either used or freed on error).
 */
int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst,
			      struct btrfs_qgroup_list *prealloc)
{
	struct btrfs_fs_info *fs_info = trans->fs_info;
	struct btrfs_qgroup *parent;
	struct btrfs_qgroup *member;
	struct btrfs_qgroup_list *list;
	struct btrfs_qgroup_list *prealloc = NULL;
	int ret = 0;

	ASSERT(prealloc);

	/* Check the level of src and dst first */
	if (btrfs_qgroup_level(src) >= btrfs_qgroup_level(dst))
		return -EINVAL;
@@ -1602,11 +1598,6 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst
		}
	}

	prealloc = kzalloc(sizeof(*list), GFP_NOFS);
	if (!prealloc) {
		ret = -ENOMEM;
		goto out;
	}
	ret = add_qgroup_relation_item(trans, src, dst);
	if (ret)
		goto out;
+10 −1
Original line number Diff line number Diff line
@@ -278,6 +278,14 @@ struct btrfs_qgroup {
	struct kobject kobj;
};

/* Glue structure to represent the relations between qgroups. */
struct btrfs_qgroup_list {
	struct list_head next_group;
	struct list_head next_member;
	struct btrfs_qgroup *group;
	struct btrfs_qgroup *member;
};

struct btrfs_squota_delta {
	/* The fstree root this delta counts against. */
	u64 root;
@@ -321,7 +329,8 @@ int btrfs_qgroup_rescan(struct btrfs_fs_info *fs_info);
void btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info);
int btrfs_qgroup_wait_for_completion(struct btrfs_fs_info *fs_info,
				     bool interruptible);
int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst);
int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst,
			      struct btrfs_qgroup_list *prealloc);
int btrfs_del_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
			      u64 dst);
int btrfs_create_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid);