* [PATCH] btrfs: make qgroup rescan work to handle fs freezing
@ 2026-09-21 7:03 ` Qu Wenruo
2026-09-21 8:36 ` Dongjiang Zhu
` (2 more replies)
0 siblings, 3 replies; 18+ messages in thread
From: Qu Wenruo @ 2026-09-21 7:03 UTC (permalink / raw)
To: linux-btrfs; +Cc: Richard Weinberger
[BUG]
There is a bug report that a running qgroup rescan can fail a pm
suspension:
[ T278489] Freezing remaining freezable tasks
[ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
[ T278489] Showing freezable workqueues that are still busy:
[...]
[ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
[ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
[CAUSE]
Btrfs qgroup rescan is running in a workqueue, and when the fs is
frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
But a sleeping workload still counts as active for the workqueue until
the workload exits.
So the qgroup rescan item will sleep on the frozen fs, and fail the pm
suspension.
[FIX]
I strongly doubt whether we should even use a workqueue for the qgroup
rescan workload, a kthread would be a more suitable choice and can handle
fs and process freezing way better.
But that will be a long term solution.
For now fix the problems by:
- make rescan_should_stop() to return true if the fs is frozen
This should handle most cases.
- Use btrfs_try_start_transaction() to avoid sleeping on
sb_start_intwrite()
This is less common but still possible, and if we sleep on
sb_start_intwrite(), the workqueue will never have a chance to
deactivate.
The above fixes should interrupt the rescan, so that pm suspension won't
fail.
And since the INCONSISTENT flag is not cleared, the end user/tool should
re-start a rescan if they need uptodate qgroup numbers.
And since we're here, also remove the "if (!trans) return;" check, and
end the transaction if there is one, so that the error message is always
shown.
Reported-by: Richard Weinberger <richard@nod.at>
Link: https://lore.kernel.org/linux-btrfs/mu8fq8a8.b9a9a1ae-dd6b-45a4-a415-5db853d61536@nod.at/
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 14 ++++++++------
fs/btrfs/transaction.c | 22 +++++++++++++++++++---
fs/btrfs/transaction.h | 4 ++++
3 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 05e35eb126dc..0390a17561e2 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3866,6 +3866,8 @@ static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
{
if (btrfs_fs_closing(fs_info))
return true;
+ if (fs_info->sb->s_writers.frozen > SB_UNFROZEN)
+ return true;
if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state))
return true;
if (!btrfs_qgroup_enabled(fs_info))
@@ -3901,9 +3903,11 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
path->skip_locking = true;
while (!ret && !(stopped = rescan_should_stop(fs_info))) {
- trans = btrfs_start_transaction(fs_info->fs_root, 0);
+ trans = btrfs_try_start_transaction(fs_info->fs_root, 0);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
+ if (ret == -EINTR)
+ stopped = true;
break;
}
@@ -3934,7 +3938,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
* btrfs_quota_disable().
*/
if (did_leaf_rescans) {
- trans = btrfs_start_transaction(fs_info->quota_root, 1);
+ trans = btrfs_try_start_transaction(fs_info->quota_root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
trans = NULL;
@@ -3963,10 +3967,8 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
complete_all(&fs_info->qgroup_rescan_completion);
mutex_unlock(&fs_info->qgroup_rescan_lock);
- if (!trans)
- return;
-
- btrfs_end_transaction(trans);
+ if (trans)
+ btrfs_end_transaction(trans);
if (stopped) {
btrfs_info(fs_info, "qgroup scan paused");
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index ca114235bbe1..76787417331e 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -559,7 +559,7 @@ static bool may_wait_transaction(struct btrfs_fs_info *fs_info, int type)
if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags))
return false;
- if (type == TRANS_START)
+ if ((type & TRANS_START) == TRANS_START)
return true;
return false;
@@ -719,8 +719,17 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,
* If we are ATTACH, it means we just want to catch the current
* transaction and commit it, so we needn't do sb_start_intwrite().
*/
- if (type & __TRANS_FREEZABLE)
- sb_start_intwrite(fs_info->sb);
+ if (type & __TRANS_FREEZABLE) {
+ if (type & __TRANS_TRYLOCK) {
+ if (!sb_start_intwrite_trylock(fs_info->sb)) {
+ ret = -EINTR;
+ kmem_cache_free(btrfs_trans_handle_cachep, h);
+ goto alloc_fail;
+ }
+ } else {
+ sb_start_intwrite(fs_info->sb);
+ }
+ }
if (may_wait_transaction(fs_info, type))
wait_current_trans(fs_info, type);
@@ -841,6 +850,13 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
BTRFS_RESERVE_FLUSH_ALL, true);
}
+struct btrfs_trans_handle *btrfs_try_start_transaction(struct btrfs_root *root,
+ unsigned int num_items)
+{
+ return start_transaction(root, num_items, TRANS_TRY_START,
+ BTRFS_RESERVE_FLUSH_ALL, true);
+}
+
struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
struct btrfs_root *root,
unsigned int num_items)
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 89153cd22596..a9da059d3806 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -127,9 +127,11 @@ enum {
ENUM_BIT(__TRANS_JOIN_NOLOCK),
ENUM_BIT(__TRANS_DUMMY),
ENUM_BIT(__TRANS_JOIN_NOSTART),
+ ENUM_BIT(__TRANS_TRYLOCK),
};
#define TRANS_START (__TRANS_START | __TRANS_FREEZABLE)
+#define TRANS_TRY_START (__TRANS_START | __TRANS_FREEZABLE | __TRANS_TRYLOCK)
#define TRANS_ATTACH (__TRANS_ATTACH)
#define TRANS_JOIN (__TRANS_JOIN | __TRANS_FREEZABLE)
#define TRANS_JOIN_NOLOCK (__TRANS_JOIN_NOLOCK)
@@ -308,6 +310,8 @@ do { \
int btrfs_end_transaction(struct btrfs_trans_handle *trans);
struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
unsigned int num_items);
+struct btrfs_trans_handle *btrfs_try_start_transaction(struct btrfs_root *root,
+ unsigned int num_items);
struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
struct btrfs_root *root,
unsigned int num_items);
--
2.55.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-21 7:03 ` [PATCH] " Qu Wenruo
@ 2026-09-21 8:36 ` Dongjiang Zhu
2026-09-21 13:12 ` David Sterba
2026-09-21 9:16 ` Qu Wenruo
2026-09-21 13:18 ` David Sterba
2 siblings, 1 reply; 18+ messages in thread
From: Dongjiang Zhu @ 2026-09-21 8:36 UTC (permalink / raw)
To: Qu Wenruo, linux-btrfs; +Cc: Richard Weinberger
在 2026/9/21 15:03, Qu Wenruo 写道:
> [BUG]
> There is a bug report that a running qgroup rescan can fail a pm
> suspension:
>
> [ T278489] Freezing remaining freezable tasks
> [ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
> [ T278489] Showing freezable workqueues that are still busy:
> [...]
> [ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
> [ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
>
> [CAUSE]
> Btrfs qgroup rescan is running in a workqueue, and when the fs is
> frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
>
> But a sleeping workload still counts as active for the workqueue until
> the workload exits.
>
> So the qgroup rescan item will sleep on the frozen fs, and fail the pm
> suspension.
>
> [FIX]
> I strongly doubt whether we should even use a workqueue for the qgroup
> rescan workload, a kthread would be a more suitable choice and can handle
> fs and process freezing way better.
> But that will be a long term solution.
Hi Qu,
Thanks for working on this.
I recently posted an RFC series addressing qgroup rescan lifecycle
issues around quota disable/enable, error cleanup, and remounts [1].
From the recent discussion, I got the impression that full qgroups
might be phased out or substantially reworked. Is that a fair
understanding? Would focused lifecycle fixes like these still be
useful in the meantime? I'd appreciate your thoughts on how best to
continue this work.
> [...]
> The above fixes should interrupt the rescan, so that pm suspension won't
> fail.
> And since the INCONSISTENT flag is not cleared, the end user/tool should
> re-start a rescan if they need uptodate qgroup numbers.
Also, the Sashiko review points out that RESCAN remains set when the
worker stops for freezing, preventing a new rescan after thawing [2].
This looks similar to the cleanup issue addressed by patch 4 in my
series, where stopping due to quota disable also leaves RESCAN set.
[1]
https://lore.kernel.org/linux-btrfs/cover.1789524388.git.zhudongjiang@fygo.io/
[2]
https://sashiko.dev/#/patchset/cfc34be50699e2e0e5c84e4d57345c7e0506caf8.1789974081.git.wqu%40suse.com
Thanks,
Dongjiang
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-21 8:36 ` Dongjiang Zhu
@ 2026-09-21 13:12 ` David Sterba
2026-09-21 13:34 ` Dongjiang Zhu
` (2 more replies)
0 siblings, 3 replies; 18+ messages in thread
From: David Sterba @ 2026-09-21 13:12 UTC (permalink / raw)
To: Dongjiang Zhu; +Cc: Qu Wenruo, linux-btrfs, Richard Weinberger
On Mon, Sep 21, 2026 at 04:36:51PM +0800, Dongjiang Zhu wrote:
> 在 2026/9/21 15:03, Qu Wenruo 写道:
> > [BUG]
> > There is a bug report that a running qgroup rescan can fail a pm
> > suspension:
> >
> > [ T278489] Freezing remaining freezable tasks
> > [ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
> > [ T278489] Showing freezable workqueues that are still busy:
> > [...]
> > [ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
> > [ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
> >
> > [CAUSE]
> > Btrfs qgroup rescan is running in a workqueue, and when the fs is
> > frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
> >
> > But a sleeping workload still counts as active for the workqueue until
> > the workload exits.
> >
> > So the qgroup rescan item will sleep on the frozen fs, and fail the pm
> > suspension.
> >
> > [FIX]
> > I strongly doubt whether we should even use a workqueue for the qgroup
> > rescan workload, a kthread would be a more suitable choice and can handle
> > fs and process freezing way better.
> > But that will be a long term solution.
>
> Hi Qu,
>
> Thanks for working on this.
>
> I recently posted an RFC series addressing qgroup rescan lifecycle
> issues around quota disable/enable, error cleanup, and remounts [1].
>
> From the recent discussion, I got the impression that full qgroups
> might be phased out or substantially reworked. Is that a fair
> understanding?
Do you have link to the dicussion? Phasing out current qgroups in the
full mode would be a functionality loss and we don't have a replacement.
On the design level, the qgroups are general enough to cover the COW
design, sharing. The compression was originally intended but IIRC we
don't account compressed extents separately.
Reworking could happen, the performance hit of qgroups in full mode has
been a problem since beginning, but I don't see any easy change there.
If the sematics change we'd need to add another mode, like we have the
simple quota mode.
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-21 13:12 ` David Sterba
@ 2026-09-21 13:34 ` Dongjiang Zhu
2026-09-21 21:46 ` Qu Wenruo
2026-09-22 6:24 ` Andrei Borzenkov
2 siblings, 0 replies; 18+ messages in thread
From: Dongjiang Zhu @ 2026-09-21 13:34 UTC (permalink / raw)
To: dsterba; +Cc: Qu Wenruo, linux-btrfs, Richard Weinberger
在 2026/9/21 21:12, David Sterba 写道:
> On Mon, Sep 21, 2026 at 04:36:51PM +0800, Dongjiang Zhu wrote:
>> 在 2026/9/21 15:03, Qu Wenruo 写道:
>>> [BUG]
>>> There is a bug report that a running qgroup rescan can fail a pm
>>> suspension:
>>>
>>> [ T278489] Freezing remaining freezable tasks
>>> [ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
>>> [ T278489] Showing freezable workqueues that are still busy:
>>> [...]
>>> [ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
>>> [ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
>>>
>>> [CAUSE]
>>> Btrfs qgroup rescan is running in a workqueue, and when the fs is
>>> frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
>>>
>>> But a sleeping workload still counts as active for the workqueue until
>>> the workload exits.
>>>
>>> So the qgroup rescan item will sleep on the frozen fs, and fail the pm
>>> suspension.
>>>
>>> [FIX]
>>> I strongly doubt whether we should even use a workqueue for the qgroup
>>> rescan workload, a kthread would be a more suitable choice and can handle
>>> fs and process freezing way better.
>>> But that will be a long term solution.
>>
>> Hi Qu,
>>
>> Thanks for working on this.
>>
>> I recently posted an RFC series addressing qgroup rescan lifecycle
>> issues around quota disable/enable, error cleanup, and remounts [1].
>>
>> From the recent discussion, I got the impression that full qgroups
>> might be phased out or substantially reworked. Is that a fair
>> understanding?
>
> Do you have link to the dicussion? Phasing out current qgroups in the
> full mode would be a functionality loss and we don't have a replacement.
> On the design level, the qgroups are general enough to cover the COW
> design, sharing. The compression was originally intended but IIRC we
> don't account compressed extents separately.
>
> Reworking could happen, the performance hit of qgroups in full mode has
> been a problem since beginning, but I don't see any easy change there.
> If the sematics change we'd need to add another mode, like we have the
> simple quota mode.
Hi David,
I was referring to this discussion [1].
Perhaps I misunderstood Qu's comments about qgroups no longer being recommended.
Thanks for clarifying.
[1] https://lore.kernel.org/linux-btrfs/mu8fq8a8.b9a9a1ae-dd6b-45a4-a415-5db853d61536@nod.at/T/
Thanks,
Dongjiang
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-21 13:12 ` David Sterba
2026-09-21 13:34 ` Dongjiang Zhu
@ 2026-09-21 21:46 ` Qu Wenruo
2026-09-22 6:30 ` Andrei Borzenkov
2026-09-22 6:24 ` Andrei Borzenkov
2 siblings, 1 reply; 18+ messages in thread
From: Qu Wenruo @ 2026-09-21 21:46 UTC (permalink / raw)
To: dsterba, Dongjiang Zhu; +Cc: Qu Wenruo, linux-btrfs, Richard Weinberger
在 2026/9/21 22:42, David Sterba 写道:
> On Mon, Sep 21, 2026 at 04:36:51PM +0800, Dongjiang Zhu wrote:
>> 在 2026/9/21 15:03, Qu Wenruo 写道:
>>> [BUG]
>>> There is a bug report that a running qgroup rescan can fail a pm
>>> suspension:
>>>
>>> [ T278489] Freezing remaining freezable tasks
>>> [ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
>>> [ T278489] Showing freezable workqueues that are still busy:
>>> [...]
>>> [ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
>>> [ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
>>>
>>> [CAUSE]
>>> Btrfs qgroup rescan is running in a workqueue, and when the fs is
>>> frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
>>>
>>> But a sleeping workload still counts as active for the workqueue until
>>> the workload exits.
>>>
>>> So the qgroup rescan item will sleep on the frozen fs, and fail the pm
>>> suspension.
>>>
>>> [FIX]
>>> I strongly doubt whether we should even use a workqueue for the qgroup
>>> rescan workload, a kthread would be a more suitable choice and can handle
>>> fs and process freezing way better.
>>> But that will be a long term solution.
>>
>> Hi Qu,
>>
>> Thanks for working on this.
>>
>> I recently posted an RFC series addressing qgroup rescan lifecycle
>> issues around quota disable/enable, error cleanup, and remounts [1].
>>
>> From the recent discussion, I got the impression that full qgroups
>> might be phased out or substantially reworked. Is that a fair
>> understanding?
>
> Do you have link to the dicussion? Phasing out current qgroups in the
> full mode would be a functionality loss and we don't have a replacement.
> On the design level, the qgroups are general enough to cover the COW
> design, sharing. The compression was originally intended but IIRC we
> don't account compressed extents separately.
The biggest problem is the insolvable nature of tracking the owner of
every extent vs changing all extent owners in one snapshot
creation/deletion.
This means qgroup is only reliable with rigid subvolume layout, which is
never the common use case.
And all the current workarounds are killing qgroup limit functionality,
requiring endless rescans again and again.
>
> Reworking could happen, the performance hit of qgroups in full mode has
> been a problem since beginning, but I don't see any easy change there.
> If the sematics change we'd need to add another mode, like we have the
> simple quota mode.
For now the only major user of qgroup original mode is snapper (for
subvolume cleanup policy), and we're already pushing snapper to not
enable qgroup by default (although no good progress yet).
I'm open to new designs like simple-quota, or maybe "reference" only
accounting (making it almost the same as the quota files supported by
all other fses).
But I do not think full quota mode should be a feature that should be
enabled by default.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-21 21:46 ` Qu Wenruo
@ 2026-09-22 6:30 ` Andrei Borzenkov
2026-09-22 6:57 ` Qu Wenruo
0 siblings, 1 reply; 18+ messages in thread
From: Andrei Borzenkov @ 2026-09-22 6:30 UTC (permalink / raw)
To: Qu Wenruo
Cc: dsterba, Dongjiang Zhu, Qu Wenruo, linux-btrfs,
Richard Weinberger
On Tue, Sep 22, 2026 at 12:46 AM Qu Wenruo <quwenruo.btrfs@gmx.com> wrote:
>
>
>
> 在 2026/9/21 22:42, David Sterba 写道:
> > On Mon, Sep 21, 2026 at 04:36:51PM +0800, Dongjiang Zhu wrote:
> >> 在 2026/9/21 15:03, Qu Wenruo 写道:
> >>> [BUG]
> >>> There is a bug report that a running qgroup rescan can fail a pm
> >>> suspension:
> >>>
> >>> [ T278489] Freezing remaining freezable tasks
> >>> [ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
> >>> [ T278489] Showing freezable workqueues that are still busy:
> >>> [...]
> >>> [ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
> >>> [ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
> >>>
> >>> [CAUSE]
> >>> Btrfs qgroup rescan is running in a workqueue, and when the fs is
> >>> frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
> >>>
> >>> But a sleeping workload still counts as active for the workqueue until
> >>> the workload exits.
> >>>
> >>> So the qgroup rescan item will sleep on the frozen fs, and fail the pm
> >>> suspension.
> >>>
> >>> [FIX]
> >>> I strongly doubt whether we should even use a workqueue for the qgroup
> >>> rescan workload, a kthread would be a more suitable choice and can handle
> >>> fs and process freezing way better.
> >>> But that will be a long term solution.
> >>
> >> Hi Qu,
> >>
> >> Thanks for working on this.
> >>
> >> I recently posted an RFC series addressing qgroup rescan lifecycle
> >> issues around quota disable/enable, error cleanup, and remounts [1].
> >>
> >> From the recent discussion, I got the impression that full qgroups
> >> might be phased out or substantially reworked. Is that a fair
> >> understanding?
> >
> > Do you have link to the dicussion? Phasing out current qgroups in the
> > full mode would be a functionality loss and we don't have a replacement.
> > On the design level, the qgroups are general enough to cover the COW
> > design, sharing. The compression was originally intended but IIRC we
> > don't account compressed extents separately.
>
> The biggest problem is the insolvable nature of tracking the owner of
> every extent vs changing all extent owners in one snapshot
> creation/deletion.
>
> This means qgroup is only reliable with rigid subvolume layout, which is
> never the common use case.
>
> And all the current workarounds are killing qgroup limit functionality,
> requiring endless rescans again and again.
>
> >
> > Reworking could happen, the performance hit of qgroups in full mode has
> > been a problem since beginning, but I don't see any easy change there.
> > If the sematics change we'd need to add another mode, like we have the
> > simple quota mode.
>
> For now the only major user of qgroup original mode is snapper (for
> subvolume cleanup policy), and we're already pushing snapper to not
> enable qgroup by default (although no good progress yet).
>
The major use case of qgroup-like functionality is snapshot
management. Whether it is implemented by snapper or by something else.
Without the ability to get an accurate estimation of the real
subvolume space consumption (how much space will become free after
deleting this subvolume) any snapshot based workflow becomes a
nightmare.
> I'm open to new designs like simple-quota, or maybe "reference" only
> accounting (making it almost the same as the quota files supported by
> all other fses).
>
I do not see how "reference only" is useful in any way. We already
have enough tools showing us "used" space exceeding the physical
device size by an order of magnitude.
> But I do not think full quota mode should be a feature that should be
> enabled by default.
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-22 6:30 ` Andrei Borzenkov
@ 2026-09-22 6:57 ` Qu Wenruo
0 siblings, 0 replies; 18+ messages in thread
From: Qu Wenruo @ 2026-09-22 6:57 UTC (permalink / raw)
To: Andrei Borzenkov, Qu Wenruo
Cc: dsterba, Dongjiang Zhu, linux-btrfs, Richard Weinberger
在 2026/9/22 16:00, Andrei Borzenkov 写道:
> On Tue, Sep 22, 2026 at 12:46 AM Qu Wenruo <quwenruo.btrfs@gmx.com> wrote:
>>
>>
>>
>> 在 2026/9/21 22:42, David Sterba 写道:
>>> On Mon, Sep 21, 2026 at 04:36:51PM +0800, Dongjiang Zhu wrote:
>>>> 在 2026/9/21 15:03, Qu Wenruo 写道:
>>>>> [BUG]
>>>>> There is a bug report that a running qgroup rescan can fail a pm
>>>>> suspension:
>>>>>
>>>>> [ T278489] Freezing remaining freezable tasks
>>>>> [ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
>>>>> [ T278489] Showing freezable workqueues that are still busy:
>>>>> [...]
>>>>> [ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
>>>>> [ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
>>>>>
>>>>> [CAUSE]
>>>>> Btrfs qgroup rescan is running in a workqueue, and when the fs is
>>>>> frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
>>>>>
>>>>> But a sleeping workload still counts as active for the workqueue until
>>>>> the workload exits.
>>>>>
>>>>> So the qgroup rescan item will sleep on the frozen fs, and fail the pm
>>>>> suspension.
>>>>>
>>>>> [FIX]
>>>>> I strongly doubt whether we should even use a workqueue for the qgroup
>>>>> rescan workload, a kthread would be a more suitable choice and can handle
>>>>> fs and process freezing way better.
>>>>> But that will be a long term solution.
>>>>
>>>> Hi Qu,
>>>>
>>>> Thanks for working on this.
>>>>
>>>> I recently posted an RFC series addressing qgroup rescan lifecycle
>>>> issues around quota disable/enable, error cleanup, and remounts [1].
>>>>
>>>> From the recent discussion, I got the impression that full qgroups
>>>> might be phased out or substantially reworked. Is that a fair
>>>> understanding?
>>>
>>> Do you have link to the dicussion? Phasing out current qgroups in the
>>> full mode would be a functionality loss and we don't have a replacement.
>>> On the design level, the qgroups are general enough to cover the COW
>>> design, sharing. The compression was originally intended but IIRC we
>>> don't account compressed extents separately.
>>
>> The biggest problem is the insolvable nature of tracking the owner of
>> every extent vs changing all extent owners in one snapshot
>> creation/deletion.
>>
>> This means qgroup is only reliable with rigid subvolume layout, which is
>> never the common use case.
>>
>> And all the current workarounds are killing qgroup limit functionality,
>> requiring endless rescans again and again.
>>
>>>
>>> Reworking could happen, the performance hit of qgroups in full mode has
>>> been a problem since beginning, but I don't see any easy change there.
>>> If the sematics change we'd need to add another mode, like we have the
>>> simple quota mode.
>>
>> For now the only major user of qgroup original mode is snapper (for
>> subvolume cleanup policy), and we're already pushing snapper to not
>> enable qgroup by default (although no good progress yet).
>>
>
> The major use case of qgroup-like functionality is snapshot
> management. Whether it is implemented by snapper or by something else.
> Without the ability to get an accurate estimation of the real
> subvolume space consumption (how much space will become free after
> deleting this subvolume) any snapshot based workflow becomes a
> nightmare.
Then let me give you a very simple example.
There are a dozen of directories in a non-btrfs fs, then one wants to
delete some of those directories to free up space.
What would a regular user do to determine how many bytes can be freed by
deleting a directory? They run "du -sh".
I see no difference between running "btrfs fi du" on btrfs, and "du -sh"
on a non-btrfs for this particular case.
Furthermore, even snapper didn't need to use qgroup for 90% or even 99%
of users.
Just disable qgroup on an openSUSE/SLE systems (and that's already most
people do), snapper will still do regular snapshot rotation all fine.
You're asking 99% of end users to take the burden that only 1% users
really need.
That's not how things should work.
>
>> I'm open to new designs like simple-quota, or maybe "reference" only
>> accounting (making it almost the same as the quota files supported by
>> all other fses).
>>
>
> I do not see how "reference only" is useful in any way. We already
> have enough tools showing us "used" space exceeding the physical
> device size by an order of magnitude.
>
>> But I do not think full quota mode should be a feature that should be
>> enabled by default.
>>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-21 13:12 ` David Sterba
2026-09-21 13:34 ` Dongjiang Zhu
2026-09-21 21:46 ` Qu Wenruo
@ 2026-09-22 6:24 ` Andrei Borzenkov
2 siblings, 0 replies; 18+ messages in thread
From: Andrei Borzenkov @ 2026-09-22 6:24 UTC (permalink / raw)
To: dsterba; +Cc: Dongjiang Zhu, Qu Wenruo, linux-btrfs, Richard Weinberger
On Mon, Sep 21, 2026 at 4:33 PM David Sterba <dsterba@suse.cz> wrote:
>
> On Mon, Sep 21, 2026 at 04:36:51PM +0800, Dongjiang Zhu wrote:
> > 在 2026/9/21 15:03, Qu Wenruo 写道:
> > > [BUG]
> > > There is a bug report that a running qgroup rescan can fail a pm
> > > suspension:
> > >
> > > [ T278489] Freezing remaining freezable tasks
> > > [ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
> > > [ T278489] Showing freezable workqueues that are still busy:
> > > [...]
> > > [ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
> > > [ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
> > >
> > > [CAUSE]
> > > Btrfs qgroup rescan is running in a workqueue, and when the fs is
> > > frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
> > >
> > > But a sleeping workload still counts as active for the workqueue until
> > > the workload exits.
> > >
> > > So the qgroup rescan item will sleep on the frozen fs, and fail the pm
> > > suspension.
> > >
> > > [FIX]
> > > I strongly doubt whether we should even use a workqueue for the qgroup
> > > rescan workload, a kthread would be a more suitable choice and can handle
> > > fs and process freezing way better.
> > > But that will be a long term solution.
> >
> > Hi Qu,
> >
> > Thanks for working on this.
> >
> > I recently posted an RFC series addressing qgroup rescan lifecycle
> > issues around quota disable/enable, error cleanup, and remounts [1].
> >
> > From the recent discussion, I got the impression that full qgroups
> > might be phased out or substantially reworked. Is that a fair
> > understanding?
>
> Do you have link to the dicussion?
https://lore.kernel.org/linux-btrfs/03dcccbc-35c7-4b9d-9275-17a4b4111b25@suse.com/
> Phasing out current qgroups in the
> full mode would be a functionality loss and we don't have a replacement.
> On the design level, the qgroups are general enough to cover the COW
> design, sharing. The compression was originally intended but IIRC we
> don't account compressed extents separately.
>
> Reworking could happen, the performance hit of qgroups in full mode has
> been a problem since beginning, but I don't see any easy change there.
> If the sematics change we'd need to add another mode, like we have the
> simple quota mode.
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-21 7:03 ` [PATCH] " Qu Wenruo
2026-09-21 8:36 ` Dongjiang Zhu
@ 2026-09-21 9:16 ` Qu Wenruo
2026-09-21 13:18 ` David Sterba
2 siblings, 0 replies; 18+ messages in thread
From: Qu Wenruo @ 2026-09-21 9:16 UTC (permalink / raw)
To: linux-btrfs; +Cc: Richard Weinberger
[BUG]
There is a bug report that a running qgroup rescan can fail a pm
suspension:
[ T278489] Freezing remaining freezable tasks
[ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
[ T278489] Showing freezable workqueues that are still busy:
[...]
[ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
[ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
[CAUSE]
Btrfs qgroup rescan is running in a workqueue, and when the fs is
frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
But a sleeping workload still counts as active for the workqueue until
the workload exits.
So the qgroup rescan item will sleep on the frozen fs, and fail the pm
suspension.
[FIX]
I strongly doubt whether we should even use a workqueue for the qgroup
rescan workload, a kthread would be a more suitable choice and can handle
fs and process freezing way better.
But that will be a long term solution.
For now fix the problems by:
- make rescan_should_stop() to return true if the fs is frozen
This should handle most cases.
- Use btrfs_try_start_transaction() to avoid sleeping on
sb_start_intwrite()
This is less common but still possible, and if we sleep on
sb_start_intwrite(), the workqueue will never have a chance to
deactivate.
The above fixes should interrupt the rescan, so that pm suspension won't
fail.
And since the INCONSISTENT flag is not cleared, the end user/tool should
re-start a rescan if they need uptodate qgroup numbers.
And since we're here, also remove the "if (!trans) return;" check, and
end the transaction if there is one, so that the error message is always
shown.
Reported-by: Richard Weinberger <richard@nod.at>
Link: https://lore.kernel.org/linux-btrfs/mu8fq8a8.b9a9a1ae-dd6b-45a4-a415-5db853d61536@nod.at/
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 14 ++++++++------
fs/btrfs/transaction.c | 22 +++++++++++++++++++---
fs/btrfs/transaction.h | 4 ++++
3 files changed, 31 insertions(+), 9 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 05e35eb126dc..0390a17561e2 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3866,6 +3866,8 @@ static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
{
if (btrfs_fs_closing(fs_info))
return true;
+ if (fs_info->sb->s_writers.frozen > SB_UNFROZEN)
+ return true;
if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state))
return true;
if (!btrfs_qgroup_enabled(fs_info))
@@ -3901,9 +3903,11 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
path->skip_locking = true;
while (!ret && !(stopped = rescan_should_stop(fs_info))) {
- trans = btrfs_start_transaction(fs_info->fs_root, 0);
+ trans = btrfs_try_start_transaction(fs_info->fs_root, 0);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
+ if (ret == -EINTR)
+ stopped = true;
break;
}
@@ -3934,7 +3938,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
* btrfs_quota_disable().
*/
if (did_leaf_rescans) {
- trans = btrfs_start_transaction(fs_info->quota_root, 1);
+ trans = btrfs_try_start_transaction(fs_info->quota_root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
trans = NULL;
@@ -3963,10 +3967,8 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
complete_all(&fs_info->qgroup_rescan_completion);
mutex_unlock(&fs_info->qgroup_rescan_lock);
- if (!trans)
- return;
-
- btrfs_end_transaction(trans);
+ if (trans)
+ btrfs_end_transaction(trans);
if (stopped) {
btrfs_info(fs_info, "qgroup scan paused");
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index ca114235bbe1..76787417331e 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -559,7 +559,7 @@ static bool may_wait_transaction(struct btrfs_fs_info *fs_info, int type)
if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags))
return false;
- if (type == TRANS_START)
+ if ((type & TRANS_START) == TRANS_START)
return true;
return false;
@@ -719,8 +719,17 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,
* If we are ATTACH, it means we just want to catch the current
* transaction and commit it, so we needn't do sb_start_intwrite().
*/
- if (type & __TRANS_FREEZABLE)
- sb_start_intwrite(fs_info->sb);
+ if (type & __TRANS_FREEZABLE) {
+ if (type & __TRANS_TRYLOCK) {
+ if (!sb_start_intwrite_trylock(fs_info->sb)) {
+ ret = -EINTR;
+ kmem_cache_free(btrfs_trans_handle_cachep, h);
+ goto alloc_fail;
+ }
+ } else {
+ sb_start_intwrite(fs_info->sb);
+ }
+ }
if (may_wait_transaction(fs_info, type))
wait_current_trans(fs_info, type);
@@ -841,6 +850,13 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
BTRFS_RESERVE_FLUSH_ALL, true);
}
+struct btrfs_trans_handle *btrfs_try_start_transaction(struct btrfs_root *root,
+ unsigned int num_items)
+{
+ return start_transaction(root, num_items, TRANS_TRY_START,
+ BTRFS_RESERVE_FLUSH_ALL, true);
+}
+
struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
struct btrfs_root *root,
unsigned int num_items)
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 89153cd22596..a9da059d3806 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -127,9 +127,11 @@ enum {
ENUM_BIT(__TRANS_JOIN_NOLOCK),
ENUM_BIT(__TRANS_DUMMY),
ENUM_BIT(__TRANS_JOIN_NOSTART),
+ ENUM_BIT(__TRANS_TRYLOCK),
};
#define TRANS_START (__TRANS_START | __TRANS_FREEZABLE)
+#define TRANS_TRY_START (__TRANS_START | __TRANS_FREEZABLE | __TRANS_TRYLOCK)
#define TRANS_ATTACH (__TRANS_ATTACH)
#define TRANS_JOIN (__TRANS_JOIN | __TRANS_FREEZABLE)
#define TRANS_JOIN_NOLOCK (__TRANS_JOIN_NOLOCK)
@@ -308,6 +310,8 @@ do { \
int btrfs_end_transaction(struct btrfs_trans_handle *trans);
struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
unsigned int num_items);
+struct btrfs_trans_handle *btrfs_try_start_transaction(struct btrfs_root *root,
+ unsigned int num_items);
struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
struct btrfs_root *root,
unsigned int num_items);
--
2.55.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-21 7:03 ` [PATCH] " Qu Wenruo
2026-09-21 8:36 ` Dongjiang Zhu
2026-09-21 9:16 ` Qu Wenruo
@ 2026-09-21 13:18 ` David Sterba
2026-09-21 21:48 ` Qu Wenruo
2 siblings, 1 reply; 18+ messages in thread
From: David Sterba @ 2026-09-21 13:18 UTC (permalink / raw)
To: Qu Wenruo; +Cc: linux-btrfs, Richard Weinberger
On Mon, Sep 21, 2026 at 06:46:45PM +0930, Qu Wenruo wrote:
> --- a/fs/btrfs/transaction.c
> +++ b/fs/btrfs/transaction.c
> @@ -559,7 +559,7 @@ static bool may_wait_transaction(struct btrfs_fs_info *fs_info, int type)
> if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags))
> return false;
>
> - if (type == TRANS_START)
> + if ((type & TRANS_START) == TRANS_START)
This does not seem right, the type should be a single number, not a
bitmask
> #define TRANS_START (__TRANS_START | __TRANS_FREEZABLE)
> +#define TRANS_TRY_START (__TRANS_START | __TRANS_FREEZABLE | __TRANS_TRYLOCK)
If you meant to distinguish normal start and 'try' start then it's check
for two values.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH] btrfs: make qgroup rescan work to handle fs freezing
2026-09-21 13:18 ` David Sterba
@ 2026-09-21 21:48 ` Qu Wenruo
0 siblings, 0 replies; 18+ messages in thread
From: Qu Wenruo @ 2026-09-21 21:48 UTC (permalink / raw)
To: dsterba, Qu Wenruo; +Cc: linux-btrfs, Richard Weinberger
在 2026/9/21 22:48, David Sterba 写道:
> On Mon, Sep 21, 2026 at 06:46:45PM +0930, Qu Wenruo wrote:
>> --- a/fs/btrfs/transaction.c
>> +++ b/fs/btrfs/transaction.c
>> @@ -559,7 +559,7 @@ static bool may_wait_transaction(struct btrfs_fs_info *fs_info, int type)
>> if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags))
>> return false;
>>
>> - if (type == TRANS_START)
>> + if ((type & TRANS_START) == TRANS_START)
>
> This does not seem right, the type should be a single number, not a
> bitmask
But TRANS_START is already defined as a bitmask.
>
>> #define TRANS_START (__TRANS_START | __TRANS_FREEZABLE)
>> +#define TRANS_TRY_START (__TRANS_START | __TRANS_FREEZABLE | __TRANS_TRYLOCK)
>
> If you meant to distinguish normal start and 'try' start then it's check
> for two values.
So what your recommended change?
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 1/3] btrfs: let rescan_should_stop() decide if the rescan flag is cleared
2026-09-21 9:16 [PATCH v2 0/3] btrfs: make qgroup rescan work to handle fs freezing Qu Wenruo
2026-09-21 7:03 ` [PATCH] " Qu Wenruo
@ 2026-09-21 9:16 ` Qu Wenruo
2026-09-21 10:14 ` Dongjiang Zhu
2026-09-21 9:16 ` [PATCH v2 2/3] btrfs: always show the message when qgroup rescan ended Qu Wenruo
2026-09-21 9:16 ` [PATCH v2 3/3] btrfs: make qgroup rescan work handle fs freezing Qu Wenruo
3 siblings, 1 reply; 18+ messages in thread
From: Qu Wenruo @ 2026-09-21 9:16 UTC (permalink / raw)
To: linux-btrfs
Currently if rescan_should_stop() returned true, aka the rescan should
be stopped, then we won't clear the BTRFS_QGROUP_STATUS_BIT_RESCAN
unless the BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN is set.
However the reason why we should stop rescan is also affecting whether
we should clear the rescan flag:
- fs closing
Should not clear RESCAN flag, as the next mount will resume the rescan
- fs remounting (from RW to RO)
The next RW mount will resume qgroup rescan, so we should not clear
the RESCAN flag.
- quota already disabled
We should clear the RESCAN flag.
- CANCEL_RESCAN bit set
We should clear the RESCAN flag, for now it's a special case and
checked manually later.
So it's better to let rescan_should_stop() to determine whether the
RESCAN flag should also be cleared.
Since we're here, also slightly change the message, the CANCEL_RESCAN
bit is already cleared before outputting the message, so that
corresponding branch is dead.
Save whether we still have the RESCAN bit set when still holding the
qgroup_rescan_lock instead, and use that saved result to determine if
the rescan is properly paused or canceled.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 41 ++++++++++++++++++++++++++++++-----------
1 file changed, 30 insertions(+), 11 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 05e35eb126dc..eb71f5d7c1a5 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3862,16 +3862,31 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
return ret;
}
-static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
+/*
+ * Return true if the rescan should be stopped.
+ * If returning true, update @clear_rescan_ret to indicate whether the rescan
+ * flag should be cleared.
+ *
+ * Return false if the rescan should continue.
+ */
+static bool rescan_should_stop(struct btrfs_fs_info *fs_info, bool *clear_rescan_ret)
{
- if (btrfs_fs_closing(fs_info))
+ if (btrfs_fs_closing(fs_info)) {
+ *clear_rescan_ret = false;
return true;
- if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state))
+ }
+ if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state)) {
+ *clear_rescan_ret = false;
return true;
- if (!btrfs_qgroup_enabled(fs_info))
+ }
+ if (!btrfs_qgroup_enabled(fs_info)) {
+ *clear_rescan_ret = true;
return true;
- if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags))
+ }
+ if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags)) {
+ *clear_rescan_ret = true;
return true;
+ }
return false;
}
@@ -3883,7 +3898,9 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
struct btrfs_trans_handle *trans = NULL;
int ret = 0;
bool stopped = false;
+ bool clear_rescan = false;
bool did_leaf_rescans = false;
+ bool canceled;
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
return;
@@ -3900,7 +3917,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
path->search_commit_root = true;
path->skip_locking = true;
- while (!ret && !(stopped = rescan_should_stop(fs_info))) {
+ while (!ret && !(stopped = rescan_should_stop(fs_info, &clear_rescan))) {
trans = btrfs_start_transaction(fs_info->fs_root, 0);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
@@ -3947,8 +3964,8 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
}
mutex_lock(&fs_info->qgroup_rescan_lock);
- if (!stopped || test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN,
- &fs_info->qgroup_flags))
+ if (!stopped || clear_rescan ||
+ test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags))
clear_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
if (trans) {
int ret2 = update_qgroup_status_item(trans);
@@ -3960,6 +3977,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
}
fs_info->qgroup_rescan_running = false;
clear_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags);
+ canceled = !test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
complete_all(&fs_info->qgroup_rescan_completion);
mutex_unlock(&fs_info->qgroup_rescan_lock);
@@ -3969,9 +3987,10 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
btrfs_end_transaction(trans);
if (stopped) {
- btrfs_info(fs_info, "qgroup scan paused");
- } else if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags)) {
- btrfs_info(fs_info, "qgroup scan cancelled");
+ if (canceled)
+ btrfs_info(fs_info, "qgroup scan cancelled");
+ else
+ btrfs_info(fs_info, "qgroup scan paused");
} else if (ret >= 0) {
btrfs_info(fs_info, "qgroup scan completed%s",
ret > 0 ? " (inconsistency flag cleared)" : "");
--
2.55.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH v2 1/3] btrfs: let rescan_should_stop() decide if the rescan flag is cleared
2026-09-21 9:16 ` [PATCH v2 1/3] btrfs: let rescan_should_stop() decide if the rescan flag is cleared Qu Wenruo
@ 2026-09-21 10:14 ` Dongjiang Zhu
2026-09-21 10:26 ` Qu Wenruo
0 siblings, 1 reply; 18+ messages in thread
From: Dongjiang Zhu @ 2026-09-21 10:14 UTC (permalink / raw)
To: Qu Wenruo, linux-btrfs
在 2026/9/21 17:16, Qu Wenruo 写道:
> Currently if rescan_should_stop() returned true, aka the rescan should
> be stopped, then we won't clear the BTRFS_QGROUP_STATUS_BIT_RESCAN
> unless the BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN is set.
>
> However the reason why we should stop rescan is also affecting whether
> we should clear the rescan flag:
>
> - fs closing
> Should not clear RESCAN flag, as the next mount will resume the rescan
>
> - fs remounting (from RW to RO)
> The next RW mount will resume qgroup rescan, so we should not clear
> the RESCAN flag.
>
> - quota already disabled
> We should clear the RESCAN flag.
>
> - CANCEL_RESCAN bit set
> We should clear the RESCAN flag, for now it's a special case and
> checked manually later.
>
> So it's better to let rescan_should_stop() to determine whether the
> RESCAN flag should also be cleared.
>
> Since we're here, also slightly change the message, the CANCEL_RESCAN
> bit is already cleared before outputting the message, so that
> corresponding branch is dead.
>
> Save whether we still have the RESCAN bit set when still holding the
> qgroup_rescan_lock instead, and use that saved result to determine if
> the rescan is properly paused or canceled.
>
> Signed-off-by: Qu Wenruo <wqu@suse.com>
> ---
> fs/btrfs/qgroup.c | 41 ++++++++++++++++++++++++++++++-----------
> 1 file changed, 30 insertions(+), 11 deletions(-)
>
> diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
> index 05e35eb126dc..eb71f5d7c1a5 100644
> --- a/fs/btrfs/qgroup.c
> +++ b/fs/btrfs/qgroup.c
> @@ -3862,16 +3862,31 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
> return ret;
> }
>
> -static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
> +/*
> + * Return true if the rescan should be stopped.
> + * If returning true, update @clear_rescan_ret to indicate whether the rescan
> + * flag should be cleared.
> + *
> + * Return false if the rescan should continue.
> + */
> +static bool rescan_should_stop(struct btrfs_fs_info *fs_info, bool *clear_rescan_ret)
> {
> - if (btrfs_fs_closing(fs_info))
> + if (btrfs_fs_closing(fs_info)) {
> + *clear_rescan_ret = false;
> return true;
> - if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state))
> + }
> + if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state)) {
> + *clear_rescan_ret = false;
> return true;
What about an RW-to-RW remount, such as: mount -o remount,compress=zstd /mnt
If the rescan worker observes REMOUNTING, wouldn't it stop with RESCAN still set, but without a corresponding resume call after the remount?
Thanks,
Dongjiang
> - if (!btrfs_qgroup_enabled(fs_info))
> + }
> + if (!btrfs_qgroup_enabled(fs_info)) {
> + *clear_rescan_ret = true;
> return true;
> - if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags))
> + }
> + if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags)) {
> + *clear_rescan_ret = true;
> return true;
> + }
> return false;
> }
>
> @@ -3883,7 +3898,9 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
> struct btrfs_trans_handle *trans = NULL;
> int ret = 0;
> bool stopped = false;
> + bool clear_rescan = false;
> bool did_leaf_rescans = false;
> + bool canceled;
>
> if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
> return;
> @@ -3900,7 +3917,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
> path->search_commit_root = true;
> path->skip_locking = true;
>
> - while (!ret && !(stopped = rescan_should_stop(fs_info))) {
> + while (!ret && !(stopped = rescan_should_stop(fs_info, &clear_rescan))) {
> trans = btrfs_start_transaction(fs_info->fs_root, 0);
> if (IS_ERR(trans)) {
> ret = PTR_ERR(trans);
> @@ -3947,8 +3964,8 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
> }
>
> mutex_lock(&fs_info->qgroup_rescan_lock);
> - if (!stopped || test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN,
> - &fs_info->qgroup_flags))
> + if (!stopped || clear_rescan ||
> + test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags))
> clear_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
> if (trans) {
> int ret2 = update_qgroup_status_item(trans);
> @@ -3960,6 +3977,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
> }
> fs_info->qgroup_rescan_running = false;
> clear_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags);
> + canceled = !test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
> complete_all(&fs_info->qgroup_rescan_completion);
> mutex_unlock(&fs_info->qgroup_rescan_lock);
>
> @@ -3969,9 +3987,10 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
> btrfs_end_transaction(trans);
>
> if (stopped) {
> - btrfs_info(fs_info, "qgroup scan paused");
> - } else if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags)) {
> - btrfs_info(fs_info, "qgroup scan cancelled");
> + if (canceled)
> + btrfs_info(fs_info, "qgroup scan cancelled");
> + else
> + btrfs_info(fs_info, "qgroup scan paused");
> } else if (ret >= 0) {
> btrfs_info(fs_info, "qgroup scan completed%s",
> ret > 0 ? " (inconsistency flag cleared)" : "");
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 1/3] btrfs: let rescan_should_stop() decide if the rescan flag is cleared
2026-09-21 10:14 ` Dongjiang Zhu
@ 2026-09-21 10:26 ` Qu Wenruo
2026-09-21 10:39 ` Dongjiang Zhu
0 siblings, 1 reply; 18+ messages in thread
From: Qu Wenruo @ 2026-09-21 10:26 UTC (permalink / raw)
To: Dongjiang Zhu, linux-btrfs
在 2026/9/21 19:44, Dongjiang Zhu 写道:
> 在 2026/9/21 17:16, Qu Wenruo 写道:
>> Currently if rescan_should_stop() returned true, aka the rescan should
>> be stopped, then we won't clear the BTRFS_QGROUP_STATUS_BIT_RESCAN
>> unless the BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN is set.
>>
>> However the reason why we should stop rescan is also affecting whether
>> we should clear the rescan flag:
>>
>> - fs closing
>> Should not clear RESCAN flag, as the next mount will resume the rescan
>>
>> - fs remounting (from RW to RO)
>> The next RW mount will resume qgroup rescan, so we should not clear
>> the RESCAN flag.
>>
>> - quota already disabled
>> We should clear the RESCAN flag.
>>
>> - CANCEL_RESCAN bit set
>> We should clear the RESCAN flag, for now it's a special case and
>> checked manually later.
>>
>> So it's better to let rescan_should_stop() to determine whether the
>> RESCAN flag should also be cleared.
>>
>> Since we're here, also slightly change the message, the CANCEL_RESCAN
>> bit is already cleared before outputting the message, so that
>> corresponding branch is dead.
>>
>> Save whether we still have the RESCAN bit set when still holding the
>> qgroup_rescan_lock instead, and use that saved result to determine if
>> the rescan is properly paused or canceled.
>>
>> Signed-off-by: Qu Wenruo <wqu@suse.com>
>> ---
>> fs/btrfs/qgroup.c | 41 ++++++++++++++++++++++++++++++-----------
>> 1 file changed, 30 insertions(+), 11 deletions(-)
>>
>> diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
>> index 05e35eb126dc..eb71f5d7c1a5 100644
>> --- a/fs/btrfs/qgroup.c
>> +++ b/fs/btrfs/qgroup.c
>> @@ -3862,16 +3862,31 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
>> return ret;
>> }
>>
>> -static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
>> +/*
>> + * Return true if the rescan should be stopped.
>> + * If returning true, update @clear_rescan_ret to indicate whether the rescan
>> + * flag should be cleared.
>> + *
>> + * Return false if the rescan should continue.
>> + */
>> +static bool rescan_should_stop(struct btrfs_fs_info *fs_info, bool *clear_rescan_ret)
>> {
>> - if (btrfs_fs_closing(fs_info))
>> + if (btrfs_fs_closing(fs_info)) {
>> + *clear_rescan_ret = false;
>> return true;
>> - if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state))
>> + }
>> + if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state)) {
>> + *clear_rescan_ret = false;
>> return true;
>
> What about an RW-to-RW remount, such as: mount -o remount,compress=zstd /mnt
>
> If the rescan worker observes REMOUNTING, wouldn't it stop with RESCAN still set, but without a corresponding resume call after the remount?
That's an existing problem, feel free to fix it.
Here I only care about preparing it for the incoming freezing check.
>
> Thanks,
> Dongjiang
>
>> - if (!btrfs_qgroup_enabled(fs_info))
>> + }
>> + if (!btrfs_qgroup_enabled(fs_info)) {
>> + *clear_rescan_ret = true;
>> return true;
>> - if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags))
>> + }
>> + if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags)) {
>> + *clear_rescan_ret = true;
>> return true;
>> + }
>> return false;
>> }
>>
>> @@ -3883,7 +3898,9 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
>> struct btrfs_trans_handle *trans = NULL;
>> int ret = 0;
>> bool stopped = false;
>> + bool clear_rescan = false;
>> bool did_leaf_rescans = false;
>> + bool canceled;
>>
>> if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
>> return;
>> @@ -3900,7 +3917,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
>> path->search_commit_root = true;
>> path->skip_locking = true;
>>
>> - while (!ret && !(stopped = rescan_should_stop(fs_info))) {
>> + while (!ret && !(stopped = rescan_should_stop(fs_info, &clear_rescan))) {
>> trans = btrfs_start_transaction(fs_info->fs_root, 0);
>> if (IS_ERR(trans)) {
>> ret = PTR_ERR(trans);
>> @@ -3947,8 +3964,8 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
>> }
>>
>> mutex_lock(&fs_info->qgroup_rescan_lock);
>> - if (!stopped || test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN,
>> - &fs_info->qgroup_flags))
>> + if (!stopped || clear_rescan ||
>> + test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags))
>> clear_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
>> if (trans) {
>> int ret2 = update_qgroup_status_item(trans);
>> @@ -3960,6 +3977,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
>> }
>> fs_info->qgroup_rescan_running = false;
>> clear_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags);
>> + canceled = !test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info->qgroup_flags);
>> complete_all(&fs_info->qgroup_rescan_completion);
>> mutex_unlock(&fs_info->qgroup_rescan_lock);
>>
>> @@ -3969,9 +3987,10 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
>> btrfs_end_transaction(trans);
>>
>> if (stopped) {
>> - btrfs_info(fs_info, "qgroup scan paused");
>> - } else if (test_bit(BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN, &fs_info->qgroup_flags)) {
>> - btrfs_info(fs_info, "qgroup scan cancelled");
>> + if (canceled)
>> + btrfs_info(fs_info, "qgroup scan cancelled");
>> + else
>> + btrfs_info(fs_info, "qgroup scan paused");
>> } else if (ret >= 0) {
>> btrfs_info(fs_info, "qgroup scan completed%s",
>> ret > 0 ? " (inconsistency flag cleared)" : "");
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH v2 1/3] btrfs: let rescan_should_stop() decide if the rescan flag is cleared
2026-09-21 10:26 ` Qu Wenruo
@ 2026-09-21 10:39 ` Dongjiang Zhu
0 siblings, 0 replies; 18+ messages in thread
From: Dongjiang Zhu @ 2026-09-21 10:39 UTC (permalink / raw)
To: Qu Wenruo, linux-btrfs
在 2026/9/21 18:26, Qu Wenruo 写道:
>
>
> 在 2026/9/21 19:44, Dongjiang Zhu 写道:
>> 在 2026/9/21 17:16, Qu Wenruo 写道:
>>> Currently if rescan_should_stop() returned true, aka the rescan should
>>> be stopped, then we won't clear the BTRFS_QGROUP_STATUS_BIT_RESCAN
>>> unless the BTRFS_QGROUP_RUNTIME_BIT_CANCEL_RESCAN is set.
>>>
>>> However the reason why we should stop rescan is also affecting whether
>>> we should clear the rescan flag:
>>>
>>> - fs closing
>>> Should not clear RESCAN flag, as the next mount will resume the rescan
>>>
>>> - fs remounting (from RW to RO)
>>> The next RW mount will resume qgroup rescan, so we should not clear
>>> the RESCAN flag.
>>>
>>> - quota already disabled
>>> We should clear the RESCAN flag.
>>>
>>> - CANCEL_RESCAN bit set
>>> We should clear the RESCAN flag, for now it's a special case and
>>> checked manually later.
>>>
>>> So it's better to let rescan_should_stop() to determine whether the
>>> RESCAN flag should also be cleared.
>>>
>>> Since we're here, also slightly change the message, the CANCEL_RESCAN
>>> bit is already cleared before outputting the message, so that
>>> corresponding branch is dead.
>>>
>>> Save whether we still have the RESCAN bit set when still holding the
>>> qgroup_rescan_lock instead, and use that saved result to determine if
>>> the rescan is properly paused or canceled.
>>>
>>> Signed-off-by: Qu Wenruo <wqu@suse.com>
>>> ---
>>> fs/btrfs/qgroup.c | 41 ++++++++++++++++++++++++++++++-----------
>>> 1 file changed, 30 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
>>> index 05e35eb126dc..eb71f5d7c1a5 100644
>>> --- a/fs/btrfs/qgroup.c
>>> +++ b/fs/btrfs/qgroup.c
>>> @@ -3862,16 +3862,31 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,
>>> return ret;
>>> }
>>> -static bool rescan_should_stop(struct btrfs_fs_info *fs_info)
>>> +/*
>>> + * Return true if the rescan should be stopped.
>>> + * If returning true, update @clear_rescan_ret to indicate whether the rescan
>>> + * flag should be cleared.
>>> + *
>>> + * Return false if the rescan should continue.
>>> + */
>>> +static bool rescan_should_stop(struct btrfs_fs_info *fs_info, bool *clear_rescan_ret)
>>> {
>>> - if (btrfs_fs_closing(fs_info))
>>> + if (btrfs_fs_closing(fs_info)) {
>>> + *clear_rescan_ret = false;
>>> return true;
>>> - if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state))
>>> + }
>>> + if (test_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state)) {
>>> + *clear_rescan_ret = false;
>>> return true;
>>
>> What about an RW-to-RW remount, such as: mount -o remount,compress=zstd /mnt
>>
>> If the rescan worker observes REMOUNTING, wouldn't it stop with RESCAN still set, but without a corresponding resume call after the remount?
>
> That's an existing problem, feel free to fix it.
>
> Here I only care about preparing it for the incoming freezing check.
Understood, thanks. Once your series is merged, I'll update and rebase my RFC series, then resend the remaining fixes.
Thanks,
Dongjiang
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v2 2/3] btrfs: always show the message when qgroup rescan ended
2026-09-21 9:16 [PATCH v2 0/3] btrfs: make qgroup rescan work to handle fs freezing Qu Wenruo
2026-09-21 7:03 ` [PATCH] " Qu Wenruo
2026-09-21 9:16 ` [PATCH v2 1/3] btrfs: let rescan_should_stop() decide if the rescan flag is cleared Qu Wenruo
@ 2026-09-21 9:16 ` Qu Wenruo
2026-09-21 9:16 ` [PATCH v2 3/3] btrfs: make qgroup rescan work handle fs freezing Qu Wenruo
3 siblings, 0 replies; 18+ messages in thread
From: Qu Wenruo @ 2026-09-21 9:16 UTC (permalink / raw)
To: linux-btrfs
Currently if we failed to start a transaction to update the qgroup
status item, we show no message on the reason why the rescan finished.
This is not helpful to end users.
Fix it by only ending the transaction if there is one, instead of exiting.
So the message is always shown.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index eb71f5d7c1a5..4adcc6032dce 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3981,10 +3981,8 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
complete_all(&fs_info->qgroup_rescan_completion);
mutex_unlock(&fs_info->qgroup_rescan_lock);
- if (!trans)
- return;
-
- btrfs_end_transaction(trans);
+ if (trans)
+ btrfs_end_transaction(trans);
if (stopped) {
if (canceled)
--
2.55.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* [PATCH v2 3/3] btrfs: make qgroup rescan work handle fs freezing
2026-09-21 9:16 [PATCH v2 0/3] btrfs: make qgroup rescan work to handle fs freezing Qu Wenruo
` (2 preceding siblings ...)
2026-09-21 9:16 ` [PATCH v2 2/3] btrfs: always show the message when qgroup rescan ended Qu Wenruo
@ 2026-09-21 9:16 ` Qu Wenruo
3 siblings, 0 replies; 18+ messages in thread
From: Qu Wenruo @ 2026-09-21 9:16 UTC (permalink / raw)
To: linux-btrfs; +Cc: Richard Weinberger
[BUG]
There is a bug report that a running qgroup rescan can fail a pm
suspension:
[ T278489] Freezing remaining freezable tasks
[ T278489] Freezing remaining freezable tasks failed after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
[ T278489] Showing freezable workqueues that are still busy:
[...]
[ T278489] pwq 56: cpus=0-13 flags=0x4 nice=0 active=1 refcnt=16
[ T278489] in-flight: 205929:btrfs_work_helper [btrfs] for 111s
[CAUSE]
Btrfs qgroup rescan is running in a workqueue, and when the fs is
frozen, btrfs_start_transaction() will sleep on sb_start_intwrite().
But a sleeping workload still counts as active for the workqueue until
the workload exits.
So the qgroup rescan item will sleep on the frozen fs, and fail the pm
suspension.
[FIX]
I strongly doubt whether we should even use a workqueue for the qgroup
rescan workload, a kthread would be a more suitable choice and can handle
fs and process freezing way better.
But that will be a long term solution.
For now fix the problems by:
- make rescan_should_stop() return true if the fs is frozen
This should handle most cases, also it should clear the RESCAN flag.
- Use btrfs_try_start_transaction() to avoid sleeping on
sb_start_intwrite()
This is less common but still possible if the fs is frozen after
rescan_should_stop() check but before starting a transaction, and if we
sleep on sb_start_intwrite(), the workqueue will never have a chance to
deactivate.
The above fixes should interrupt the rescan, so that pm suspension won't
fail.
And since the INCONSISTENT flag is not cleared, the end user/tool should
re-start a rescan if they need up-to-date qgroup numbers.
Reported-by: Richard Weinberger <richard@nod.at>
Link: https://lore.kernel.org/linux-btrfs/mu8fq8a8.b9a9a1ae-dd6b-45a4-a415-5db853d61536@nod.at/
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 12 ++++++++++--
fs/btrfs/transaction.c | 22 +++++++++++++++++++---
fs/btrfs/transaction.h | 4 ++++
3 files changed, 33 insertions(+), 5 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 4adcc6032dce..8593b38f1bb7 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3879,6 +3879,10 @@ static bool rescan_should_stop(struct btrfs_fs_info *fs_info, bool *clear_rescan
*clear_rescan_ret = false;
return true;
}
+ if (fs_info->sb->s_writers.frozen > SB_UNFROZEN) {
+ *clear_rescan_ret = true;
+ return true;
+ }
if (!btrfs_qgroup_enabled(fs_info)) {
*clear_rescan_ret = true;
return true;
@@ -3918,9 +3922,13 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
path->skip_locking = true;
while (!ret && !(stopped = rescan_should_stop(fs_info, &clear_rescan))) {
- trans = btrfs_start_transaction(fs_info->fs_root, 0);
+ trans = btrfs_try_start_transaction(fs_info->fs_root, 0);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
+ if (ret == -EINTR) {
+ stopped = true;
+ clear_rescan = true;
+ }
break;
}
@@ -3951,7 +3959,7 @@ static void btrfs_qgroup_rescan_worker(struct btrfs_work *work)
* btrfs_quota_disable().
*/
if (did_leaf_rescans) {
- trans = btrfs_start_transaction(fs_info->quota_root, 1);
+ trans = btrfs_try_start_transaction(fs_info->quota_root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
trans = NULL;
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index ca114235bbe1..76787417331e 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -559,7 +559,7 @@ static bool may_wait_transaction(struct btrfs_fs_info *fs_info, int type)
if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags))
return false;
- if (type == TRANS_START)
+ if ((type & TRANS_START) == TRANS_START)
return true;
return false;
@@ -719,8 +719,17 @@ start_transaction(struct btrfs_root *root, unsigned int num_items,
* If we are ATTACH, it means we just want to catch the current
* transaction and commit it, so we needn't do sb_start_intwrite().
*/
- if (type & __TRANS_FREEZABLE)
- sb_start_intwrite(fs_info->sb);
+ if (type & __TRANS_FREEZABLE) {
+ if (type & __TRANS_TRYLOCK) {
+ if (!sb_start_intwrite_trylock(fs_info->sb)) {
+ ret = -EINTR;
+ kmem_cache_free(btrfs_trans_handle_cachep, h);
+ goto alloc_fail;
+ }
+ } else {
+ sb_start_intwrite(fs_info->sb);
+ }
+ }
if (may_wait_transaction(fs_info, type))
wait_current_trans(fs_info, type);
@@ -841,6 +850,13 @@ struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
BTRFS_RESERVE_FLUSH_ALL, true);
}
+struct btrfs_trans_handle *btrfs_try_start_transaction(struct btrfs_root *root,
+ unsigned int num_items)
+{
+ return start_transaction(root, num_items, TRANS_TRY_START,
+ BTRFS_RESERVE_FLUSH_ALL, true);
+}
+
struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
struct btrfs_root *root,
unsigned int num_items)
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 89153cd22596..a9da059d3806 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -127,9 +127,11 @@ enum {
ENUM_BIT(__TRANS_JOIN_NOLOCK),
ENUM_BIT(__TRANS_DUMMY),
ENUM_BIT(__TRANS_JOIN_NOSTART),
+ ENUM_BIT(__TRANS_TRYLOCK),
};
#define TRANS_START (__TRANS_START | __TRANS_FREEZABLE)
+#define TRANS_TRY_START (__TRANS_START | __TRANS_FREEZABLE | __TRANS_TRYLOCK)
#define TRANS_ATTACH (__TRANS_ATTACH)
#define TRANS_JOIN (__TRANS_JOIN | __TRANS_FREEZABLE)
#define TRANS_JOIN_NOLOCK (__TRANS_JOIN_NOLOCK)
@@ -308,6 +310,8 @@ do { \
int btrfs_end_transaction(struct btrfs_trans_handle *trans);
struct btrfs_trans_handle *btrfs_start_transaction(struct btrfs_root *root,
unsigned int num_items);
+struct btrfs_trans_handle *btrfs_try_start_transaction(struct btrfs_root *root,
+ unsigned int num_items);
struct btrfs_trans_handle *btrfs_start_transaction_fallback_global_rsv(
struct btrfs_root *root,
unsigned int num_items);
--
2.55.0
^ permalink raw reply related [flat|nested] 18+ messages in thread