f2fs: avoid to conduct roll-forward due to the remained garbage blocks
authorJaegeuk Kim <jaegeuk.kim@samsung.com>
Tue, 15 Apr 2014 04:57:55 +0000 (13:57 +0900)
committerJaegeuk Kim <jaegeuk.kim@samsung.com>
Wed, 7 May 2014 01:21:54 +0000 (10:21 +0900)
The f2fs always scans the next chain of direct node blocks.
But some garbage blocks are able to be remained due to no discard support or
SSR triggers.
This occasionally wreaks recovering wrong inodes that were used or BUG_ONs
due to reallocating node ids as follows.

When mount this f2fs image:
http://linuxtesting.org/downloads/f2fs_fault_image.zip
BUG_ON is triggered in f2fs driver (messages below are generated on
kernel 3.13.2; for other kernels output is similar):

kernel BUG at fs/f2fs/node.c:215!
 Call Trace:
 [<ffffffffa032ebad>] recover_inode_page+0x1fd/0x3e0 [f2fs]
 [<ffffffff811446e7>] ? __lock_page+0x67/0x70
 [<ffffffff81089990>] ? autoremove_wake_function+0x50/0x50
 [<ffffffffa0337788>] recover_fsync_data+0x1398/0x15d0 [f2fs]
 [<ffffffff812b9e5c>] ? selinux_d_instantiate+0x1c/0x20
 [<ffffffff811cb20b>] ? d_instantiate+0x5b/0x80
 [<ffffffffa0321044>] f2fs_fill_super+0xb04/0xbf0 [f2fs]
 [<ffffffff811b861e>] ? mount_bdev+0x7e/0x210
 [<ffffffff811b8769>] mount_bdev+0x1c9/0x210
 [<ffffffffa0320540>] ? validate_superblock+0x210/0x210 [f2fs]
 [<ffffffffa031cf8d>] f2fs_mount+0x1d/0x30 [f2fs]
 [<ffffffff811b9497>] mount_fs+0x47/0x1c0
 [<ffffffff81166e00>] ? __alloc_percpu+0x10/0x20
 [<ffffffff811d4032>] vfs_kern_mount+0x72/0x110
 [<ffffffff811d6763>] do_mount+0x493/0x910
 [<ffffffff811615cb>] ? strndup_user+0x5b/0x80
 [<ffffffff811d6c70>] SyS_mount+0x90/0xe0
 [<ffffffff8166f8d9>] system_call_fastpath+0x16/0x1b

Found by Linux File System Verification project (linuxtesting.org).

Reported-by: Andrey Tsyvarev <tsyvarev@ispras.ru>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
fs/f2fs/checkpoint.c
fs/f2fs/f2fs.h
fs/f2fs/segment.c

index 4aa521aa9bc3a794fbe52625e689b93140ed38a5..890e23d208a8599ce65dee6e46914ae21d26f4d0 100644 (file)
@@ -762,6 +762,12 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
        void *kaddr;
        int i;
 
+       /*
+        * This avoids to conduct wrong roll-forward operations and uses
+        * metapages, so should be called prior to sync_meta_pages below.
+        */
+       discard_next_dnode(sbi);
+
        /* Flush all the NAT/SIT pages */
        while (get_pages(sbi, F2FS_DIRTY_META))
                sync_meta_pages(sbi, META, LONG_MAX);
index 2ecac8312359062268986adfe894f6320379bdd4..2c5a5dadae65883fabbbd3d89cd74d600f9a0b8a 100644 (file)
@@ -1179,6 +1179,7 @@ int f2fs_issue_flush(struct f2fs_sb_info *);
 void invalidate_blocks(struct f2fs_sb_info *, block_t);
 void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t);
 void clear_prefree_segments(struct f2fs_sb_info *);
+void discard_next_dnode(struct f2fs_sb_info *);
 int npages_for_summary_flush(struct f2fs_sb_info *);
 void allocate_new_segments(struct f2fs_sb_info *);
 struct page *get_sum_page(struct f2fs_sb_info *, unsigned int);
index 1e264e761f71f3dd28134d2a6f07fd8b538ca701..9993f94848fc942f7be8ad77553987e427588531 100644 (file)
@@ -335,13 +335,26 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
        mutex_unlock(&dirty_i->seglist_lock);
 }
 
-static void f2fs_issue_discard(struct f2fs_sb_info *sbi,
+static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
                                block_t blkstart, block_t blklen)
 {
        sector_t start = SECTOR_FROM_BLOCK(sbi, blkstart);
        sector_t len = SECTOR_FROM_BLOCK(sbi, blklen);
-       blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0);
        trace_f2fs_issue_discard(sbi->sb, blkstart, blklen);
+       return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0);
+}
+
+void discard_next_dnode(struct f2fs_sb_info *sbi)
+{
+       struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
+       block_t blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
+
+       if (f2fs_issue_discard(sbi, blkaddr, 1)) {
+               struct page *page = grab_meta_page(sbi, blkaddr);
+               /* zero-filled page */
+               set_page_dirty(page);
+               f2fs_put_page(page, 1);
+       }
 }
 
 static void add_discard_addrs(struct f2fs_sb_info *sbi,