Commit b20fe56c authored by Qu Wenruo's avatar Qu Wenruo Committed by David Sterba
Browse files

btrfs: qgroup: allow quick inherit if snapshot is created and added to the same parent



Currently "btrfs subvolume snapshot -i <qgroupid>" would always mark the
qgroup inconsistent.

This can be annoying if the fs has a lot of snapshots, and needs qgroup
to get the accounting for the amount of bytes it can free for each
snapshot.

Although we have the new simple quote as a solution, there is also a
case where we can skip the full scan, if all the following conditions
are met:

- The source subvolume belongs to a higher level parent qgroup
- The parent qgroup already owns all its bytes exclusively
- The new snapshot is also added to the same parent qgroup

In that case, we only need to add nodesize to the parent qgroup and
avoid a full rescan.

This patch would add the extra quick accounting update for such inherit.

Signed-off-by: default avatarQu Wenruo <wqu@suse.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 86211eea
Loading
Loading
Loading
Loading
+72 −7
Original line number Diff line number Diff line
@@ -3138,6 +3138,62 @@ static int qgroup_auto_inherit(struct btrfs_fs_info *fs_info,
	return 0;
}

/*
 * Check if we can skip rescan when inheriting qgroups.  If @src has a single
 * @parent, and that @parent is owning all its bytes exclusively, we can skip
 * the full rescan, by just adding nodesize to the @parent's excl/rfer.
 *
 * Return <0 for fatal errors (like srcid/parentid has no qgroup).
 * Return 0 if a quick inherit is done.
 * Return >0 if a quick inherit is not possible, and a full rescan is needed.
 */
static int qgroup_snapshot_quick_inherit(struct btrfs_fs_info *fs_info,
					 u64 srcid, u64 parentid)
{
	struct btrfs_qgroup *src;
	struct btrfs_qgroup *parent;
	struct btrfs_qgroup_list *list;
	int nr_parents = 0;

	src = find_qgroup_rb(fs_info, srcid);
	if (!src)
		return -ENOENT;
	parent = find_qgroup_rb(fs_info, parentid);
	if (!parent)
		return -ENOENT;

	/*
	 * Source has no parent qgroup, but our new qgroup would have one.
	 * Qgroup numbers would become inconsistent.
	 */
	if (list_empty(&src->groups))
		return 1;

	list_for_each_entry(list, &src->groups, next_group) {
		/* The parent is not the same, quick update is not possible. */
		if (list->group->qgroupid != parentid)
			return 1;
		nr_parents++;
		/*
		 * More than one parent qgroup, we can't be sure about accounting
		 * consistency.
		 */
		if (nr_parents > 1)
			return 1;
	}

	/*
	 * The parent is not exclusively owning all its bytes.  We're not sure
	 * if the source has any bytes not fully owned by the parent.
	 */
	if (parent->excl != parent->rfer)
		return 1;

	parent->excl += fs_info->nodesize;
	parent->rfer += fs_info->nodesize;
	return 0;
}

/*
 * Copy the accounting information between qgroups. This is necessary
 * when a snapshot or a subvolume is created. Throwing an error will
@@ -3306,6 +3362,13 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,

		qgroup_dirty(fs_info, dstgroup);
		qgroup_dirty(fs_info, srcgroup);

		/*
		 * If the source qgroup has parent but the new one doesn't,
		 * we need a full rescan.
		 */
		if (!inherit && !list_empty(&srcgroup->groups))
			need_rescan = true;
	}

	if (!inherit)
@@ -3320,14 +3383,16 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
			if (ret)
				goto unlock;
		}
		++i_qgroups;

		/*
		 * If we're doing a snapshot, and adding the snapshot to a new
		 * qgroup, the numbers are guaranteed to be incorrect.
		 */
		if (srcid)
		if (srcid) {
			/* Check if we can do a quick inherit. */
			ret = qgroup_snapshot_quick_inherit(fs_info, srcid, *i_qgroups);
			if (ret < 0)
				goto unlock;
			if (ret > 0)
				need_rescan = true;
			ret = 0;
		}
		++i_qgroups;
	}

	for (i = 0; i <  inherit->num_ref_copies; ++i, i_qgroups += 2) {