diff mbox series

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

Message ID 5dea98d4f3749f895402164c3cba61b176ff3b2e.1708919464.git.wqu@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: qgroup: allow quick inherit if a snapshot if created and added to the same parent | expand

Commit Message

Qu Wenruo Feb. 26, 2024, 3:51 a.m. UTC
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: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/qgroup.c | 74 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 67 insertions(+), 7 deletions(-)

Comments

David Sterba Feb. 28, 2024, 2:01 p.m. UTC | #1
On Mon, Feb 26, 2024 at 02:21:13PM +1030, Qu Wenruo wrote:
> 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.

Sounds reasonable and safe.
> 
> This patch would add the extra quick accounting update for such inherit.

"Add th extra ..."

> Signed-off-by: Qu Wenruo <wqu@suse.com>

Reviewed-by: David Sterba <dsterba@suse.com>

>  fs/btrfs/qgroup.c | 74 ++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 67 insertions(+), 7 deletions(-)
> 
> +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;
> +
> +	list_for_each_entry(list, &src->groups, next_group) {
> +		/* The parent is not the same, quick update not possible. */
> +		if (list->group->qgroupid != parentid)
> +			return 1;
> +		nr_parents++;
> +	}
> +	/*
> +	 * The source has multiple parents, do not bother it and require a
> +	 * full rescan.
> +	 */
> +	if (nr_parents != 1)
> +		return 1;

You can put the check to the list_for_each above so it does not continue
once it's known that there are more parent qgroups.
Qu Wenruo Feb. 28, 2024, 10:56 p.m. UTC | #2
在 2024/2/29 00:31, David Sterba 写道:
> On Mon, Feb 26, 2024 at 02:21:13PM +1030, Qu Wenruo wrote:
>> 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.
> 
> Sounds reasonable and safe.
>>
>> This patch would add the extra quick accounting update for such inherit.
> 
> "Add th extra ..."
> 
>> Signed-off-by: Qu Wenruo <wqu@suse.com>
> 
> Reviewed-by: David Sterba <dsterba@suse.com>
> 
>>   fs/btrfs/qgroup.c | 74 ++++++++++++++++++++++++++++++++++++++++++-----
>>   1 file changed, 67 insertions(+), 7 deletions(-)
>>
>> +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;
>> +
>> +	list_for_each_entry(list, &src->groups, next_group) {
>> +		/* The parent is not the same, quick update not possible. */
>> +		if (list->group->qgroupid != parentid)
>> +			return 1;
>> +		nr_parents++;
>> +	}
>> +	/*
>> +	 * The source has multiple parents, do not bother it and require a
>> +	 * full rescan.
>> +	 */
>> +	if (nr_parents != 1)
>> +		return 1;
> 
> You can put the check to the list_for_each above so it does not continue
> once it's known that there are more parent qgroups.

Let me check if I can do a single line check to make sure a list only 
contains one entry.

Thanks,
Qu
diff mbox series

Patch

diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index b3bf08fc2a39..4fa83c76b37b 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3087,6 +3087,56 @@  static int qgroup_auto_inherit(struct btrfs_fs_info *fs_info,
 	return 0;
 }
 
+/*
+ * 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;
+
+	list_for_each_entry(list, &src->groups, next_group) {
+		/* The parent is not the same, quick update not possible. */
+		if (list->group->qgroupid != parentid)
+			return 1;
+		nr_parents++;
+	}
+	/*
+	 * The source has multiple parents, do not bother it and require a
+	 * full rescan.
+	 */
+	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
@@ -3255,6 +3305,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)
@@ -3269,14 +3326,17 @@  int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
 			if (ret)
 				goto unlock;
 		}
+		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;
-
-		/*
-		 * If we're doing a snapshot, and adding the snapshot to a new
-		 * qgroup, the numbers are guaranteed to be incorrect.
-		 */
-		if (srcid)
-			need_rescan = true;
 	}
 
 	for (i = 0; i <  inherit->num_ref_copies; ++i, i_qgroups += 2) {