fsck.f2fs: LFS alloc_type must have free segment after blkoff
authorJaegeuk Kim <jaegeuk@kernel.org>
Thu, 10 Dec 2015 04:30:41 +0000 (20:30 -0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Fri, 11 Dec 2015 23:57:51 +0000 (15:57 -0800)
This patch checks alloc_type of current segment type.
If it is LFS, the last of segment should have no valid block.

Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fsck/f2fs.h
fsck/fsck.c
fsck/fsck.h
fsck/mount.c

index 100854e..03a0646 100644 (file)
@@ -354,6 +354,22 @@ static inline bool IS_VALID_BLK_ADDR(struct f2fs_sb_info *sbi, u32 addr)
        return 1;
 }
 
+static inline int IS_CUR_SEGNO(struct f2fs_sb_info *sbi, u32 segno, int type)
+{
+       int i;
+
+       for (i = 0; i < NO_CHECK_TYPE; i++) {
+               struct curseg_info *curseg = CURSEG_I(sbi, i);
+
+               if (type == i)
+                       continue;
+
+               if (segno == curseg->segno)
+                       return 1;
+       }
+       return 0;
+}
+
 static inline u64 BLKOFF_FROM_MAIN(struct f2fs_sb_info *sbi, u64 blk_addr)
 {
        ASSERT(blk_addr >= SM_I(sbi)->main_blkaddr);
index adc61d2..1b55c30 100644 (file)
@@ -1368,13 +1368,24 @@ int check_curseg_offset(struct f2fs_sb_info *sbi)
        for (i = 0; i < NO_CHECK_TYPE; i++) {
                struct curseg_info *curseg = CURSEG_I(sbi, i);
                struct seg_entry *se;
+               int j, nblocks;
 
                se = get_seg_entry(sbi, curseg->segno);
                if (f2fs_test_bit(curseg->next_blkoff,
-                               (const char *)se->cur_valid_map) == 1) {
+                                       (const char *)se->cur_valid_map)) {
                        ASSERT_MSG("Next block offset is not free, type:%d", i);
                        return -EINVAL;
                }
+               if (curseg->alloc_type == SSR)
+                       return 0;
+
+               nblocks = sbi->blocks_per_seg;
+               for (j = curseg->next_blkoff + 1; j < nblocks; j++) {
+                       if (f2fs_test_bit(j, (const char *)se->cur_valid_map)) {
+                               ASSERT_MSG("LFS must have free section:%d", i);
+                               return -EINVAL;
+                       }
+               }
        }
        return 0;
 }
@@ -1528,6 +1539,8 @@ int fsck_verify(struct f2fs_sb_info *sbi)
        if (force || (config.bug_on && config.fix_on && !config.ro)) {
                fix_hard_links(sbi);
                fix_nat_entries(sbi);
+               move_curseg_info(sbi, SM_I(sbi)->main_blkaddr);
+               write_curseg_info(sbi);
                rewrite_sit_area_bitmap(sbi);
                fix_checkpoint(sbi);
        }
index f6b8c53..4876914 100644 (file)
@@ -132,6 +132,10 @@ extern void fsck_free(struct f2fs_sb_info *);
 extern int f2fs_do_mount(struct f2fs_sb_info *);
 extern void f2fs_do_umount(struct f2fs_sb_info *);
 
+extern void move_curseg_info(struct f2fs_sb_info *, u64);
+extern void write_curseg_info(struct f2fs_sb_info *);
+extern int find_next_free_block(struct f2fs_sb_info *, u64 *, int, int);
+
 extern void print_raw_sb_info(struct f2fs_super_block *);
 
 /* dump.c */
index 99e2439..970b159 100644 (file)
@@ -1133,6 +1133,103 @@ void rewrite_sit_area_bitmap(struct f2fs_sb_info *sbi)
        }
 }
 
+int find_next_free_block(struct f2fs_sb_info *sbi, u64 *to, int left, int type)
+{
+       struct seg_entry *se;
+       u32 segno;
+       u64 offset;
+
+       while (*to >= SM_I(sbi)->main_blkaddr &&
+                       *to < F2FS_RAW_SUPER(sbi)->block_count) {
+               segno = GET_SEGNO(sbi, *to);
+               offset = OFFSET_IN_SEG(sbi, *to);
+
+               se = get_seg_entry(sbi, segno);
+
+               if (se->valid_blocks == sbi->blocks_per_seg)
+                       goto next;
+
+               if (se->valid_blocks == 0 && !(segno % sbi->segs_per_sec) &&
+                                       !IS_CUR_SEGNO(sbi, segno, type)) {
+                       struct seg_entry *se2;
+                       int i;
+
+                       for (i = 0; i < sbi->segs_per_sec; i++) {
+                               se2 = get_seg_entry(sbi, segno + i);
+                               if (se2->valid_blocks)
+                                       break;
+                       }
+                       if (i == sbi->segs_per_sec)
+                               return 0;
+               }
+
+               if (se->type == type &&
+                       !f2fs_test_bit(offset, (const char *)se->cur_valid_map))
+                       return 0;
+next:
+               *to = left ? *to - 1: *to + 1;
+       }
+       return -1;
+}
+
+void move_curseg_info(struct f2fs_sb_info *sbi, u64 from)
+{
+       int i, ret;
+
+       /* update summary blocks having nullified journal entries */
+       for (i = 0; i < NO_CHECK_TYPE; i++) {
+               struct curseg_info *curseg = CURSEG_I(sbi, i);
+               u32 old_segno;
+               u64 ssa_blk, to;
+
+               /* update original SSA too */
+               ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+               ret = dev_write_block(curseg->sum_blk, ssa_blk);
+               ASSERT(ret >= 0);
+
+               to = from;
+               ret = find_next_free_block(sbi, &to, 0, i);
+               ASSERT(ret == 0);
+
+               old_segno = curseg->segno;
+               curseg->segno = GET_SEGNO(sbi, to);
+               curseg->next_blkoff = OFFSET_IN_SEG(sbi, to);
+               curseg->alloc_type = SSR;
+
+               /* update new segno */
+               ssa_blk = GET_SUM_BLKADDR(sbi, curseg->segno);
+               ret = dev_read_block(curseg->sum_blk, ssa_blk);
+               ASSERT(ret >= 0);
+
+               /* update se->types */
+               reset_curseg(sbi, i);
+
+               DBG(0, "Move curseg[%d] %x -> %x after %"PRIx64"\n",
+                               i, old_segno, curseg->segno, from);
+       }
+}
+
+void write_curseg_info(struct f2fs_sb_info *sbi)
+{
+       struct f2fs_checkpoint *cp = F2FS_CKPT(sbi);
+       int i;
+
+       for (i = 0; i < NO_CHECK_TYPE; i++) {
+               cp->alloc_type[i] = CURSEG_I(sbi, i)->alloc_type;
+               if (i < CURSEG_HOT_NODE) {
+                       set_cp(cur_data_segno[i], CURSEG_I(sbi, i)->segno);
+                       set_cp(cur_data_blkoff[i],
+                                       CURSEG_I(sbi, i)->next_blkoff);
+               } else {
+                       int n = i - CURSEG_HOT_NODE;
+
+                       set_cp(cur_node_segno[n], CURSEG_I(sbi, i)->segno);
+                       set_cp(cur_node_blkoff[n],
+                                       CURSEG_I(sbi, i)->next_blkoff);
+               }
+       }
+}
+
 int lookup_nat_in_journal(struct f2fs_sb_info *sbi, u32 nid,
                                        struct f2fs_nat_entry *raw_nat)
 {