diff options
| author | Chao Yu <chao2.yu@samsung.com> | 2014-05-26 20:41:07 -0400 |
|---|---|---|
| committer | Jaegeuk Kim <jaegeuk@kernel.org> | 2014-06-04 00:34:30 -0400 |
| commit | bac4eef6537a663585f3fb3d633a629c72e3b73d (patch) | |
| tree | 40f3bd22e30d61eb1a485062b94e1f3c2cd97dd5 | |
| parent | bfec07d0f8ed78b10df3ca3bc23e27de1166ea45 (diff) | |
f2fs: avoid crash when trace f2fs_submit_page_mbio event in ra_sum_pages
Previously we allocate pages with no mapping in ra_sum_pages(), so we may
encounter a crash in event trace of f2fs_submit_page_mbio where we access
mapping data of the page.
We'd better allocate pages in bd_inode mapping and invalidate these pages after
we restore data from pages. It could avoid crash in above scenario.
Changes from V1
o remove redundant code in ra_sum_pages() suggested by Jaegeuk Kim.
Call Trace:
[<f1031630>] ? ftrace_raw_event_f2fs_write_checkpoint+0x80/0x80 [f2fs]
[<f10377bb>] f2fs_submit_page_mbio+0x1cb/0x200 [f2fs]
[<f103c5da>] restore_node_summary+0x13a/0x280 [f2fs]
[<f103e22d>] build_curseg+0x2bd/0x620 [f2fs]
[<f104043b>] build_segment_manager+0x1cb/0x920 [f2fs]
[<f1032c85>] f2fs_fill_super+0x535/0x8e0 [f2fs]
[<c115b66a>] mount_bdev+0x16a/0x1a0
[<f102f63f>] f2fs_mount+0x1f/0x30 [f2fs]
[<c115c096>] mount_fs+0x36/0x170
[<c1173635>] vfs_kern_mount+0x55/0xe0
[<c1175388>] do_mount+0x1e8/0x900
[<c1175d72>] SyS_mount+0x82/0xc0
[<c16059cc>] sysenter_do_call+0x12/0x22
Suggested-by: Jaegeuk Kim <jaegeuk.kim@samsung.com>
Signed-off-by: Chao Yu <chao2.yu@samsung.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
| -rw-r--r-- | fs/f2fs/node.c | 52 |
1 files changed, 24 insertions, 28 deletions
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 3d60d3d34ed2..02a59e9027b1 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c | |||
| @@ -1658,35 +1658,29 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) | |||
| 1658 | 1658 | ||
| 1659 | /* | 1659 | /* |
| 1660 | * ra_sum_pages() merge contiguous pages into one bio and submit. | 1660 | * ra_sum_pages() merge contiguous pages into one bio and submit. |
| 1661 | * these pre-readed pages are linked in pages list. | 1661 | * these pre-readed pages are alloced in bd_inode's mapping tree. |
| 1662 | */ | 1662 | */ |
| 1663 | static int ra_sum_pages(struct f2fs_sb_info *sbi, struct list_head *pages, | 1663 | static int ra_sum_pages(struct f2fs_sb_info *sbi, struct page **pages, |
| 1664 | int start, int nrpages) | 1664 | int start, int nrpages) |
| 1665 | { | 1665 | { |
| 1666 | struct page *page; | 1666 | struct inode *inode = sbi->sb->s_bdev->bd_inode; |
| 1667 | int page_idx = start; | 1667 | struct address_space *mapping = inode->i_mapping; |
| 1668 | int i, page_idx = start; | ||
| 1668 | struct f2fs_io_info fio = { | 1669 | struct f2fs_io_info fio = { |
| 1669 | .type = META, | 1670 | .type = META, |
| 1670 | .rw = READ_SYNC | REQ_META | REQ_PRIO | 1671 | .rw = READ_SYNC | REQ_META | REQ_PRIO |
| 1671 | }; | 1672 | }; |
| 1672 | 1673 | ||
| 1673 | for (; page_idx < start + nrpages; page_idx++) { | 1674 | for (i = 0; page_idx < start + nrpages; page_idx++, i++) { |
| 1674 | /* alloc temporal page for read node summary info*/ | 1675 | /* alloc page in bd_inode for reading node summary info */ |
| 1675 | page = alloc_page(GFP_F2FS_ZERO); | 1676 | pages[i] = grab_cache_page(mapping, page_idx); |
| 1676 | if (!page) | 1677 | if (!pages[i]) |
| 1677 | break; | 1678 | break; |
| 1678 | 1679 | f2fs_submit_page_mbio(sbi, pages[i], page_idx, &fio); | |
| 1679 | lock_page(page); | ||
| 1680 | page->index = page_idx; | ||
| 1681 | list_add_tail(&page->lru, pages); | ||
| 1682 | } | 1680 | } |
| 1683 | 1681 | ||
| 1684 | list_for_each_entry(page, pages, lru) | ||
| 1685 | f2fs_submit_page_mbio(sbi, page, page->index, &fio); | ||
| 1686 | |||
| 1687 | f2fs_submit_merged_bio(sbi, META, READ); | 1682 | f2fs_submit_merged_bio(sbi, META, READ); |
| 1688 | 1683 | return i; | |
| 1689 | return page_idx - start; | ||
| 1690 | } | 1684 | } |
| 1691 | 1685 | ||
| 1692 | int restore_node_summary(struct f2fs_sb_info *sbi, | 1686 | int restore_node_summary(struct f2fs_sb_info *sbi, |
| @@ -1694,11 +1688,11 @@ int restore_node_summary(struct f2fs_sb_info *sbi, | |||
| 1694 | { | 1688 | { |
| 1695 | struct f2fs_node *rn; | 1689 | struct f2fs_node *rn; |
| 1696 | struct f2fs_summary *sum_entry; | 1690 | struct f2fs_summary *sum_entry; |
| 1697 | struct page *page, *tmp; | 1691 | struct inode *inode = sbi->sb->s_bdev->bd_inode; |
| 1698 | block_t addr; | 1692 | block_t addr; |
| 1699 | int bio_blocks = MAX_BIO_BLOCKS(max_hw_blocks(sbi)); | 1693 | int bio_blocks = MAX_BIO_BLOCKS(max_hw_blocks(sbi)); |
| 1700 | int i, last_offset, nrpages, err = 0; | 1694 | struct page *pages[bio_blocks]; |
| 1701 | LIST_HEAD(page_list); | 1695 | int i, idx, last_offset, nrpages, err = 0; |
| 1702 | 1696 | ||
| 1703 | /* scan the node segment */ | 1697 | /* scan the node segment */ |
| 1704 | last_offset = sbi->blocks_per_seg; | 1698 | last_offset = sbi->blocks_per_seg; |
| @@ -1709,29 +1703,31 @@ int restore_node_summary(struct f2fs_sb_info *sbi, | |||
| 1709 | nrpages = min(last_offset - i, bio_blocks); | 1703 | nrpages = min(last_offset - i, bio_blocks); |
| 1710 | 1704 | ||
| 1711 | /* read ahead node pages */ | 1705 | /* read ahead node pages */ |
| 1712 | nrpages = ra_sum_pages(sbi, &page_list, addr, nrpages); | 1706 | nrpages = ra_sum_pages(sbi, pages, addr, nrpages); |
| 1713 | if (!nrpages) | 1707 | if (!nrpages) |
| 1714 | return -ENOMEM; | 1708 | return -ENOMEM; |
| 1715 | 1709 | ||
| 1716 | list_for_each_entry_safe(page, tmp, &page_list, lru) { | 1710 | for (idx = 0; idx < nrpages; idx++) { |
| 1717 | if (err) | 1711 | if (err) |
| 1718 | goto skip; | 1712 | goto skip; |
| 1719 | 1713 | ||
| 1720 | lock_page(page); | 1714 | lock_page(pages[idx]); |
| 1721 | if (unlikely(!PageUptodate(page))) { | 1715 | if (unlikely(!PageUptodate(pages[idx]))) { |
| 1722 | err = -EIO; | 1716 | err = -EIO; |
| 1723 | } else { | 1717 | } else { |
| 1724 | rn = F2FS_NODE(page); | 1718 | rn = F2FS_NODE(pages[idx]); |
| 1725 | sum_entry->nid = rn->footer.nid; | 1719 | sum_entry->nid = rn->footer.nid; |
| 1726 | sum_entry->version = 0; | 1720 | sum_entry->version = 0; |
| 1727 | sum_entry->ofs_in_node = 0; | 1721 | sum_entry->ofs_in_node = 0; |
| 1728 | sum_entry++; | 1722 | sum_entry++; |
| 1729 | } | 1723 | } |
| 1730 | unlock_page(page); | 1724 | unlock_page(pages[idx]); |
| 1731 | skip: | 1725 | skip: |
| 1732 | list_del(&page->lru); | 1726 | page_cache_release(pages[idx]); |
| 1733 | __free_pages(page, 0); | ||
| 1734 | } | 1727 | } |
| 1728 | |||
| 1729 | invalidate_mapping_pages(inode->i_mapping, addr, | ||
| 1730 | addr + nrpages); | ||
| 1735 | } | 1731 | } |
| 1736 | return err; | 1732 | return err; |
| 1737 | } | 1733 | } |
