Skip to content

Commit 9c4a3de

Browse files
author
Andreas Gruenbacher
committed
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 <agruenba@redhat.com>
1 parent 9334c73 commit 9c4a3de

5 files changed

Lines changed: 37 additions & 25 deletions

File tree

fs/gfs2/incore.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,7 @@ struct gfs2_sbd {
716716
struct gfs2_glock *sd_rename_gl;
717717
struct gfs2_glock *sd_freeze_gl;
718718
struct work_struct sd_freeze_work;
719+
struct work_struct sd_withdraw_work;
719720
wait_queue_head_t sd_kill_wait;
720721
wait_queue_head_t sd_async_glock_wait;
721722
atomic_t sd_glock_disposal;

fs/gfs2/ops_fstype.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,8 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
12151215
if (error)
12161216
goto fail_debug;
12171217

1218+
INIT_WORK(&sdp->sd_withdraw_work, gfs2_withdraw_func);
1219+
12181220
error = init_locking(sdp, &mount_gh, DO);
12191221
if (error)
12201222
goto fail_lm;

fs/gfs2/super.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ static void gfs2_put_super(struct super_block *sb)
603603
gfs2_quota_cleanup(sdp);
604604
}
605605

606-
WARN_ON(gfs2_withdrawing(sdp));
606+
flush_work(&sdp->sd_withdraw_work);
607607

608608
/* At this point, we're through modifying the disk */
609609

fs/gfs2/util.c

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -309,43 +309,50 @@ void gfs2_lm(struct gfs2_sbd *sdp, const char *fmt, ...)
309309
va_end(args);
310310
}
311311

312-
void gfs2_withdraw(struct gfs2_sbd *sdp)
312+
void gfs2_withdraw_func(struct work_struct *work)
313313
{
314+
struct gfs2_sbd *sdp = container_of(work, struct gfs2_sbd, sd_withdraw_work);
314315
struct lm_lockstruct *ls = &sdp->sd_lockstruct;
315316
const struct lm_lockops *lm = ls->ls_ops;
316317

318+
if (test_bit(SDF_KILL, &sdp->sd_flags))
319+
return;
320+
321+
BUG_ON(sdp->sd_args.ar_debug);
322+
323+
signal_our_withdraw(sdp);
324+
325+
kobject_uevent(&sdp->sd_kobj, KOBJ_OFFLINE);
326+
327+
if (!strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm"))
328+
wait_for_completion(&sdp->sd_wdack);
329+
330+
if (lm->lm_unmount)
331+
lm->lm_unmount(sdp, false);
332+
fs_err(sdp, "file system withdrawn\n");
333+
clear_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags);
334+
}
335+
336+
void gfs2_withdraw(struct gfs2_sbd *sdp)
337+
{
317338
if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW) {
318339
unsigned long old = READ_ONCE(sdp->sd_flags), new;
319340

320341
do {
321-
if (old & BIT(SDF_WITHDRAWN)) {
322-
wait_on_bit(&sdp->sd_flags,
323-
SDF_WITHDRAW_IN_PROG,
324-
TASK_UNINTERRUPTIBLE);
342+
if (old & BIT(SDF_WITHDRAWN))
325343
return;
326-
}
327344
new = old | BIT(SDF_WITHDRAWN) | BIT(SDF_WITHDRAW_IN_PROG);
328345
} while (unlikely(!try_cmpxchg(&sdp->sd_flags, &old, new)));
329346

330-
fs_err(sdp, "about to withdraw this file system\n");
331-
BUG_ON(sdp->sd_args.ar_debug);
332-
333-
signal_our_withdraw(sdp);
334-
335-
kobject_uevent(&sdp->sd_kobj, KOBJ_OFFLINE);
336-
337-
if (!strcmp(sdp->sd_lockstruct.ls_ops->lm_proto_name, "lock_dlm"))
338-
wait_for_completion(&sdp->sd_wdack);
339-
340-
if (lm->lm_unmount) {
341-
fs_err(sdp, "telling LM to unmount\n");
342-
lm->lm_unmount(sdp, false);
343-
}
344-
fs_err(sdp, "File system withdrawn\n");
345347
dump_stack();
346-
clear_bit(SDF_WITHDRAW_IN_PROG, &sdp->sd_flags);
347-
smp_mb__after_atomic();
348-
wake_up_bit(&sdp->sd_flags, SDF_WITHDRAW_IN_PROG);
348+
/*
349+
* There is no need to withdraw when the superblock hasn't been
350+
* fully initialized, yet.
351+
*/
352+
if (!(sdp->sd_vfs->s_flags & SB_BORN))
353+
return;
354+
fs_err(sdp, "about to withdraw this file system\n");
355+
schedule_work(&sdp->sd_withdraw_work);
349356
}
350357

351358
if (sdp->sd_args.ar_errors == GFS2_ERRORS_PANIC)

fs/gfs2/util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ gfs2_tune_get_i(&(sdp)->sd_tune, &(sdp)->sd_tune.field)
232232

233233
__printf(2, 3)
234234
void gfs2_lm(struct gfs2_sbd *sdp, const char *fmt, ...);
235+
236+
void gfs2_withdraw_func(struct work_struct *work);
235237
void gfs2_withdraw(struct gfs2_sbd *sdp);
236238

237239
#endif /* __UTIL_DOT_H__ */

0 commit comments

Comments
 (0)