gfs2: init system threads before freeze lock

[ Upstream commit a28dc123fa66ba7f3eca7cffc4b01d96bfd35c27 ]

Patch 96b1454f2e ("gfs2: move freeze glock outside the make_fs_rw and _ro
functions") changed the gfs2 mount sequence so that it holds the freeze
lock before calling gfs2_make_fs_rw. Before this patch, gfs2_make_fs_rw
called init_threads to initialize the quotad and logd threads. That is a
problem if the system needs to withdraw due to IO errors early in the
mount sequence, for example, while initializing the system statfs inode:

1. An IO error causes the statfs glock to not sync properly after
   recovery, and leaves items on the ail list.
2. The leftover items on the ail list causes its do_xmote call to fail,
   which makes it want to withdraw. But since the glock code cannot
   withdraw (because the withdraw sequence uses glocks) it relies upon
   the logd daemon to initiate the withdraw.
3. The withdraw can never be performed by the logd daemon because all
   this takes place before the logd daemon is started.

This patch moves function init_threads from super.c to ops_fstype.c
and it changes gfs2_fill_super to start its threads before holding the
freeze lock, and if there's an error, stop its threads after releasing
it. This allows the logd to run unblocked by the freeze lock. Thus,
the logd daemon can perform its withdraw sequence properly.

Fixes: 96b1454f2e8e ("gfs2: move freeze glock outside the make_fs_rw and _ro functions")
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Bob Peterson 2021-05-14 07:42:33 -05:00 committed by AuxXxilium
parent 7867fca400
commit 0916190817
2 changed files with 48 additions and 55 deletions

View File

@ -1072,6 +1072,34 @@ void gfs2_online_uevent(struct gfs2_sbd *sdp)
kobject_uevent_env(&sdp->sd_kobj, KOBJ_ONLINE, envp); kobject_uevent_env(&sdp->sd_kobj, KOBJ_ONLINE, envp);
} }
static int init_threads(struct gfs2_sbd *sdp)
{
struct task_struct *p;
int error = 0;
p = kthread_run(gfs2_logd, sdp, "gfs2_logd");
if (IS_ERR(p)) {
error = PTR_ERR(p);
fs_err(sdp, "can't start logd thread: %d\n", error);
return error;
}
sdp->sd_logd_process = p;
p = kthread_run(gfs2_quotad, sdp, "gfs2_quotad");
if (IS_ERR(p)) {
error = PTR_ERR(p);
fs_err(sdp, "can't start quotad thread: %d\n", error);
goto fail;
}
sdp->sd_quotad_process = p;
return 0;
fail:
kthread_stop(sdp->sd_logd_process);
sdp->sd_logd_process = NULL;
return error;
}
/** /**
* gfs2_fill_super - Read in superblock * gfs2_fill_super - Read in superblock
* @sb: The VFS superblock * @sb: The VFS superblock
@ -1198,6 +1226,14 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
goto fail_per_node; goto fail_per_node;
} }
if (!sb_rdonly(sb)) {
error = init_threads(sdp);
if (error) {
gfs2_withdraw_delayed(sdp);
goto fail_per_node;
}
}
error = gfs2_freeze_lock(sdp, &freeze_gh, 0); error = gfs2_freeze_lock(sdp, &freeze_gh, 0);
if (error) if (error)
goto fail_per_node; goto fail_per_node;
@ -1207,6 +1243,12 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc)
gfs2_freeze_unlock(&freeze_gh); gfs2_freeze_unlock(&freeze_gh);
if (error) { if (error) {
if (sdp->sd_quotad_process)
kthread_stop(sdp->sd_quotad_process);
sdp->sd_quotad_process = NULL;
if (sdp->sd_logd_process)
kthread_stop(sdp->sd_logd_process);
sdp->sd_logd_process = NULL;
fs_err(sdp, "can't make FS RW: %d\n", error); fs_err(sdp, "can't make FS RW: %d\n", error);
goto fail_per_node; goto fail_per_node;
} }

View File

@ -126,34 +126,6 @@ int gfs2_jdesc_check(struct gfs2_jdesc *jd)
return 0; return 0;
} }
static int init_threads(struct gfs2_sbd *sdp)
{
struct task_struct *p;
int error = 0;
p = kthread_run(gfs2_logd, sdp, "gfs2_logd");
if (IS_ERR(p)) {
error = PTR_ERR(p);
fs_err(sdp, "can't start logd thread: %d\n", error);
return error;
}
sdp->sd_logd_process = p;
p = kthread_run(gfs2_quotad, sdp, "gfs2_quotad");
if (IS_ERR(p)) {
error = PTR_ERR(p);
fs_err(sdp, "can't start quotad thread: %d\n", error);
goto fail;
}
sdp->sd_quotad_process = p;
return 0;
fail:
kthread_stop(sdp->sd_logd_process);
sdp->sd_logd_process = NULL;
return error;
}
/** /**
* gfs2_make_fs_rw - Turn a Read-Only FS into a Read-Write one * gfs2_make_fs_rw - Turn a Read-Only FS into a Read-Write one
* @sdp: the filesystem * @sdp: the filesystem
@ -168,26 +140,17 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
struct gfs2_log_header_host head; struct gfs2_log_header_host head;
int error; int error;
error = init_threads(sdp);
if (error) {
gfs2_withdraw_delayed(sdp);
return error;
}
j_gl->gl_ops->go_inval(j_gl, DIO_METADATA); j_gl->gl_ops->go_inval(j_gl, DIO_METADATA);
if (gfs2_withdrawn(sdp)) { if (gfs2_withdrawn(sdp))
error = -EIO; return -EIO;
goto fail;
}
error = gfs2_find_jhead(sdp->sd_jdesc, &head, false); error = gfs2_find_jhead(sdp->sd_jdesc, &head, false);
if (error || gfs2_withdrawn(sdp)) if (error || gfs2_withdrawn(sdp))
goto fail; return error;
if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) { if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) {
gfs2_consist(sdp); gfs2_consist(sdp);
error = -EIO; return -EIO;
goto fail;
} }
/* Initialize some head of the log stuff */ /* Initialize some head of the log stuff */
@ -195,20 +158,8 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp)
gfs2_log_pointers_init(sdp, head.lh_blkno); gfs2_log_pointers_init(sdp, head.lh_blkno);
error = gfs2_quota_init(sdp); error = gfs2_quota_init(sdp);
if (error || gfs2_withdrawn(sdp)) if (!error && !gfs2_withdrawn(sdp))
goto fail; set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags);
return 0;
fail:
if (sdp->sd_quotad_process)
kthread_stop(sdp->sd_quotad_process);
sdp->sd_quotad_process = NULL;
if (sdp->sd_logd_process)
kthread_stop(sdp->sd_logd_process);
sdp->sd_logd_process = NULL;
return error; return error;
} }