From: Andreas Gruenbacher Date: Tue, 5 Aug 2025 21:12:06 +0000 (+0200) Subject: gfs2: Asynchronous withdraw X-Git-Url: https://gentwo.org/gitweb/?a=commitdiff_plain;h=9c4a3de6cd5b46aa743c460b86eb58fe5f93daec;p=linux%2F.git gfs2: Asynchronous withdraw 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 --- diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h index 5a0ea416cfda..db0e72adb999 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h @@ -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; diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c index f748d320fa14..c42982bdd4b2 100644 --- a/fs/gfs2/ops_fstype.c +++ b/fs/gfs2/ops_fstype.c @@ -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; diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 54c6f2098f01..ea9084e871a8 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -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 */ diff --git a/fs/gfs2/util.c b/fs/gfs2/util.c index 27fdcbce2d75..c454bea101de 100644 --- a/fs/gfs2/util.c +++ b/fs/gfs2/util.c @@ -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) diff --git a/fs/gfs2/util.h b/fs/gfs2/util.h index da0373b1e82b..f424ef2523c4 100644 --- a/fs/gfs2/util.h +++ b/fs/gfs2/util.h @@ -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__ */