]> git.hungrycats.org Git - linux/commitdiff
btrfs: avoid long stall when dropping a non-shared large subvolume
authorQu Wenruo <wqu@suse.com>
Thu, 27 Aug 2026 06:55:30 +0000 (16:25 +0930)
committerDavid Sterba <dsterba@suse.com>
Mon, 14 Sep 2026 11:23:36 +0000 (13:23 +0200)
Commit 011b46c30476 ("btrfs: skip subtree scan if it's too high to avoid
low stall in btrfs_commit_transaction()") introduced a mechanism to skip
large subtree during snapshot dropping.

But even for a subvolume without any shared tree blocks, we can still
queue quite a lot of qgroup records into one transaction, and cause a
long qgroup related stall.

So also add a check against the subvolume root level, to determine if we
need to mark qgroup inconsistent.

Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent-tree.c
fs/btrfs/qgroup.c
fs/btrfs/qgroup.h

index d6a4390ee34ac973ca247d034696a8cafda7cccc..a0d5ab03aae264a83f7f05bba15c947cbc40facd 100644 (file)
@@ -6315,6 +6315,16 @@ int btrfs_drop_snapshot(struct btrfs_root *root, bool update_ref, bool for_reloc
        set_bit(BTRFS_ROOT_DELETING, &root->state);
        unfinished_drop = test_bit(BTRFS_ROOT_UNFINISHED_DROP, &root->state);
 
+       /*
+        * For subvolume dropping, check if the subvolume is large enough so
+        * that we need to mark qgroup inconsistent to avoid long qgroup stall.
+        *
+        * Even for a subvolume without any snapshot, there can still be
+        * a lot of qgroup records queued into one transaction.
+        */
+       if (!for_reloc)
+               btrfs_qgroup_check_tree_drop(fs_info, rootid,
+                                            btrfs_header_level(root->node));
        if (btrfs_disk_key_objectid(&root_item->drop_progress) == 0) {
                level = btrfs_header_level(root->node);
                path->nodes[level] = btrfs_lock_root_node(root);
index e01b31aa0b1bf0809c853048a9c631d8ebc2a5b5..05e35eb126dc5b4529f0ac41146f9c8b30367508 100644 (file)
@@ -2748,6 +2748,24 @@ walk_down:
        return 0;
 }
 
+void btrfs_qgroup_check_tree_drop(struct btrfs_fs_info *fs_info, u64 rootid, u8 level)
+{
+       u8 drop_subtree_thres;
+
+       if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_FULL)
+               return;
+
+       if (!btrfs_is_fstree(rootid))
+               return;
+
+       spin_lock(&fs_info->qgroup_lock);
+       drop_subtree_thres = fs_info->qgroup_drop_subtree_thres;
+       spin_unlock(&fs_info->qgroup_lock);
+
+       if (level >= drop_subtree_thres)
+               qgroup_mark_inconsistent(fs_info, "subtree level reached threshold");
+}
+
 static void qgroup_iterator_nested_add(struct list_head *head, struct btrfs_qgroup *qgroup)
 {
        if (!list_empty(&qgroup->nested_iterator))
index 090ba53678726986a6e95e4e04aa5e3386771726..c64b26b09c22f25ba8befe20dd122afdcd6cd4e8 100644 (file)
@@ -376,6 +376,7 @@ int btrfs_qgroup_trace_leaf_items(struct btrfs_trans_handle *trans,
 int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
                               struct extent_buffer *root_eb,
                               u64 root_gen, int root_level);
+void btrfs_qgroup_check_tree_drop(struct btrfs_fs_info *fs_info, u64 rootid, u8 level);
 int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
                                u64 num_bytes, struct ulist *old_roots,
                                struct ulist *new_roots);