diff options
author | Abhi Das <adas@redhat.com> | 2019-04-30 17:53:47 -0400 |
---|---|---|
committer | Andreas Gruenbacher <agruenba@redhat.com> | 2019-05-07 17:39:14 -0400 |
commit | 8f91821990fd6f170a5dca79697a441181a41b16 (patch) | |
tree | 6efd729419e769c9a86681b5a8aca56e6c3e5995 /fs/gfs2 | |
parent | fbb27873f21d5fb9bf556edcaa67e9891636e5d4 (diff) |
gfs2: fix race between gfs2_freeze_func and unmount
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>
Diffstat (limited to 'fs/gfs2')
-rw-r--r-- | fs/gfs2/incore.h | 1 | ||||
-rw-r--r-- | 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 78c8e761b321..b15755068593 100644 --- a/fs/gfs2/incore.h +++ b/fs/gfs2/incore.h | |||
@@ -621,6 +621,7 @@ enum { | |||
621 | SDF_SKIP_DLM_UNLOCK = 8, | 621 | SDF_SKIP_DLM_UNLOCK = 8, |
622 | SDF_FORCE_AIL_FLUSH = 9, | 622 | SDF_FORCE_AIL_FLUSH = 9, |
623 | SDF_AIL1_IO_ERROR = 10, | 623 | SDF_AIL1_IO_ERROR = 10, |
624 | SDF_FS_FROZEN = 11, | ||
624 | }; | 625 | }; |
625 | 626 | ||
626 | enum gfs2_freeze_state { | 627 | enum gfs2_freeze_state { |
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index 6d89d0ecec2a..5b148cf03293 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c | |||
@@ -973,8 +973,7 @@ void gfs2_freeze_func(struct work_struct *work) | |||
973 | if (error) { | 973 | if (error) { |
974 | printk(KERN_INFO "GFS2: couldn't get freeze lock : %d\n", error); | 974 | printk(KERN_INFO "GFS2: couldn't get freeze lock : %d\n", error); |
975 | gfs2_assert_withdraw(sdp, 0); | 975 | gfs2_assert_withdraw(sdp, 0); |
976 | } | 976 | } else { |
977 | else { | ||
978 | atomic_set(&sdp->sd_freeze_state, SFS_UNFROZEN); | 977 | atomic_set(&sdp->sd_freeze_state, SFS_UNFROZEN); |
979 | error = thaw_super(sb); | 978 | error = thaw_super(sb); |
980 | if (error) { | 979 | if (error) { |
@@ -987,6 +986,8 @@ void gfs2_freeze_func(struct work_struct *work) | |||
987 | gfs2_glock_dq_uninit(&freeze_gh); | 986 | gfs2_glock_dq_uninit(&freeze_gh); |
988 | } | 987 | } |
989 | deactivate_super(sb); | 988 | deactivate_super(sb); |
989 | clear_bit_unlock(SDF_FS_FROZEN, &sdp->sd_flags); | ||
990 | wake_up_bit(&sdp->sd_flags, SDF_FS_FROZEN); | ||
990 | return; | 991 | return; |
991 | } | 992 | } |
992 | 993 | ||
@@ -1029,6 +1030,7 @@ static int gfs2_freeze(struct super_block *sb) | |||
1029 | msleep(1000); | 1030 | msleep(1000); |
1030 | } | 1031 | } |
1031 | error = 0; | 1032 | error = 0; |
1033 | set_bit(SDF_FS_FROZEN, &sdp->sd_flags); | ||
1032 | out: | 1034 | out: |
1033 | mutex_unlock(&sdp->sd_freeze_mutex); | 1035 | mutex_unlock(&sdp->sd_freeze_mutex); |
1034 | return error; | 1036 | return error; |
@@ -1053,7 +1055,7 @@ static int gfs2_unfreeze(struct super_block *sb) | |||
1053 | 1055 | ||
1054 | gfs2_glock_dq_uninit(&sdp->sd_freeze_gh); | 1056 | gfs2_glock_dq_uninit(&sdp->sd_freeze_gh); |
1055 | mutex_unlock(&sdp->sd_freeze_mutex); | 1057 | mutex_unlock(&sdp->sd_freeze_mutex); |
1056 | return 0; | 1058 | return wait_on_bit(&sdp->sd_flags, SDF_FS_FROZEN, TASK_INTERRUPTIBLE); |
1057 | } | 1059 | } |
1058 | 1060 | ||
1059 | /** | 1061 | /** |