diff options
author | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2014-01-27 22:25:06 -0500 |
---|---|---|
committer | Jaegeuk Kim <jaegeuk.kim@samsung.com> | 2014-02-17 00:58:52 -0500 |
commit | abb2366c82c3d2dac3d7e9a74332137da8fc9399 (patch) | |
tree | fef0d48c75366bcc5b60ba69565756ca3890c03e | |
parent | 5e443818fa0b2a2845561ee25bec181424fb2889 (diff) |
f2fs: fix to recover xattr node block
If a new xattr node page was allocated and its inode is fsynced, we should
recover the xattr node page during the roll-forward process after power-cut.
But, previously, f2fs didn't handle that case, resulting in kernel panic as
follows reported by Tom Li.
BUG: unable to handle kernel paging request at ffffc9001c861a98
IP: [<ffffffffa0295236>] check_index_in_prev_nodes+0x86/0x2d0 [f2fs]
Call Trace:
[<ffffffff815ece9b>] ? printk+0x48/0x4a
[<ffffffffa029626a>] recover_fsync_data+0xdca/0xf50 [f2fs]
[<ffffffffa02873ae>] f2fs_fill_super+0x92e/0x970 [f2fs]
[<ffffffff8112c9f8>] mount_bdev+0x1b8/0x200
[<ffffffffa0286a80>] ? f2fs_remount+0x130/0x130 [f2fs]
[<ffffffffa0285e40>] f2fs_mount+0x10/0x20 [f2fs]
[<ffffffff8112d4de>] mount_fs+0x3e/0x1b0
[<ffffffff810ef4eb>] ? __alloc_percpu+0xb/0x10
[<ffffffff8114761f>] vfs_kern_mount+0x6f/0x120
[<ffffffff811497b9>] do_mount+0x259/0xa90
[<ffffffff810ead1d>] ? memdup_user+0x3d/0x80
[<ffffffff810eadb3>] ? strndup_user+0x53/0x70
[<ffffffff8114a2c9>] SyS_mount+0x89/0xd0
[<ffffffff815feae2>] system_call_fastpath+0x16/0x1b
This patch adds a recovery function of xattr node pages.
Reported-by: Tom Li <biergaizi@members.fsf.org>
Signed-off-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
-rw-r--r-- | fs/f2fs/f2fs.h | 1 | ||||
-rw-r--r-- | fs/f2fs/node.c | 40 | ||||
-rw-r--r-- | fs/f2fs/recovery.c | 3 |
3 files changed, 44 insertions, 0 deletions
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 3f223aa64a57..55288d2d82e6 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h | |||
@@ -1121,6 +1121,7 @@ void alloc_nid_done(struct f2fs_sb_info *, nid_t); | |||
1121 | void alloc_nid_failed(struct f2fs_sb_info *, nid_t); | 1121 | void alloc_nid_failed(struct f2fs_sb_info *, nid_t); |
1122 | void recover_node_page(struct f2fs_sb_info *, struct page *, | 1122 | void recover_node_page(struct f2fs_sb_info *, struct page *, |
1123 | struct f2fs_summary *, struct node_info *, block_t); | 1123 | struct f2fs_summary *, struct node_info *, block_t); |
1124 | bool recover_xattr_data(struct inode *, struct page *, block_t); | ||
1124 | int recover_inode_page(struct f2fs_sb_info *, struct page *); | 1125 | int recover_inode_page(struct f2fs_sb_info *, struct page *); |
1125 | int restore_node_summary(struct f2fs_sb_info *, unsigned int, | 1126 | int restore_node_summary(struct f2fs_sb_info *, unsigned int, |
1126 | struct f2fs_summary_block *); | 1127 | struct f2fs_summary_block *); |
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index b0649b76eb4f..82f4753ef418 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c | |||
@@ -1535,6 +1535,46 @@ void recover_node_page(struct f2fs_sb_info *sbi, struct page *page, | |||
1535 | clear_node_page_dirty(page); | 1535 | clear_node_page_dirty(page); |
1536 | } | 1536 | } |
1537 | 1537 | ||
1538 | bool recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) | ||
1539 | { | ||
1540 | struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); | ||
1541 | nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid; | ||
1542 | nid_t new_xnid = nid_of_node(page); | ||
1543 | struct node_info ni; | ||
1544 | |||
1545 | if (ofs_of_node(page) != XATTR_NODE_OFFSET) | ||
1546 | return false; | ||
1547 | |||
1548 | /* 1: invalidate the previous xattr nid */ | ||
1549 | if (!prev_xnid) | ||
1550 | goto recover_xnid; | ||
1551 | |||
1552 | /* Deallocate node address */ | ||
1553 | get_node_info(sbi, prev_xnid, &ni); | ||
1554 | f2fs_bug_on(ni.blk_addr == NULL_ADDR); | ||
1555 | invalidate_blocks(sbi, ni.blk_addr); | ||
1556 | dec_valid_node_count(sbi, inode); | ||
1557 | set_node_addr(sbi, &ni, NULL_ADDR); | ||
1558 | |||
1559 | recover_xnid: | ||
1560 | /* 2: allocate new xattr nid */ | ||
1561 | if (unlikely(!inc_valid_node_count(sbi, inode))) | ||
1562 | f2fs_bug_on(1); | ||
1563 | |||
1564 | remove_free_nid(NM_I(sbi), new_xnid); | ||
1565 | get_node_info(sbi, new_xnid, &ni); | ||
1566 | ni.ino = inode->i_ino; | ||
1567 | set_node_addr(sbi, &ni, NEW_ADDR); | ||
1568 | F2FS_I(inode)->i_xattr_nid = new_xnid; | ||
1569 | |||
1570 | /* 3: update xattr blkaddr */ | ||
1571 | refresh_sit_entry(sbi, NEW_ADDR, blkaddr); | ||
1572 | set_node_addr(sbi, &ni, blkaddr); | ||
1573 | |||
1574 | update_inode_page(inode); | ||
1575 | return true; | ||
1576 | } | ||
1577 | |||
1538 | int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) | 1578 | int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) |
1539 | { | 1579 | { |
1540 | struct f2fs_inode *src, *dst; | 1580 | struct f2fs_inode *src, *dst; |
diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 976a7a934db5..f1b0b8917436 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c | |||
@@ -301,6 +301,9 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, | |||
301 | if (recover_inline_data(inode, page)) | 301 | if (recover_inline_data(inode, page)) |
302 | goto out; | 302 | goto out; |
303 | 303 | ||
304 | if (recover_xattr_data(inode, page, blkaddr)) | ||
305 | goto out; | ||
306 | |||
304 | start = start_bidx_of_node(ofs_of_node(page), fi); | 307 | start = start_bidx_of_node(ofs_of_node(page), fi); |
305 | if (IS_INODE(page)) | 308 | if (IS_INODE(page)) |
306 | end = start + ADDRS_PER_INODE(fi); | 309 | end = start + ADDRS_PER_INODE(fi); |