diff mbox series

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

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

Commit Message

Qu Wenruo Feb. 29, 2024, 3:34 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.

Add the extra quick accounting update for such inherit.

Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
 fs/btrfs/qgroup.c | 74 ++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 67 insertions(+), 7 deletions(-)
---
Changelog:
v2:
- Move the case of source subvolume without a parent into
  qgroup_snapshot_quick_inherit()

- Exit early if the source subvolume has too many parents

- Small commit message update on the last sentence

Comments

David Sterba Feb. 29, 2024, 8:07 p.m. UTC | #1
On Thu, Feb 29, 2024 at 02:04:44PM +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.
> 
> Add the extra quick accounting update for such inherit.
> 
> Reviewed-by: David Sterba <dsterba@suse.com>
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> ---
>  fs/btrfs/qgroup.c | 74 ++++++++++++++++++++++++++++++++++++++++++-----
>  1 file changed, 67 insertions(+), 7 deletions(-)
> ---
> Changelog:
> v2:
> - Move the case of source subvolume without a parent into
>   qgroup_snapshot_quick_inherit()
> 
> - Exit early if the source subvolume has too many parents
> 
> - Small commit message update on the last sentence

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

Patch

diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index b3bf08fc2a39..4fa513a6c91e 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3087,6 +3087,63 @@  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;
+
+	/*
+	 * Source has no parent qgroup, but our new qgroup would have one.
+	 * Qgroup numbers would go 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 not possible. */
+		if (list->group->qgroupid != parentid)
+			return 1;
+		nr_parents++;
+		/*
+		 * More than one parent qgroups, we're not sure about if qgroup
+		 * would be inconsistent.
+		 */
+		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
@@ -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) {