From e345b87b0b0444d1c644b0ea15cfb50e88f10b55 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Tue, 19 Dec 2023 16:49:26 +0100 Subject: gfs2: Fix freeze consistency check in log_write_header Functions gfs2_freeze_super() and gfs2_thaw_super() are using the SDF_FROZEN flag to indicate when the filesystem is frozen, synchronized by sd_freeze_mutex. However, this doesn't prevent writes from happening between the point of calling thaw_super() and the point where the SDF_FROZEN flag is cleared, so the following assert can trigger in log_write_header(): gfs2_assert_withdraw(sdp, !test_bit(SDF_FROZEN, &sdp->sd_flags)); Fix that by checking for sb->s_writers.frozen != SB_FREEZE_COMPLETE in log_write_header() instead. To make sure that the filesystem-specific part of freezing happens before sb->s_writers.frozen is set to SB_FREEZE_COMPLETE, move that code from gfs2_freeze_locally() into gfs2_freeze_fs() and hook that up to the .freeze_fs operation. Signed-off-by: Andreas Gruenbacher --- fs/gfs2/log.c | 3 ++- fs/gfs2/super.c | 40 ++++++++++++++++------------------------ 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 6b3ba8f7b67a..8cddf955ebc0 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -913,8 +913,9 @@ void gfs2_write_log_header(struct gfs2_sbd *sdp, struct gfs2_jdesc *jd, static void log_write_header(struct gfs2_sbd *sdp, u32 flags) { blk_opf_t op_flags = REQ_PREFLUSH | REQ_FUA | REQ_META | REQ_SYNC; + struct super_block *sb = sdp->sd_vfs; - gfs2_assert_withdraw(sdp, !test_bit(SDF_FROZEN, &sdp->sd_flags)); + gfs2_assert_withdraw(sdp, sb->s_writers.frozen != SB_FREEZE_COMPLETE); if (test_bit(SDF_NOBARRIERS, &sdp->sd_flags)) { gfs2_ordered_wait(sdp); diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c index ae92ae1203d8..e5f79466340d 100644 --- a/fs/gfs2/super.c +++ b/fs/gfs2/super.c @@ -673,28 +673,6 @@ static int gfs2_sync_fs(struct super_block *sb, int wait) return sdp->sd_log_error; } -static int gfs2_freeze_locally(struct gfs2_sbd *sdp) -{ - struct super_block *sb = sdp->sd_vfs; - int error; - - error = freeze_super(sb, FREEZE_HOLDER_USERSPACE); - if (error) - return error; - - if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) { - gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_FREEZE | - GFS2_LFC_FREEZE_GO_SYNC); - if (gfs2_withdrawing_or_withdrawn(sdp)) { - error = thaw_super(sb, FREEZE_HOLDER_USERSPACE); - if (error) - return error; - return -EIO; - } - } - return 0; -} - static int gfs2_do_thaw(struct gfs2_sbd *sdp) { struct super_block *sb = sdp->sd_vfs; @@ -724,7 +702,7 @@ void gfs2_freeze_func(struct work_struct *work) if (test_bit(SDF_FROZEN, &sdp->sd_flags)) goto freeze_failed; - error = gfs2_freeze_locally(sdp); + error = freeze_super(sb, FREEZE_HOLDER_USERSPACE); if (error) goto freeze_failed; @@ -765,7 +743,7 @@ static int gfs2_freeze_super(struct super_block *sb, enum freeze_holder who) } for (;;) { - error = gfs2_freeze_locally(sdp); + error = freeze_super(sb, FREEZE_HOLDER_USERSPACE); if (error) { fs_info(sdp, "GFS2: couldn't freeze filesystem: %d\n", error); @@ -801,6 +779,19 @@ out: return error; } +static int gfs2_freeze_fs(struct super_block *sb) +{ + struct gfs2_sbd *sdp = sb->s_fs_info; + + if (test_bit(SDF_JOURNAL_LIVE, &sdp->sd_flags)) { + gfs2_log_flush(sdp, NULL, GFS2_LOG_HEAD_FLUSH_FREEZE | + GFS2_LFC_FREEZE_GO_SYNC); + if (gfs2_withdrawing_or_withdrawn(sdp)) + return -EIO; + } + return 0; +} + /** * gfs2_thaw_super - reallow writes to the filesystem * @sb: the VFS structure for the filesystem @@ -1599,6 +1590,7 @@ const struct super_operations gfs2_super_ops = { .put_super = gfs2_put_super, .sync_fs = gfs2_sync_fs, .freeze_super = gfs2_freeze_super, + .freeze_fs = gfs2_freeze_fs, .thaw_super = gfs2_thaw_super, .statfs = gfs2_statfs, .drop_inode = gfs2_drop_inode, -- cgit