aboutsummaryrefslogtreecommitdiffstats
path: root/fs/f2fs
diff options
context:
space:
mode:
authorJaegeuk Kim <jaegeuk.kim@samsung.com>2014-04-15 00:57:55 -0400
committerJaegeuk Kim <jaegeuk.kim@samsung.com>2014-05-06 21:21:54 -0400
commit1e87a78d95ecea7a989349860feb42db3e4b7db5 (patch)
tree77e8a24c621760c18ee7cf599ad93fd92744032f /fs/f2fs
parentb270ad6f0aedd27bdc689fc15f26bc650a59b12b (diff)
f2fs: avoid to conduct roll-forward due to the remained garbage blocks
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>
Diffstat (limited to 'fs/f2fs')
-rw-r--r--fs/f2fs/checkpoint.c6
-rw-r--r--fs/f2fs/f2fs.h1
-rw-r--r--fs/f2fs/segment.c17
3 files changed, 22 insertions, 2 deletions
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 4aa521aa9bc3..890e23d208a8 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -762,6 +762,12 @@ static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount)
762 void *kaddr; 762 void *kaddr;
763 int i; 763 int i;
764 764
765 /*
766 * This avoids to conduct wrong roll-forward operations and uses
767 * metapages, so should be called prior to sync_meta_pages below.
768 */
769 discard_next_dnode(sbi);
770
765 /* Flush all the NAT/SIT pages */ 771 /* Flush all the NAT/SIT pages */
766 while (get_pages(sbi, F2FS_DIRTY_META)) 772 while (get_pages(sbi, F2FS_DIRTY_META))
767 sync_meta_pages(sbi, META, LONG_MAX); 773 sync_meta_pages(sbi, META, LONG_MAX);
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
index 2ecac8312359..2c5a5dadae65 100644
--- a/fs/f2fs/f2fs.h
+++ b/fs/f2fs/f2fs.h
@@ -1179,6 +1179,7 @@ int f2fs_issue_flush(struct f2fs_sb_info *);
1179void invalidate_blocks(struct f2fs_sb_info *, block_t); 1179void invalidate_blocks(struct f2fs_sb_info *, block_t);
1180void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t); 1180void refresh_sit_entry(struct f2fs_sb_info *, block_t, block_t);
1181void clear_prefree_segments(struct f2fs_sb_info *); 1181void clear_prefree_segments(struct f2fs_sb_info *);
1182void discard_next_dnode(struct f2fs_sb_info *);
1182int npages_for_summary_flush(struct f2fs_sb_info *); 1183int npages_for_summary_flush(struct f2fs_sb_info *);
1183void allocate_new_segments(struct f2fs_sb_info *); 1184void allocate_new_segments(struct f2fs_sb_info *);
1184struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); 1185struct page *get_sum_page(struct f2fs_sb_info *, unsigned int);
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 1e264e761f71..9993f94848fc 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -335,13 +335,26 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
335 mutex_unlock(&dirty_i->seglist_lock); 335 mutex_unlock(&dirty_i->seglist_lock);
336} 336}
337 337
338static void f2fs_issue_discard(struct f2fs_sb_info *sbi, 338static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
339 block_t blkstart, block_t blklen) 339 block_t blkstart, block_t blklen)
340{ 340{
341 sector_t start = SECTOR_FROM_BLOCK(sbi, blkstart); 341 sector_t start = SECTOR_FROM_BLOCK(sbi, blkstart);
342 sector_t len = SECTOR_FROM_BLOCK(sbi, blklen); 342 sector_t len = SECTOR_FROM_BLOCK(sbi, blklen);
343 blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0);
344 trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); 343 trace_f2fs_issue_discard(sbi->sb, blkstart, blklen);
344 return blkdev_issue_discard(sbi->sb->s_bdev, start, len, GFP_NOFS, 0);
345}
346
347void discard_next_dnode(struct f2fs_sb_info *sbi)
348{
349 struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
350 block_t blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
351
352 if (f2fs_issue_discard(sbi, blkaddr, 1)) {
353 struct page *page = grab_meta_page(sbi, blkaddr);
354 /* zero-filled page */
355 set_page_dirty(page);
356 f2fs_put_page(page, 1);
357 }
345} 358}
346 359
347static void add_discard_addrs(struct f2fs_sb_info *sbi, 360static void add_discard_addrs(struct f2fs_sb_info *sbi,