fsck.f2fs: write checkpoint with OPU mode
authorChao Yu <yuchao0@huawei.com>
Wed, 17 Jul 2019 01:28:51 +0000 (09:28 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Tue, 20 Aug 2019 18:23:58 +0000 (11:23 -0700)
This original patch was from Weichao Guo.

We may encounter both checkpoints invalid in such a case:
1. kernel writes CP A;
2. power-cut when kernel writes CP B, then CP B is corrupted;
3. fsck: load CP A, fix meta/data;
4. power-cut when fsck writes CP A in-place, then CP A is corrupted too;

To avoid both checkpoints being invalid, this patch changes to duplicate
valid checkpoint to mirror position first, and then, write fixed checkpoint
to CP #0 position.

This can make sure that, while fsck repairing, even there is sudden
power-cut, last valid checkpoint can be kept in CP #1 position.

Signed-off-by: Weichao Guo <guoweichao@huawei.com>
Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fsck/f2fs.h
fsck/fsck.c
fsck/fsck.h
fsck/mount.c

index 4dc6698..52e68ec 100644 (file)
@@ -195,6 +195,8 @@ struct f2fs_sb_info {
 
        unsigned int cur_victim_sec;            /* current victim section num */
        u32 free_segments;
+
+       int cp_backuped;                        /* backup valid checkpoint */
 };
 
 static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi)
index d7b9ab7..b4431de 100644 (file)
@@ -2127,6 +2127,16 @@ static void fix_checkpoint(struct f2fs_sb_info *sbi)
        ASSERT(ret >= 0);
 }
 
+static void fix_checkpoints(struct f2fs_sb_info *sbi)
+{
+       /* copy valid checkpoint to its mirror position */
+       duplicate_checkpoint(sbi);
+
+       /* repair checkpoint at CP #0 position */
+       sbi->cur_cp = 1;
+       fix_checkpoint(sbi);
+}
+
 int check_curseg_offset(struct f2fs_sb_info *sbi, int type)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, type);
@@ -2765,6 +2775,7 @@ int fsck_verify(struct f2fs_sb_info *sbi)
                }
        }
 #endif
+
        /* fix global metadata */
        if (force || (c.fix_on && f2fs_dev_is_writable())) {
                struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
@@ -2777,10 +2788,10 @@ int fsck_verify(struct f2fs_sb_info *sbi)
                        rewrite_sit_area_bitmap(sbi);
                        fix_curseg_info(sbi);
                        fix_checksum(sbi);
-                       fix_checkpoint(sbi);
+                       fix_checkpoints(sbi);
                } else if (is_set_ckpt_flags(cp, CP_FSCK_FLAG) ||
                        is_set_ckpt_flags(cp, CP_QUOTA_NEED_FSCK_FLAG)) {
-                       write_checkpoint(sbi);
+                       write_checkpoints(sbi);
                }
        }
        return ret;
index d38e8de..3699b35 100644 (file)
@@ -191,7 +191,9 @@ extern void flush_sit_entries(struct f2fs_sb_info *);
 extern void move_curseg_info(struct f2fs_sb_info *, u64, int);
 extern void write_curseg_info(struct f2fs_sb_info *);
 extern int find_next_free_block(struct f2fs_sb_info *, u64 *, int, int);
+extern void duplicate_checkpoint(struct f2fs_sb_info *);
 extern void write_checkpoint(struct f2fs_sb_info *);
+extern void write_checkpoints(struct f2fs_sb_info *);
 extern void update_superblock(struct f2fs_super_block *, int);
 extern void update_data_blkaddr(struct f2fs_sb_info *, nid_t, u16, block_t);
 extern void update_nat_blkaddr(struct f2fs_sb_info *, nid_t, nid_t, block_t);
index 60e0e4a..973c82f 100644 (file)
@@ -2229,7 +2229,7 @@ void flush_journal_entries(struct f2fs_sb_info *sbi)
        int n_sits = flush_sit_journal_entries(sbi);
 
        if (n_nats || n_sits)
-               write_checkpoint(sbi);
+               write_checkpoints(sbi);
 }
 
 void flush_sit_entries(struct f2fs_sb_info *sbi)
@@ -2478,6 +2478,47 @@ void nullify_nat_entry(struct f2fs_sb_info *sbi, u32 nid)
        free(nat_block);
 }
 
+void duplicate_checkpoint(struct f2fs_sb_info *sbi)
+{
+       struct f2fs_super_block *sb = F2FS_RAW_SUPER(sbi);
+       unsigned long long dst, src;
+       void *buf;
+       unsigned int seg_size = 1 << get_sb(log_blocks_per_seg);
+       int ret;
+
+       if (sbi->cp_backuped)
+               return;
+
+       buf = malloc(F2FS_BLKSIZE * seg_size);
+       ASSERT(buf);
+
+       if (sbi->cur_cp == 1) {
+               src = get_sb(cp_blkaddr);
+               dst = src + seg_size;
+       } else {
+               dst = get_sb(cp_blkaddr);
+               src = dst + seg_size;
+       }
+
+       ret = dev_read(buf, src << F2FS_BLKSIZE_BITS,
+                               seg_size << F2FS_BLKSIZE_BITS);
+       ASSERT(ret >= 0);
+
+       ret = dev_write(buf, dst << F2FS_BLKSIZE_BITS,
+                               seg_size << F2FS_BLKSIZE_BITS);
+       ASSERT(ret >= 0);
+
+       free(buf);
+
+       ret = f2fs_fsync_device();
+       ASSERT(ret >= 0);
+
+       sbi->cp_backuped = 1;
+
+       MSG(0, "Info: Duplicate valid checkpoint to mirror position "
+               "%llu -> %llu\n", src, dst);
+}
+
 void write_checkpoint(struct f2fs_sb_info *sbi)
 {
        struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
@@ -2557,6 +2598,16 @@ void write_checkpoint(struct f2fs_sb_info *sbi)
        ASSERT(ret >= 0);
 }
 
+void write_checkpoints(struct f2fs_sb_info *sbi)
+{
+       /* copy valid checkpoint to its mirror position */
+       duplicate_checkpoint(sbi);
+
+       /* repair checkpoint at CP #0 position */
+       sbi->cur_cp = 1;
+       write_checkpoint(sbi);
+}
+
 void build_nat_area_bitmap(struct f2fs_sb_info *sbi)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);