| From 0ac1aa6fa53dacab3e1a3d92607ea22045498a1b Mon Sep 17 00:00:00 2001 |
| From: Abhi Das <adas@redhat.com> |
| Date: Tue, 30 Apr 2019 16:53:47 -0500 |
| Subject: gfs2: fix race between gfs2_freeze_func and unmount |
| |
| [ Upstream commit 8f91821990fd6f170a5dca79697a441181a41b16 ] |
| |
| As part of the freeze operation, gfs2_freeze_func() is left blocking |
| on a request to hold the sd_freeze_gl in SH. This glock is held in EX |
| by the gfs2_freeze() code. |
| |
| A subsequent call to gfs2_unfreeze() releases the EXclusively held |
| sd_freeze_gl, which allows gfs2_freeze_func() to acquire it in SH and |
| resume its operation. |
| |
| gfs2_unfreeze(), however, doesn't wait for gfs2_freeze_func() to complete. |
| If a umount is issued right after unfreeze, it could result in an |
| inconsistent filesystem because some journal data (statfs update) isn't |
| written out. |
| |
| Refer to commit 24972557b12c for a more detailed explanation of how |
| freeze/unfreeze work. |
| |
| This patch causes gfs2_unfreeze() to wait for gfs2_freeze_func() to |
| complete before returning to the user. |
| |
| Signed-off-by: Abhi Das <adas@redhat.com> |
| Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/gfs2/incore.h | 1 + |
| fs/gfs2/super.c | 8 +++++--- |
| 2 files changed, 6 insertions(+), 3 deletions(-) |
| |
| diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h |
| index cdf07b408f54c..539e8dc5a3f6c 100644 |
| --- a/fs/gfs2/incore.h |
| +++ b/fs/gfs2/incore.h |
| @@ -621,6 +621,7 @@ enum { |
| SDF_SKIP_DLM_UNLOCK = 8, |
| SDF_FORCE_AIL_FLUSH = 9, |
| SDF_AIL1_IO_ERROR = 10, |
| + SDF_FS_FROZEN = 11, |
| }; |
| |
| enum gfs2_freeze_state { |
| diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c |
| index ca71163ff7cfd..360206704a14c 100644 |
| --- a/fs/gfs2/super.c |
| +++ b/fs/gfs2/super.c |
| @@ -973,8 +973,7 @@ void gfs2_freeze_func(struct work_struct *work) |
| if (error) { |
| printk(KERN_INFO "GFS2: couldn't get freeze lock : %d\n", error); |
| gfs2_assert_withdraw(sdp, 0); |
| - } |
| - else { |
| + } else { |
| atomic_set(&sdp->sd_freeze_state, SFS_UNFROZEN); |
| error = thaw_super(sb); |
| if (error) { |
| @@ -987,6 +986,8 @@ void gfs2_freeze_func(struct work_struct *work) |
| gfs2_glock_dq_uninit(&freeze_gh); |
| } |
| deactivate_super(sb); |
| + clear_bit_unlock(SDF_FS_FROZEN, &sdp->sd_flags); |
| + wake_up_bit(&sdp->sd_flags, SDF_FS_FROZEN); |
| return; |
| } |
| |
| @@ -1029,6 +1030,7 @@ static int gfs2_freeze(struct super_block *sb) |
| msleep(1000); |
| } |
| error = 0; |
| + set_bit(SDF_FS_FROZEN, &sdp->sd_flags); |
| out: |
| mutex_unlock(&sdp->sd_freeze_mutex); |
| return error; |
| @@ -1053,7 +1055,7 @@ static int gfs2_unfreeze(struct super_block *sb) |
| |
| gfs2_glock_dq_uninit(&sdp->sd_freeze_gh); |
| mutex_unlock(&sdp->sd_freeze_mutex); |
| - return 0; |
| + return wait_on_bit(&sdp->sd_flags, SDF_FS_FROZEN, TASK_INTERRUPTIBLE); |
| } |
| |
| /** |
| -- |
| 2.20.1 |
| |