summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2026-08-27 16:25:29 +0930
committerDavid Sterba <dsterba@suse.com>2026-09-14 13:23:36 +0200
commit1d7404d4daeacbebe5a75f812dd6e45e3340d340 (patch)
tree2573ef8a4a237a03d16c23d703c4a61378a52fac
parentf5e60e8d7080d29739a4f056d5dbc28eafd4578e (diff)
downloadlinux-next-1d7404d4daeacbebe5a75f812dd6e45e3340d340.tar.gz
linux-next-1d7404d4daeacbebe5a75f812dd6e45e3340d340.zip
btrfs: reject new qgroup rescan during subvolume dropping
Commit 011b46c30476 ("btrfs: skip subtree scan if it's too high to avoid low stall in btrfs_commit_transaction()") introduced a threshold to skip huge subtree scan during subvolume dropping. But that's not covering all cases, e.g. rescan can still be started immediately after that huge subtree skipping. This will cause rescan to do the same accounting for that subtree anyway, still causing a long stall during transaction commit. Introduce a new runtime qgroup flag, BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN, so that during cleanup of a subvolume, no new qgroup rescan can be initiated. The rejection uses the same -EINPROGRESS, as if there is already a running qgroup rescan. And since we have the extra bit, we can no longer allow plain assignment in btrfs_quota_enable(), as the plain assignment will override the REJECT_RESCAN bit. To co-operate this new flag: - Make btrfs_quota_enable() to only set BTRFS_QGROUP_STATUS_BIT_ON So it won't override the existing BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN bit. - Make btrfs_quota_disable() to clear every non-rescan bit This includes: * BTRFS_QGROUP_STATUS_BIT_ON * BTRFS_QGROUP_STATUS_BIT_INCONSISTENT * BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING For rescan related bits, they are either cleared by the rescan thread, or by the caller who rejects rescan. Reviewed-by: Boris Burkov <boris@bur.io> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/disk-io.c2
-rw-r--r--fs/btrfs/qgroup.c13
-rw-r--r--fs/btrfs/qgroup.h11
3 files changed, 24 insertions, 2 deletions
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 466fadb1815a..a1d83ad9a4c0 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1496,7 +1496,9 @@ static int cleaner_kthread(void *arg)
btrfs_run_delayed_iputs(fs_info);
+ set_bit(BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN, &fs_info->qgroup_flags);
again = btrfs_clean_one_deleted_snapshot(fs_info);
+ clear_bit(BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN, &fs_info->qgroup_flags);
mutex_unlock(&fs_info->cleaner_mutex);
/*
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 2c2ac0f16b1e..e01b31aa0b1b 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -1103,7 +1103,7 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
struct btrfs_qgroup_status_item);
btrfs_set_qgroup_status_generation(leaf, ptr, trans->transid);
btrfs_set_qgroup_status_version(leaf, ptr, BTRFS_QGROUP_STATUS_VERSION);
- fs_info->qgroup_flags = (1UL << BTRFS_QGROUP_STATUS_BIT_ON);
+ set_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags);
if (simple) {
set_bit(BTRFS_QGROUP_STATUS_BIT_SIMPLE_MODE, &fs_info->qgroup_flags);
btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA);
@@ -1405,8 +1405,14 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
spin_lock(&fs_info->qgroup_lock);
quota_root = fs_info->quota_root;
fs_info->quota_root = NULL;
+ /*
+ * Clear all on-disk and runtime bits, except RESCAN related ones, that
+ * are either handled by rescan thread, or the caller who rejects rescan.
+ */
clear_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags);
clear_bit(BTRFS_QGROUP_STATUS_BIT_SIMPLE_MODE, &fs_info->qgroup_flags);
+ clear_bit(BTRFS_QGROUP_STATUS_BIT_INCONSISTENT, &fs_info->qgroup_flags);
+ clear_bit(BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING, &fs_info->qgroup_flags);
fs_info->qgroup_drop_subtree_thres = BTRFS_QGROUP_DROP_SUBTREE_THRES_DEFAULT;
spin_unlock(&fs_info->qgroup_lock);
@@ -3990,7 +3996,10 @@ qgroup_rescan_init(struct btrfs_fs_info *fs_info, u64 progress_objectid,
mutex_lock(&fs_info->qgroup_rescan_lock);
if (init_flags) {
- if (test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags)) {
+ if (test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN,
+ &fs_info->qgroup_flags) ||
+ test_bit(BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN,
+ &fs_info->qgroup_flags)) {
ret = -EINPROGRESS;
} else if (!test_bit(BTRFS_QGROUP_STATUS_BIT_ON, &fs_info->qgroup_flags)) {
btrfs_debug(fs_info,
diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h
index b3aaad5e617d..090ba5367872 100644
--- a/fs/btrfs/qgroup.h
+++ b/fs/btrfs/qgroup.h
@@ -124,6 +124,17 @@ struct btrfs_qgroup_swapped_blocks;
#define BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN (BITS_PER_LONG - 1)
#define BTRFS_QGROUP_RUNTIME_BIT_NO_ACCOUNTING (BITS_PER_LONG - 2)
+/*
+ * No new rescan allowed when set.
+ *
+ * During huge subtree dropping, qgroup will be marked inconsistent, and skip
+ * all future accounting to avoid long stall. But, an immediate rescan will
+ * re-enable qgroup and still stall the system.
+ *
+ * This bit is to avoid such rescan during the duration of a subvolume dropping.
+ */
+#define BTRFS_QGROUP_RUNTIME_BIT_REJECT_RESCAN (BITS_PER_LONG - 3)
+
#define BTRFS_QGROUP_DROP_SUBTREE_THRES_DEFAULT (3)
/*