Commit e1da7872 authored by Chao Yu's avatar Chao Yu Committed by Jaegeuk Kim

f2fs: introduce and spread verify_blkaddr

This patch introduces verify_blkaddr to check meta/data block address
with valid range to detect bug earlier.

In addition, once we encounter an invalid blkaddr, notice user to run
fsck to fix, and let the kernel panic.
Signed-off-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 24b81dfc
...@@ -120,7 +120,7 @@ struct page *f2fs_get_tmp_page(struct f2fs_sb_info *sbi, pgoff_t index) ...@@ -120,7 +120,7 @@ struct page *f2fs_get_tmp_page(struct f2fs_sb_info *sbi, pgoff_t index)
return __get_meta_page(sbi, index, false); return __get_meta_page(sbi, index, false);
} }
bool f2fs_is_valid_meta_blkaddr(struct f2fs_sb_info *sbi, bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
block_t blkaddr, int type) block_t blkaddr, int type)
{ {
switch (type) { switch (type) {
...@@ -141,10 +141,16 @@ bool f2fs_is_valid_meta_blkaddr(struct f2fs_sb_info *sbi, ...@@ -141,10 +141,16 @@ bool f2fs_is_valid_meta_blkaddr(struct f2fs_sb_info *sbi,
return false; return false;
break; break;
case META_POR: case META_POR:
case DATA_GENERIC:
if (unlikely(blkaddr >= MAX_BLKADDR(sbi) || if (unlikely(blkaddr >= MAX_BLKADDR(sbi) ||
blkaddr < MAIN_BLKADDR(sbi))) blkaddr < MAIN_BLKADDR(sbi)))
return false; return false;
break; break;
case META_GENERIC:
if (unlikely(blkaddr < SEG0_BLKADDR(sbi) ||
blkaddr >= MAIN_BLKADDR(sbi)))
return false;
break;
default: default:
BUG(); BUG();
} }
...@@ -177,7 +183,7 @@ int f2fs_ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, ...@@ -177,7 +183,7 @@ int f2fs_ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
blk_start_plug(&plug); blk_start_plug(&plug);
for (; nrpages-- > 0; blkno++) { for (; nrpages-- > 0; blkno++) {
if (!f2fs_is_valid_meta_blkaddr(sbi, blkno, type)) if (!f2fs_is_valid_blkaddr(sbi, blkno, type))
goto out; goto out;
switch (type) { switch (type) {
......
...@@ -485,7 +485,7 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio) ...@@ -485,7 +485,7 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio)
spin_unlock(&io->io_lock); spin_unlock(&io->io_lock);
} }
if (is_valid_blkaddr(fio->old_blkaddr)) if (__is_valid_data_blkaddr(fio->old_blkaddr))
verify_block_addr(fio, fio->old_blkaddr); verify_block_addr(fio, fio->old_blkaddr);
verify_block_addr(fio, fio->new_blkaddr); verify_block_addr(fio, fio->new_blkaddr);
...@@ -1045,7 +1045,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, ...@@ -1045,7 +1045,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map,
next_block: next_block:
blkaddr = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node); blkaddr = datablock_addr(dn.inode, dn.node_page, dn.ofs_in_node);
if (!is_valid_blkaddr(blkaddr)) { if (!is_valid_data_blkaddr(sbi, blkaddr)) {
if (create) { if (create) {
if (unlikely(f2fs_cp_error(sbi))) { if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO; err = -EIO;
...@@ -1700,7 +1700,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) ...@@ -1700,7 +1700,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
f2fs_lookup_extent_cache(inode, page->index, &ei)) { f2fs_lookup_extent_cache(inode, page->index, &ei)) {
fio->old_blkaddr = ei.blk + page->index - ei.fofs; fio->old_blkaddr = ei.blk + page->index - ei.fofs;
if (is_valid_blkaddr(fio->old_blkaddr)) { if (is_valid_data_blkaddr(fio->sbi, fio->old_blkaddr)) {
ipu_force = true; ipu_force = true;
fio->need_lock = LOCK_DONE; fio->need_lock = LOCK_DONE;
goto got_it; goto got_it;
...@@ -1727,7 +1727,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio) ...@@ -1727,7 +1727,7 @@ int f2fs_do_write_data_page(struct f2fs_io_info *fio)
* If current allocation needs SSR, * If current allocation needs SSR,
* it had better in-place writes for updated data. * it had better in-place writes for updated data.
*/ */
if (ipu_force || (is_valid_blkaddr(fio->old_blkaddr) && if (ipu_force || (is_valid_data_blkaddr(fio->sbi, fio->old_blkaddr) &&
need_inplace_update(fio))) { need_inplace_update(fio))) {
err = encrypt_one_page(fio); err = encrypt_one_page(fio);
if (err) if (err)
......
...@@ -194,7 +194,7 @@ struct cp_control { ...@@ -194,7 +194,7 @@ struct cp_control {
}; };
/* /*
* For CP/NAT/SIT/SSA readahead * indicate meta/data type
*/ */
enum { enum {
META_CP, META_CP,
...@@ -202,6 +202,8 @@ enum { ...@@ -202,6 +202,8 @@ enum {
META_SIT, META_SIT,
META_SSA, META_SSA,
META_POR, META_POR,
DATA_GENERIC,
META_GENERIC,
}; };
/* for the list of ino */ /* for the list of ino */
...@@ -2666,13 +2668,36 @@ static inline void f2fs_update_iostat(struct f2fs_sb_info *sbi, ...@@ -2666,13 +2668,36 @@ static inline void f2fs_update_iostat(struct f2fs_sb_info *sbi,
spin_unlock(&sbi->iostat_lock); spin_unlock(&sbi->iostat_lock);
} }
static inline bool is_valid_blkaddr(block_t blkaddr) bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
block_t blkaddr, int type);
void f2fs_msg(struct super_block *sb, const char *level, const char *fmt, ...);
static inline void verify_blkaddr(struct f2fs_sb_info *sbi,
block_t blkaddr, int type)
{
if (!f2fs_is_valid_blkaddr(sbi, blkaddr, type)) {
f2fs_msg(sbi->sb, KERN_ERR,
"invalid blkaddr: %u, type: %d, run fsck to fix.",
blkaddr, type);
f2fs_bug_on(sbi, 1);
}
}
static inline bool __is_valid_data_blkaddr(block_t blkaddr)
{ {
if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR) if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR)
return false; return false;
return true; return true;
} }
static inline bool is_valid_data_blkaddr(struct f2fs_sb_info *sbi,
block_t blkaddr)
{
if (!__is_valid_data_blkaddr(blkaddr))
return false;
verify_blkaddr(sbi, blkaddr, DATA_GENERIC);
return true;
}
/* /*
* file.c * file.c
*/ */
...@@ -2896,8 +2921,8 @@ void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io); ...@@ -2896,8 +2921,8 @@ void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io);
struct page *f2fs_grab_meta_page(struct f2fs_sb_info *sbi, pgoff_t index); struct page *f2fs_grab_meta_page(struct f2fs_sb_info *sbi, pgoff_t index);
struct page *f2fs_get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index); struct page *f2fs_get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index);
struct page *f2fs_get_tmp_page(struct f2fs_sb_info *sbi, pgoff_t index); struct page *f2fs_get_tmp_page(struct f2fs_sb_info *sbi, pgoff_t index);
bool f2fs_is_valid_meta_blkaddr(struct f2fs_sb_info *sbi, bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
block_t blkaddr, int type); block_t blkaddr, int type);
int f2fs_ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages, int f2fs_ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
int type, bool sync); int type, bool sync);
void f2fs_ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index); void f2fs_ra_meta_pages_cond(struct f2fs_sb_info *sbi, pgoff_t index);
......
...@@ -350,13 +350,13 @@ static pgoff_t __get_first_dirty_index(struct address_space *mapping, ...@@ -350,13 +350,13 @@ static pgoff_t __get_first_dirty_index(struct address_space *mapping,
return pgofs; return pgofs;
} }
static bool __found_offset(block_t blkaddr, pgoff_t dirty, pgoff_t pgofs, static bool __found_offset(struct f2fs_sb_info *sbi, block_t blkaddr,
int whence) pgoff_t dirty, pgoff_t pgofs, int whence)
{ {
switch (whence) { switch (whence) {
case SEEK_DATA: case SEEK_DATA:
if ((blkaddr == NEW_ADDR && dirty == pgofs) || if ((blkaddr == NEW_ADDR && dirty == pgofs) ||
is_valid_blkaddr(blkaddr)) is_valid_data_blkaddr(sbi, blkaddr))
return true; return true;
break; break;
case SEEK_HOLE: case SEEK_HOLE:
...@@ -420,7 +420,8 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence) ...@@ -420,7 +420,8 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
blkaddr = datablock_addr(dn.inode, blkaddr = datablock_addr(dn.inode,
dn.node_page, dn.ofs_in_node); dn.node_page, dn.ofs_in_node);
if (__found_offset(blkaddr, dirty, pgofs, whence)) { if (__found_offset(F2FS_I_SB(inode), blkaddr, dirty,
pgofs, whence)) {
f2fs_put_dnode(&dn); f2fs_put_dnode(&dn);
goto found; goto found;
} }
......
...@@ -68,11 +68,12 @@ static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri) ...@@ -68,11 +68,12 @@ static void __get_inode_rdev(struct inode *inode, struct f2fs_inode *ri)
} }
} }
static bool __written_first_block(struct f2fs_inode *ri) static bool __written_first_block(struct f2fs_sb_info *sbi,
struct f2fs_inode *ri)
{ {
block_t addr = le32_to_cpu(ri->i_addr[offset_in_addr(ri)]); block_t addr = le32_to_cpu(ri->i_addr[offset_in_addr(ri)]);
if (is_valid_blkaddr(addr)) if (is_valid_data_blkaddr(sbi, addr))
return true; return true;
return false; return false;
} }
...@@ -282,7 +283,7 @@ static int do_read_inode(struct inode *inode) ...@@ -282,7 +283,7 @@ static int do_read_inode(struct inode *inode)
/* get rdev by using inline_info */ /* get rdev by using inline_info */
__get_inode_rdev(inode, ri); __get_inode_rdev(inode, ri);
if (__written_first_block(ri)) if (__written_first_block(sbi, ri))
set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN); set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
if (!f2fs_need_inode_block_update(sbi, inode->i_ino)) if (!f2fs_need_inode_block_update(sbi, inode->i_ino))
......
...@@ -371,7 +371,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, ...@@ -371,7 +371,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
new_blkaddr == NULL_ADDR); new_blkaddr == NULL_ADDR);
f2fs_bug_on(sbi, nat_get_blkaddr(e) == NEW_ADDR && f2fs_bug_on(sbi, nat_get_blkaddr(e) == NEW_ADDR &&
new_blkaddr == NEW_ADDR); new_blkaddr == NEW_ADDR);
f2fs_bug_on(sbi, is_valid_blkaddr(nat_get_blkaddr(e)) && f2fs_bug_on(sbi, is_valid_data_blkaddr(sbi, nat_get_blkaddr(e)) &&
new_blkaddr == NEW_ADDR); new_blkaddr == NEW_ADDR);
/* increment version no as node is removed */ /* increment version no as node is removed */
...@@ -382,7 +382,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, ...@@ -382,7 +382,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
/* change address */ /* change address */
nat_set_blkaddr(e, new_blkaddr); nat_set_blkaddr(e, new_blkaddr);
if (!is_valid_blkaddr(new_blkaddr)) if (!is_valid_data_blkaddr(sbi, new_blkaddr))
set_nat_flag(e, IS_CHECKPOINTED, false); set_nat_flag(e, IS_CHECKPOINTED, false);
__set_nat_cache_dirty(nm_i, e); __set_nat_cache_dirty(nm_i, e);
......
...@@ -252,7 +252,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head, ...@@ -252,7 +252,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
while (1) { while (1) {
struct fsync_inode_entry *entry; struct fsync_inode_entry *entry;
if (!f2fs_is_valid_meta_blkaddr(sbi, blkaddr, META_POR)) if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR))
return 0; return 0;
page = f2fs_get_tmp_page(sbi, blkaddr); page = f2fs_get_tmp_page(sbi, blkaddr);
...@@ -507,7 +507,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, ...@@ -507,7 +507,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
} }
/* dest is valid block, try to recover from src to dest */ /* dest is valid block, try to recover from src to dest */
if (f2fs_is_valid_meta_blkaddr(sbi, dest, META_POR)) { if (f2fs_is_valid_blkaddr(sbi, dest, META_POR)) {
if (src == NULL_ADDR) { if (src == NULL_ADDR) {
err = f2fs_reserve_new_block(&dn); err = f2fs_reserve_new_block(&dn);
...@@ -568,7 +568,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list, ...@@ -568,7 +568,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list,
while (1) { while (1) {
struct fsync_inode_entry *entry; struct fsync_inode_entry *entry;
if (!f2fs_is_valid_meta_blkaddr(sbi, blkaddr, META_POR)) if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR))
break; break;
f2fs_ra_meta_pages_cond(sbi, blkaddr); f2fs_ra_meta_pages_cond(sbi, blkaddr);
......
...@@ -1919,7 +1919,7 @@ bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr) ...@@ -1919,7 +1919,7 @@ bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr)
struct seg_entry *se; struct seg_entry *se;
bool is_cp = false; bool is_cp = false;
if (!is_valid_blkaddr(blkaddr)) if (!is_valid_data_blkaddr(sbi, blkaddr))
return true; return true;
down_read(&sit_i->sentry_lock); down_read(&sit_i->sentry_lock);
...@@ -2993,7 +2993,7 @@ void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr) ...@@ -2993,7 +2993,7 @@ void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr)
{ {
struct page *cpage; struct page *cpage;
if (!is_valid_blkaddr(blkaddr)) if (!is_valid_data_blkaddr(sbi, blkaddr))
return; return;
cpage = find_lock_page(META_MAPPING(sbi), blkaddr); cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
......
...@@ -85,7 +85,7 @@ ...@@ -85,7 +85,7 @@
(GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & ((sbi)->blocks_per_seg - 1)) (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & ((sbi)->blocks_per_seg - 1))
#define GET_SEGNO(sbi, blk_addr) \ #define GET_SEGNO(sbi, blk_addr) \
((!is_valid_blkaddr(blk_addr)) ? \ ((!is_valid_data_blkaddr(sbi, blk_addr)) ? \
NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \ NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \
GET_SEGNO_FROM_SEG0(sbi, blk_addr))) GET_SEGNO_FROM_SEG0(sbi, blk_addr)))
#define BLKS_PER_SEC(sbi) \ #define BLKS_PER_SEC(sbi) \
...@@ -647,11 +647,9 @@ static inline void verify_block_addr(struct f2fs_io_info *fio, block_t blk_addr) ...@@ -647,11 +647,9 @@ static inline void verify_block_addr(struct f2fs_io_info *fio, block_t blk_addr)
if (PAGE_TYPE_OF_BIO(fio->type) == META && if (PAGE_TYPE_OF_BIO(fio->type) == META &&
(!is_read_io(fio->op) || fio->is_meta)) (!is_read_io(fio->op) || fio->is_meta))
BUG_ON(blk_addr < SEG0_BLKADDR(sbi) || verify_blkaddr(sbi, blk_addr, META_GENERIC);
blk_addr >= MAIN_BLKADDR(sbi));
else else
BUG_ON(blk_addr < MAIN_BLKADDR(sbi) || verify_blkaddr(sbi, blk_addr, DATA_GENERIC);
blk_addr >= MAX_BLKADDR(sbi));
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment