]> Gentwo Git Trees - linux/.git/commitdiff
gfs2: Asynchronous withdraw
authorAndreas Gruenbacher <agruenba@redhat.com>
Tue, 5 Aug 2025 21:12:06 +0000 (23:12 +0200)
committerAndreas Gruenbacher <agruenba@redhat.com>
Wed, 26 Nov 2025 23:51:47 +0000 (23:51 +0000)
So far, withdraws are carried out in the context of the calling task.
When another task tries to withdraw while a withdraw is already
underway, that task blocks as well.  Change that to carry out withdraws
asynchronously in workqueue context and don't block the task triggering
the withdraw anymore.

Fixes: syzbot+6b156e132970e550194c@syzkaller.appspotmail.com
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
fs/gfs2/incore.h
fs/gfs2/ops_fstype.c
fs/gfs2/super.c
fs/gfs2/util.c
fs/gfs2/util.h

index 5a0ea416cfdae9c64952d4ef9d0b036ef5a03b20..db0e72adb99961332189746e1c0de1e8f26a42fd 100644 (file)
@@ -716,6 +716,7 @@ struct gfs2_sbd {
        struct gfs2_glock *sd_rename_gl;
        struct gfs2_glock *sd_freeze_gl;
        struct work_struct sd_freeze_work;
+       struct work_struct sd_withdraw_work;
        wait_queue_head_t sd_kill_wait;
        wait_queue_head_t sd_async_glock_wait;
        atomic_t sd_glock_disposal;
index f748d320fa1489821c9edf34d363db2600886971..c42982bdd4b209980bb7817284a25789857f5df2 100644 (file)
@@ -1215,6 +1215,8 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
        if (error)
                goto fail_debug;
 
+       INIT_WORK(&sdp->sd_withdraw_work, gfs2_withdraw_func);
+
        error = init_locking(sdp, &mount_gh, DO);
        if (error)
                goto fail_lm;
index 54c6f2098f01ef320e29589ee1ace0fef753f845..ea9084e871a8f2014f7dce838bbd5bec795c2f8c 100644 (file)
@@ -603,7 +603,7 @@ static void gfs2_put_super(struct super_block *sb)
                gfs2_quota_cleanup(sdp);
        }
 
-       WARN_ON(gfs2_withdrawing(sdp));
+       flush_work(&sdp->sd_withdraw_work);
 
        /*  At this point, we're through modifying the disk  */
 
index 27fdcbce2d754671176ba51f1b3aaf5c77f9f3f2..c454bea101dec4e2cdc4fcfa417de653cd36f6e0 100644 (file)
@@ -309,43 +309,50 @@ void gfs2_lm(struct gfs2_sbd *sdp, const char *fmt, ...)
        va_end(args);
 }
 
-void gfs2_withdraw(struct gfs2_sbd *sdp)
+void gfs2_withdraw_func(struct work_struct *work)
 {
+       struct gfs2_sbd *sdp = container_of(work, struct gfs2_sbd, sd_withdraw_work);
        struct lm_lockstruct *ls = &sdp->sd_lockstruct;
        const struct lm_lockops *lm = ls->ls_ops;
 
+       if (test_bit(SDF_KILL, &sdp->sd_flags))
+               return;
+
+       BUG_ON(sdp->sd_args.ar_debug);
+
+       signal_our_withdraw(sdp);
+
+       kobject_uevent(&sdp->sd_kobj, KOBJ_OFFLINE);
+
+       if (!strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm"))
+               wait_for_completion(&sdp->sd_wdack);
+
+       if (lm->lm_unmount)
+               lm->lm_unmount(sdp, false);
+       fs_err(sdp, "file system withdrawn\n");
+       clear_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags);
+}
+
+void gfs2_withdraw(struct gfs2_sbd *sdp)
+{
        if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW) {
                unsigned long old = READ_ONCE(sdp->sd_flags), new;
 
                do {
-                       if (old & BIT(SDF_WITHDRAWN)) {
-                               wait_on_bit(&sdp->sd_flags,
-                                           SDF_WITHDRAW_IN_PROG,
-                                           TASK_UNINTERRUPTIBLE);
+                       if (old & BIT(SDF_WITHDRAWN))
                                return;
-                       }
                        new = old | BIT(SDF_WITHDRAWN) | BIT(SDF_WITHDRAW_IN_PROG);
                } while (unlikely(!try_cmpxchg(&sdp->sd_flags, &old, new)));
 
-               fs_err(sdp, "about to withdraw this file system\n");
-               BUG_ON(sdp->sd_args.ar_debug);
-
-               signal_our_withdraw(sdp);
-
-               kobject_uevent(&sdp->sd_kobj, KOBJ_OFFLINE);
-
-               if (!strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm"))
-                       wait_for_completion(&sdp->sd_wdack);
-
-               if (lm->lm_unmount) {
-                       fs_err(sdp, "telling LM to unmount\n");
-                       lm->lm_unmount(sdp, false);
-               }
-               fs_err(sdp, "File system withdrawn\n");
                dump_stack();
-               clear_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags);
-               smp_mb__after_atomic();
-               wake_up_bit(&sdp->sd_flags, SDF_WITHDRAW_IN_PROG);
+               /*
+                * There is no need to withdraw when the superblock hasn't been
+                * fully initialized, yet.
+                */
+               if (!(sdp->sd_vfs->s_flags & SB_BORN))
+                       return;
+               fs_err(sdp, "about to withdraw this file system\n");
+               schedule_work(&sdp->sd_withdraw_work);
        }
 
        if (sdp->sd_args.ar_errors == GFS2_ERRORS_PANIC)
index da0373b1e82b9e464ced97cab14ce750fd8bc820..f424ef2523c4332d6ebead7772a1d1c6373dddd7 100644 (file)
@@ -232,6 +232,8 @@ gfs2_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field)
 
 __printf(2, 3)
 void gfs2_lm(struct gfs2_sbd *sdp, const char *fmt, ...);
+
+void gfs2_withdraw_func(struct work_struct *work);
 void gfs2_withdraw(struct gfs2_sbd *sdp);
 
 #endif /* __UTIL_DOT_H__ */