Linux Btrfs filesystem development
 help / color / mirror / Atom feed
From: Qu Wenruo <wqu@suse.com>
To: Johannes Thumshirn <johannes.thumshirn@wdc.com>,
	Richard Weinberger <richard@nod.at>
Cc: linux-btrfs@vger.kernel.org
Subject: Re: qgroup rescan worker makes suspend fail
Date: Sun, 20 Sep 2026 08:18:20 +0930	[thread overview]
Message-ID: <98f6ac8c-48ca-4d07-a120-2dbefcbb443f@suse.com> (raw)
In-Reply-To: <38c0b7f1-a876-45ab-92e2-223e2f70d82d@suse.com>



在 2026/9/20 08:09, Qu Wenruo 写道:
> 
> 
> 在 2026/9/20 03:04, Johannes Thumshirn 写道:
>> On Sat, Sep 19, 2026 at 01:40:27PM +0000, Richard Weinberger wrote:
>>> Once in a while, suspending my laptop just causes the screen to freeze.
>>> Initially, I thought Linux had crashed, but it usually recovers after 
>>> 2 minutes,
>>> though the suspend doesn't actually happen. You can imagine this can 
>>> be very
>>> unfortunate when you just close the laptop lid and pack the laptop 
>>> into your
>>> bag...
>>>
>>> After the problem started occurring more frequently, I investigated 
>>> and found
>>> that the qgroup rescan worker is the problem. In dmesg, logs like 
>>> these can
>>> usually be found:
> 
> To be honest, qgroup mode is no longer recommended, except for rigid 
> subvolume layouts, and since you need rescan it's definitely the not 
> recommended case.
> 
> The current only well known user is snapper, and we're pushing snapper 
> not to utilize qgroup by default.
> 
> So unless you have a very clear use case, it's better just disable 
> qgroups completely.
>>>
>>> [246013.777637] [ T278489] Freezing remaining freezable tasks
>>> [246033.780538] [ T278489] Freezing remaining freezable tasks failed 
>>> after 20.003 seconds (0 tasks refusing to freeze, wq_busy=1):
>>> [246033.780576] [ T278489] Showing freezable workqueues that are 
>>> still busy:
>>> [246033.780582] [ T278489] workqueue events_freezable: flags=0x104
>>> [246033.780590] [ T278489]   pwq 10: cpus=2 node=0 flags=0x0 nice=0 
>>> active=0 refcnt=2
>>> [246033.780609] [ T278489]     inactive: pci_pme_list_scan
>>> [246033.780642] [ T278489] workqueue btrfs-endio-meta: flags=0xe
>>> [246033.780649] [ T278489]   pwq 57: cpus=0-13 node=0 flags=0x4 
>>> nice=0 active=0 refcnt=2
>>> [246033.780660] [ T278489]     inactive: simple_end_io_work [btrfs]
>>> [246033.781183] [ T278489] workqueue btrfs-qgroup-rescan: flags=0x2000e
>>> [246033.781189] [ T278489]   pwq 56: cpus=0-13 flags=0x4 nice=0 
>>> active=1 refcnt=16
>>> [246033.781199] [ T278489]     in-flight: 205929:btrfs_work_helper 
>>> [btrfs] for 111s
>>> [246033.781721] [ T278489] workqueue wg-kex-wginterproc: flags=0x6
>>> [246033.781726] [ T278489]   pwq 57: cpus=0-13 node=0 flags=0x4 
>>> nice=0 active=0 refcnt=2
>>> [246033.781736] [ T278489]     inactive: 
>>> wg_packet_handshake_send_worker [wireguard]
>>>
>>> My first thought was that the worker is likely not freezable, but it is.
>>> The problem is that the whole qgroup rescan is a single work item.
>>> In my case, such a scan can take up to 10 minutes, even though I have a
>>> fast NVMe SSD installed...
>>>
>>> Wouldn't it make sense to have rescan_should_stop() return true when
>>> suspend starts? I think using a pm notifier could help here.
>>> What do you think?
>>
>> Something like this (completely untested):
>>
>> diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
>> index a1d83ad9a4c0..541735d8fdb6 100644
>> --- a/fs/btrfs/disk-io.c
>> +++ b/fs/btrfs/disk-io.c
>> @@ -17,6 +17,7 @@
>>   #include <linux/error-injection.h>
>>   #include <linux/crc32c.h>
>>   #include <linux/sched/mm.h>
>> +#include <linux/suspend.h>
>>   #include <linux/unaligned.h>
>>   #include "ctree.h"
>>   #include "disk-io.h"
>> @@ -3198,6 +3199,29 @@ int btrfs_start_pre_rw_mount(struct 
>> btrfs_fs_info *fs_info)
>>       return 0;
>>   }
>> +static int btrfs_pm_notifier(struct notifier_block *nb, unsigned long 
>> action,
>> +                 void *data)
>> +{
>> +    struct btrfs_fs_info *fs_info = container_of(nb, struct 
>> btrfs_fs_info,
>> +                             pm_notifier);
> 
> IIRC this is a little too complex, we had similar cases in scrub, which 
> checks "freezing(current)".

OK, that doesn't work for freezable workqueue.

But we still have super block level s_writers.frozen checks to detect if 
the fs is being frozen.
It may not be good enough depending on if pm freezes processes or fs first.

I think it may be better to migrate the qgroup rescan worker to a 
dedicated kthread instead, then we can have much simpler checks.

Thanks,
Qu
> 
> That looks like a much simpler solution.
> 
> Thanks,
> Qu
> 
>> +
>> +    switch (action) {
>> +    case PM_HIBERNATION_PREPARE:
>> +    case PM_SUSPEND_PREPARE:
>> +    case PM_RESTORE_PREPARE:
>> +        set_bit(BTRFS_FS_PM_SUSPENDING, &fs_info->flags);
>> +        break;
>> +    case PM_POST_HIBERNATION:
>> +    case PM_POST_SUSPEND:
>> +    case PM_POST_RESTORE:
>> +        clear_bit(BTRFS_FS_PM_SUSPENDING, &fs_info->flags);
>> +        btrfs_qgroup_rescan_resume(fs_info);
>> +        break;
>> +    }
>> +
>> +    return NOTIFY_DONE;
>> +}
>> +
>>   /*
>>    * Do various sanity and dependency checks of different features.
>>    *
>> @@ -3794,6 +3818,9 @@ int __cold open_ctree(struct super_block *sb, 
>> struct btrfs_fs_devices *fs_device
>>       set_bit(BTRFS_FS_OPEN, &fs_info->flags);
>> +    fs_info->pm_notifier.notifier_call = btrfs_pm_notifier;
>> +    register_pm_notifier(&fs_info->pm_notifier);
>> +
>>       /* Kick the cleaner thread so it'll start deleting snapshots. */
>>       if (test_bit(BTRFS_FS_UNFINISHED_DROPS, &fs_info->flags))
>>           wake_up_process(fs_info->cleaner_kthread);
>> @@ -4370,6 +4397,8 @@ void __cold close_ctree(struct btrfs_fs_info 
>> *fs_info)
>>        */
>>       kthread_park(fs_info->cleaner_kthread);
>> +    unregister_pm_notifier(&fs_info->pm_notifier);
>> +
>>       /* wait for the qgroup rescan worker to stop */
>>       btrfs_qgroup_wait_for_completion(fs_info, false);
>> diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
>> index 3eba8438593c..caa90dc98e59 100644
>> --- a/fs/btrfs/fs.h
>> +++ b/fs/btrfs/fs.h
>> @@ -26,6 +26,7 @@
>>   #include <linux/wait_bit.h>
>>   #include <linux/sched.h>
>>   #include <linux/rbtree.h>
>> +#include <linux/notifier.h>
>>   #include <linux/xxhash.h>
>>   #include <linux/fserror.h>
>>   #include <uapi/linux/btrfs.h>
>> @@ -234,6 +235,8 @@ enum {
>>        */
>>       BTRFS_FS_UNALIGNED_TREE_BLOCK,
>> +    BTRFS_FS_PM_SUSPENDING,
>> +
>>   #if BITS_PER_LONG == 32
>>       /* Indicate if we have error/warn message printed on 32bit 
>> systems */
>>       BTRFS_FS_32BIT_ERROR,
>> @@ -841,6 +844,8 @@ struct btrfs_fs_info {
>>       u8 qgroup_drop_subtree_thres;
>>       u64 qgroup_enable_gen;
>> +    struct notifier_block pm_notifier;
>> +
>>       /*
>>        * If this is not 0, then it indicates a serious filesystem 
>> error has
>>        * happened and it contains that error (negative errno value).
>> diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
>> index 05e35eb126dc..b4f1290d1e14 100644
>> --- a/fs/btrfs/qgroup.c
>> +++ b/fs/btrfs/qgroup.c
>> @@ -3883,6 +3883,7 @@ static void btrfs_qgroup_rescan_worker(struct 
>> btrfs_work *work)
>>       struct btrfs_trans_handle *trans = NULL;
>>       int ret = 0;
>>       bool stopped = false;
>> +    bool pm_paused = false;
>>       bool did_leaf_rescans = false;
>>       if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
>> @@ -3900,7 +3901,18 @@ 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) {
>> +        if (rescan_should_stop(fs_info)) {
>> +            stopped = true;
>> +            break;
>> +        }
>> +
>> +        if (test_bit(BTRFS_FS_PM_SUSPENDING, &fs_info->flags)) {
>> +            stopped = true;
>> +            pm_paused = true;
>> +            break;
>> +        }
>> +
>>           trans = btrfs_start_transaction(fs_info->fs_root, 0);
>>           if (IS_ERR(trans)) {
>>               ret = PTR_ERR(trans);
>> @@ -3963,12 +3975,17 @@ 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 (pm_paused && !test_bit(BTRFS_FS_PM_SUSPENDING, &fs_info->flags))
>> +        btrfs_qgroup_rescan_resume(fs_info);
>> +
>>       if (!trans)
>>           return;
>>       btrfs_end_transaction(trans);
>> -    if (stopped) {
>> +    if (pm_paused) {
>> +        btrfs_info(fs_info, "qgroup scan paused for system suspend");
>> +    } else 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");
>> @@ -4142,13 +4159,20 @@ int btrfs_qgroup_wait_for_completion(struct 
>> btrfs_fs_info *fs_info,
>>   void
>>   btrfs_qgroup_rescan_resume(struct btrfs_fs_info *fs_info)
>>   {
>> -    if (test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info- 
>> >qgroup_flags)) {
>> -        mutex_lock(&fs_info->qgroup_rescan_lock);
>> +    if (!test_bit(BTRFS_QGROUP_STATUS_BIT_RESCAN, &fs_info- 
>> >qgroup_flags))
>> +        return;
>> +
>> +    if (btrfs_fs_closing(fs_info))
>> +        return;
>> +
>> +    mutex_lock(&fs_info->qgroup_rescan_lock);
>> +    if (!fs_info->qgroup_rescan_running) {
>> +        reinit_completion(&fs_info->qgroup_rescan_completion);
>>           fs_info->qgroup_rescan_running = true;
>>           btrfs_queue_work(fs_info->qgroup_rescan_workers,
>>                    &fs_info->qgroup_rescan_work);
>> -        mutex_unlock(&fs_info->qgroup_rescan_lock);
>>       }
>> +    mutex_unlock(&fs_info->qgroup_rescan_lock);
>>   }
>>   #define rbtree_iterate_from_safe(node, next, start)                \
> 


  reply	other threads:[~2026-09-19 22:48 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-19 13:40 qgroup rescan worker makes suspend fail Richard Weinberger
2026-09-19 17:34 ` Johannes Thumshirn
2026-09-19 18:33   ` AW: " Richard Weinberger
2026-09-19 22:39   ` Qu Wenruo
2026-09-19 22:48     ` Qu Wenruo [this message]
2026-09-20  4:56     ` Andrei Borzenkov
2026-09-20  5:28       ` Qu Wenruo
2026-09-19 23:38 ` Qu Wenruo
2026-09-20 13:04   ` AW: " Richard Weinberger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=98f6ac8c-48ca-4d07-a120-2dbefcbb443f@suse.com \
    --to=wqu@suse.com \
    --cc=johannes.thumshirn@wdc.com \
    --cc=linux-btrfs@vger.kernel.org \
    --cc=richard@nod.at \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox