diff options
author | Chao Yu <yuchao0@huawei.com> | 2019-07-17 09:28:51 +0800 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2019-08-20 11:23:58 -0700 |
commit | 81bad9d11ea5f728cfc815a3060583171c2b4d7d (patch) | |
tree | 3167d8ff795677afaac79691a8832dd516be04a2 | |
parent | 7c8df9e026483da8e783e1d5bd1988ab9109ba10 (diff) | |
download | f2fs-tools-81bad9d11ea5f728cfc815a3060583171c2b4d7d.tar.gz |
fsck.f2fs: write checkpoint with OPU mode
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>
-rw-r--r-- | fsck/f2fs.h | 2 | ||||
-rw-r--r-- | fsck/fsck.c | 15 | ||||
-rw-r--r-- | fsck/fsck.h | 2 | ||||
-rw-r--r-- | fsck/mount.c | 53 |
4 files changed, 69 insertions, 3 deletions
diff --git a/fsck/f2fs.h b/fsck/f2fs.h index 4dc6698..52e68ec 100644 --- a/fsck/f2fs.h +++ b/fsck/f2fs.h @@ -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) diff --git a/fsck/fsck.c b/fsck/fsck.c index d7b9ab7..b4431de 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -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; diff --git a/fsck/fsck.h b/fsck/fsck.h index d38e8de..3699b35 100644 --- a/fsck/fsck.h +++ b/fsck/fsck.h @@ -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); diff --git a/fsck/mount.c b/fsck/mount.c index 60e0e4a..973c82f 100644 --- a/fsck/mount.c +++ b/fsck/mount.c @@ -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); |