| From b143fd003323677769860f4f1ec94593479f51d4 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 14 May 2021 07:42:33 -0500 |
| Subject: gfs2: init system threads before freeze lock |
| |
| From: Bob Peterson <rpeterso@redhat.com> |
| |
| [ 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> |
| --- |
| fs/gfs2/ops_fstype.c | 42 ++++++++++++++++++++++++++++++ |
| fs/gfs2/super.c | 61 +++++--------------------------------------- |
| 2 files changed, 48 insertions(+), 55 deletions(-) |
| |
| diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c |
| index bd3b3be1a473..ca76e3b8792c 100644 |
| --- a/fs/gfs2/ops_fstype.c |
| +++ b/fs/gfs2/ops_fstype.c |
| @@ -1089,6 +1089,34 @@ void gfs2_online_uevent(struct gfs2_sbd *sdp) |
| 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 |
| * @sb: The VFS superblock |
| @@ -1217,6 +1245,14 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) |
| 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); |
| if (error) |
| goto fail_per_node; |
| @@ -1226,6 +1262,12 @@ static int gfs2_fill_super(struct super_block *sb, struct fs_context *fc) |
| |
| gfs2_freeze_unlock(&freeze_gh); |
| 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); |
| goto fail_per_node; |
| } |
| diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c |
| index 4d4ceb0b6903..2bdbba5ea8d7 100644 |
| --- a/fs/gfs2/super.c |
| +++ b/fs/gfs2/super.c |
| @@ -119,34 +119,6 @@ int gfs2_jdesc_check(struct gfs2_jdesc *jd) |
| 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 |
| * @sdp: the filesystem |
| @@ -161,26 +133,17 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) |
| struct gfs2_log_header_host head; |
| int error; |
| |
| - error = init_threads(sdp); |
| - if (error) { |
| - gfs2_withdraw_delayed(sdp); |
| - return error; |
| - } |
| - |
| j_gl->gl_ops->go_inval(j_gl, DIO_METADATA); |
| - if (gfs2_withdrawn(sdp)) { |
| - error = -EIO; |
| - goto fail; |
| - } |
| + if (gfs2_withdrawn(sdp)) |
| + return -EIO; |
| |
| error = gfs2_find_jhead(sdp->sd_jdesc, &head, false); |
| if (error || gfs2_withdrawn(sdp)) |
| - goto fail; |
| + return error; |
| |
| if (!(head.lh_flags & GFS2_LOG_HEAD_UNMOUNT)) { |
| gfs2_consist(sdp); |
| - error = -EIO; |
| - goto fail; |
| + return -EIO; |
| } |
| |
| /* Initialize some head of the log stuff */ |
| @@ -188,20 +151,8 @@ int gfs2_make_fs_rw(struct gfs2_sbd *sdp) |
| gfs2_log_pointers_init(sdp, head.lh_blkno); |
| |
| error = gfs2_quota_init(sdp); |
| - if (error || gfs2_withdrawn(sdp)) |
| - goto fail; |
| - |
| - 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; |
| + if (!error && !gfs2_withdrawn(sdp)) |
| + set_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags); |
| return error; |
| } |
| |
| -- |
| 2.30.2 |
| |