Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46b665ce authored by Jan Schmidt's avatar Jan Schmidt Committed by Josef Bacik
Browse files

Btrfs: split btrfs_qgroup_account_ref into four functions



The function is separated into a preparation part and the three accounting
steps mentioned in the qgroups documentation. The goal is to make steps two
and three usable by the rescan functionality. A side effect is that the
function is restructured into readable subunits.

Signed-off-by: default avatarJan Schmidt <list.btrfs@jan-o-sch.net>
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
parent 3c76cd84
Loading
Loading
Loading
Loading
+148 −105
Original line number Diff line number Diff line
@@ -1185,6 +1185,144 @@ int btrfs_qgroup_record_ref(struct btrfs_trans_handle *trans,
	return 0;
}

static int qgroup_account_ref_step1(struct btrfs_fs_info *fs_info,
				    struct ulist *roots, struct ulist *tmp,
				    u64 seq)
{
	struct ulist_node *unode;
	struct ulist_iterator uiter;
	struct ulist_node *tmp_unode;
	struct ulist_iterator tmp_uiter;
	struct btrfs_qgroup *qg;
	int ret;

	ULIST_ITER_INIT(&uiter);
	while ((unode = ulist_next(roots, &uiter))) {
		qg = find_qgroup_rb(fs_info, unode->val);
		if (!qg)
			continue;

		ulist_reinit(tmp);
						/* XXX id not needed */
		ret = ulist_add(tmp, qg->qgroupid,
				(u64)(uintptr_t)qg, GFP_ATOMIC);
		if (ret < 0)
			return ret;
		ULIST_ITER_INIT(&tmp_uiter);
		while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
			struct btrfs_qgroup_list *glist;

			qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
			if (qg->refcnt < seq)
				qg->refcnt = seq + 1;
			else
				++qg->refcnt;

			list_for_each_entry(glist, &qg->groups, next_group) {
				ret = ulist_add(tmp, glist->group->qgroupid,
						(u64)(uintptr_t)glist->group,
						GFP_ATOMIC);
				if (ret < 0)
					return ret;
			}
		}
	}

	return 0;
}

static int qgroup_account_ref_step2(struct btrfs_fs_info *fs_info,
				    struct ulist *roots, struct ulist *tmp,
				    u64 seq, int sgn, u64 num_bytes,
				    struct btrfs_qgroup *qgroup)
{
	struct ulist_node *unode;
	struct ulist_iterator uiter;
	struct btrfs_qgroup *qg;
	struct btrfs_qgroup_list *glist;
	int ret;

	ulist_reinit(tmp);
	ret = ulist_add(tmp, qgroup->qgroupid, (uintptr_t)qgroup, GFP_ATOMIC);
	if (ret < 0)
		return ret;

	ULIST_ITER_INIT(&uiter);
	while ((unode = ulist_next(tmp, &uiter))) {
		qg = (struct btrfs_qgroup *)(uintptr_t)unode->aux;
		if (qg->refcnt < seq) {
			/* not visited by step 1 */
			qg->rfer += sgn * num_bytes;
			qg->rfer_cmpr += sgn * num_bytes;
			if (roots->nnodes == 0) {
				qg->excl += sgn * num_bytes;
				qg->excl_cmpr += sgn * num_bytes;
			}
			qgroup_dirty(fs_info, qg);
		}
		WARN_ON(qg->tag >= seq);
		qg->tag = seq;

		list_for_each_entry(glist, &qg->groups, next_group) {
			ret = ulist_add(tmp, glist->group->qgroupid,
					(uintptr_t)glist->group, GFP_ATOMIC);
			if (ret < 0)
				return ret;
		}
	}

	return 0;
}

static int qgroup_account_ref_step3(struct btrfs_fs_info *fs_info,
				    struct ulist *roots, struct ulist *tmp,
				    u64 seq, int sgn, u64 num_bytes)
{
	struct ulist_node *unode;
	struct ulist_iterator uiter;
	struct btrfs_qgroup *qg;
	struct ulist_node *tmp_unode;
	struct ulist_iterator tmp_uiter;
	int ret;

	ULIST_ITER_INIT(&uiter);
	while ((unode = ulist_next(roots, &uiter))) {
		qg = find_qgroup_rb(fs_info, unode->val);
		if (!qg)
			continue;

		ulist_reinit(tmp);
		ret = ulist_add(tmp, qg->qgroupid, (uintptr_t)qg, GFP_ATOMIC);
		if (ret < 0)
			return ret;

		ULIST_ITER_INIT(&tmp_uiter);
		while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
			struct btrfs_qgroup_list *glist;

			qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
			if (qg->tag == seq)
				continue;

			if (qg->refcnt - seq == roots->nnodes) {
				qg->excl -= sgn * num_bytes;
				qg->excl_cmpr -= sgn * num_bytes;
				qgroup_dirty(fs_info, qg);
			}

			list_for_each_entry(glist, &qg->groups, next_group) {
				ret = ulist_add(tmp, glist->group->qgroupid,
						(uintptr_t)glist->group,
						GFP_ATOMIC);
				if (ret < 0)
					return ret;
			}
		}
	}

	return 0;
}

/*
 * btrfs_qgroup_account_ref is called for every ref that is added to or deleted
 * from the fs. First, all roots referencing the extent are searched, and
@@ -1200,10 +1338,8 @@ int btrfs_qgroup_account_ref(struct btrfs_trans_handle *trans,
	struct btrfs_root *quota_root;
	u64 ref_root;
	struct btrfs_qgroup *qgroup;
	struct ulist_node *unode;
	struct ulist *roots = NULL;
	struct ulist *tmp = NULL;
	struct ulist_iterator uiter;
	u64 seq;
	int ret = 0;
	int sgn;
@@ -1287,119 +1423,26 @@ int btrfs_qgroup_account_ref(struct btrfs_trans_handle *trans,
	seq = fs_info->qgroup_seq;
	fs_info->qgroup_seq += roots->nnodes + 1; /* max refcnt */

	ULIST_ITER_INIT(&uiter);
	while ((unode = ulist_next(roots, &uiter))) {
		struct ulist_node *tmp_unode;
		struct ulist_iterator tmp_uiter;
		struct btrfs_qgroup *qg;

		qg = find_qgroup_rb(fs_info, unode->val);
		if (!qg)
			continue;

		ulist_reinit(tmp);
						/* XXX id not needed */
		ret = ulist_add(tmp, qg->qgroupid,
				(u64)(uintptr_t)qg, GFP_ATOMIC);
		if (ret < 0)
			goto unlock;
		ULIST_ITER_INIT(&tmp_uiter);
		while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
			struct btrfs_qgroup_list *glist;

			qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
			if (qg->refcnt < seq)
				qg->refcnt = seq + 1;
			else
				++qg->refcnt;

			list_for_each_entry(glist, &qg->groups, next_group) {
				ret = ulist_add(tmp, glist->group->qgroupid,
						(u64)(uintptr_t)glist->group,
						GFP_ATOMIC);
				if (ret < 0)
	ret = qgroup_account_ref_step1(fs_info, roots, tmp, seq);
	if (ret)
		goto unlock;
			}
		}
	}

	/*
	 * step 2: walk from the new root
	 */
	ulist_reinit(tmp);
	ret = ulist_add(tmp, qgroup->qgroupid,
			(uintptr_t)qgroup, GFP_ATOMIC);
	if (ret < 0)
		goto unlock;
	ULIST_ITER_INIT(&uiter);
	while ((unode = ulist_next(tmp, &uiter))) {
		struct btrfs_qgroup *qg;
		struct btrfs_qgroup_list *glist;

		qg = (struct btrfs_qgroup *)(uintptr_t)unode->aux;
		if (qg->refcnt < seq) {
			/* not visited by step 1 */
			qg->rfer += sgn * node->num_bytes;
			qg->rfer_cmpr += sgn * node->num_bytes;
			if (roots->nnodes == 0) {
				qg->excl += sgn * node->num_bytes;
				qg->excl_cmpr += sgn * node->num_bytes;
			}
			qgroup_dirty(fs_info, qg);
		}
		WARN_ON(qg->tag >= seq);
		qg->tag = seq;

		list_for_each_entry(glist, &qg->groups, next_group) {
			ret = ulist_add(tmp, glist->group->qgroupid,
					(uintptr_t)glist->group, GFP_ATOMIC);
			if (ret < 0)
	ret = qgroup_account_ref_step2(fs_info, roots, tmp, seq, sgn,
				       node->num_bytes, qgroup);
	if (ret)
		goto unlock;
		}
	}

	/*
	 * step 3: walk again from old refs
	 */
	ULIST_ITER_INIT(&uiter);
	while ((unode = ulist_next(roots, &uiter))) {
		struct btrfs_qgroup *qg;
		struct ulist_node *tmp_unode;
		struct ulist_iterator tmp_uiter;

		qg = find_qgroup_rb(fs_info, unode->val);
		if (!qg)
			continue;

		ulist_reinit(tmp);
		ret = ulist_add(tmp, qg->qgroupid,
				(uintptr_t)qg, GFP_ATOMIC);
		if (ret < 0)
	ret = qgroup_account_ref_step3(fs_info, roots, tmp, seq, sgn,
				       node->num_bytes);
	if (ret)
		goto unlock;
		ULIST_ITER_INIT(&tmp_uiter);
		while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
			struct btrfs_qgroup_list *glist;

			qg = (struct btrfs_qgroup *)(uintptr_t)tmp_unode->aux;
			if (qg->tag == seq)
				continue;

			if (qg->refcnt - seq == roots->nnodes) {
				qg->excl -= sgn * node->num_bytes;
				qg->excl_cmpr -= sgn * node->num_bytes;
				qgroup_dirty(fs_info, qg);
			}

			list_for_each_entry(glist, &qg->groups, next_group) {
				ret = ulist_add(tmp, glist->group->qgroupid,
						(uintptr_t)glist->group,
						GFP_ATOMIC);
				if (ret < 0)
					goto unlock;
			}
		}
	}
	ret = 0;
unlock:
	spin_unlock(&fs_info->qgroup_lock);
	ulist_free(roots);