mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
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>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -603,7 +603,7 @@ restart:
|
||||
gfs2_quota_cleanup(sdp);
|
||||
}
|
||||
|
||||
WARN_ON(gfs2_withdrawing(sdp));
|
||||
flush_work(&sdp->sd_withdraw_work);
|
||||
|
||||
/* At this point, we're through modifying the disk */
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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__ */
|
||||
|
||||
Reference in New Issue
Block a user