f2fs: introduce and spread verify_blkaddr
authorChao Yu <yuchao0@huawei.com>
Tue, 5 Jun 2018 09:44:11 +0000 (17:44 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 8 Dec 2018 12:05:13 +0000 (13:05 +0100)
commit e1da7872f6eda977bd812346bf588c35e4495a1e upstream.

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: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
[bwh: Backported to 4.9:
 - I skipped an earlier renaming of is_valid_meta_blkaddr() to
   f2fs_is_valid_meta_blkaddr()
 - Adjust context]
Signed-off-by: Ben Hutchings <ben.hutchings@codethink.co.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/f2fs/checkpoint.c
fs/f2fs/data.c
fs/f2fs/f2fs.h
fs/f2fs/file.c
fs/f2fs/inode.c
fs/f2fs/node.c
fs/f2fs/recovery.c
fs/f2fs/segment.c
fs/f2fs/segment.h

index def27a287b28fb8c6efebab8228e2ed5f01790be..fc2fb1b817cdeda058fb1a5880c78b93ae3d0eff 100644 (file)
@@ -118,7 +118,8 @@ struct page *get_tmp_page(struct f2fs_sb_info *sbi, pgoff_t index)
        return __get_meta_page(sbi, index, false);
 }
 
-bool is_valid_meta_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type)
+bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
+                                       block_t blkaddr, int type)
 {
        switch (type) {
        case META_NAT:
@@ -138,10 +139,16 @@ bool is_valid_meta_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type)
                        return false;
                break;
        case META_POR:
+       case DATA_GENERIC:
                if (unlikely(blkaddr >= MAX_BLKADDR(sbi) ||
                        blkaddr < MAIN_BLKADDR(sbi)))
                        return false;
                break;
+       case META_GENERIC:
+               if (unlikely(blkaddr < SEG0_BLKADDR(sbi) ||
+                       blkaddr >= MAIN_BLKADDR(sbi)))
+                       return false;
+               break;
        default:
                BUG();
        }
@@ -173,7 +180,7 @@ int ra_meta_pages(struct f2fs_sb_info *sbi, block_t start, int nrpages,
        blk_start_plug(&plug);
        for (; nrpages-- > 0; blkno++) {
 
-               if (!is_valid_meta_blkaddr(sbi, blkno, type))
+               if (!f2fs_is_valid_blkaddr(sbi, blkno, type))
                        goto out;
 
                switch (type) {
index 44128c94542cdf72b4b24decb6f6e7f969de3796..68c67cce4a25dc8989d76cd86eab92f70303d67e 100644 (file)
@@ -267,7 +267,7 @@ void f2fs_submit_page_mbio(struct f2fs_io_info *fio)
 
        io = is_read ? &sbi->read_io : &sbi->write_io[btype];
 
-       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->new_blkaddr);
 
@@ -723,7 +723,7 @@ next_dnode:
 next_block:
        blkaddr = datablock_addr(dn.node_page, dn.ofs_in_node);
 
-       if (!is_valid_blkaddr(blkaddr)) {
+       if (!is_valid_data_blkaddr(sbi, blkaddr)) {
                if (create) {
                        if (unlikely(f2fs_cp_error(sbi))) {
                                err = -EIO;
@@ -1217,7 +1217,7 @@ retry_encrypt:
         * If current allocation needs SSR,
         * it had better in-place writes for updated data.
         */
-       if (unlikely(is_valid_blkaddr(fio->old_blkaddr) &&
+       if (unlikely(is_valid_data_blkaddr(fio->sbi, fio->old_blkaddr) &&
                        !is_cold_data(page) &&
                        !IS_ATOMIC_WRITTEN_PAGE(page) &&
                        need_inplace_update(inode))) {
index c9fed363fa8ba354453b89a569c77aa0ac04f47d..133c90d1d5606abe75f5fb02ef93a365f40b07c7 100644 (file)
@@ -145,7 +145,7 @@ struct cp_control {
 };
 
 /*
- * For CP/NAT/SIT/SSA readahead
+ * indicate meta/data type
  */
 enum {
        META_CP,
@@ -153,6 +153,8 @@ enum {
        META_SIT,
        META_SSA,
        META_POR,
+       DATA_GENERIC,
+       META_GENERIC,
 };
 
 /* for the list of ino */
@@ -1930,13 +1932,36 @@ static inline void *f2fs_kvzalloc(size_t size, gfp_t flags)
        (pgofs - ADDRS_PER_INODE(inode) + ADDRS_PER_BLOCK) /    \
        ADDRS_PER_BLOCK * ADDRS_PER_BLOCK + ADDRS_PER_INODE(inode))
 
-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)
                return false;
        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
  */
@@ -2122,7 +2147,8 @@ void f2fs_stop_checkpoint(struct f2fs_sb_info *, bool);
 struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t);
 struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t);
 struct page *get_tmp_page(struct f2fs_sb_info *, pgoff_t);
-bool is_valid_meta_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr, int type);
+bool f2fs_is_valid_blkaddr(struct f2fs_sb_info *sbi,
+                                       block_t blkaddr, int type);
 int ra_meta_pages(struct f2fs_sb_info *, block_t, int, int, bool);
 void ra_meta_pages_cond(struct f2fs_sb_info *, pgoff_t);
 long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long);
index ef3b5f58d9d020ed37e73f125f3a8bd42a5dd872..b9111c6d3c9dc58132fe59718c8b4ea0506f11aa 100644 (file)
@@ -310,13 +310,13 @@ static pgoff_t __get_first_dirty_index(struct address_space *mapping,
        return pgofs;
 }
 
-static bool __found_offset(block_t blkaddr, pgoff_t dirty, pgoff_t pgofs,
-                                                       int whence)
+static bool __found_offset(struct f2fs_sb_info *sbi, block_t blkaddr,
+                               pgoff_t dirty, pgoff_t pgofs, int whence)
 {
        switch (whence) {
        case SEEK_DATA:
                if ((blkaddr == NEW_ADDR && dirty == pgofs) ||
-                       is_valid_blkaddr(blkaddr))
+                       is_valid_data_blkaddr(sbi, blkaddr))
                        return true;
                break;
        case SEEK_HOLE:
@@ -378,7 +378,8 @@ static loff_t f2fs_seek_block(struct file *file, loff_t offset, int whence)
                        block_t blkaddr;
                        blkaddr = datablock_addr(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);
                                goto found;
                        }
index 7f57c1e51267052abc4b88623b9fd1d03369a386..5b6476824f9038b00c316b466c735e8e70363ea8 100644 (file)
@@ -59,11 +59,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[0]);
 
-       if (is_valid_blkaddr(addr))
+       if (is_valid_data_blkaddr(sbi, addr))
                return true;
        return false;
 }
@@ -159,7 +160,7 @@ static int do_read_inode(struct inode *inode)
        /* get rdev by using inline_info */
        __get_inode_rdev(inode, ri);
 
-       if (__written_first_block(ri))
+       if (__written_first_block(sbi, ri))
                set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
 
        if (!need_inode_block_update(sbi, inode->i_ino))
index d4d9465e2e051d1c8c35d2206c3d0d2a879df7e9..9ce84859606b2666eb4ba4420d9cce39efcb34b0 100644 (file)
@@ -304,7 +304,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
                        new_blkaddr == NULL_ADDR);
        f2fs_bug_on(sbi, nat_get_blkaddr(e) == 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);
 
        /* increment version no as node is removed */
@@ -319,7 +319,7 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,
 
        /* change address */
        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_cache_dirty(nm_i, e);
 
index 9c542d625b5ba63e480d95ef87580ccc59d5ebb5..ab4cbb4be42316a63d2d3184cb1475c513ba9d64 100644 (file)
@@ -236,7 +236,7 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head)
        while (1) {
                struct fsync_inode_entry *entry;
 
-               if (!is_valid_meta_blkaddr(sbi, blkaddr, META_POR))
+               if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR))
                        return 0;
 
                page = get_tmp_page(sbi, blkaddr);
@@ -468,7 +468,7 @@ retry_dn:
                }
 
                /* dest is valid block, try to recover from src to dest */
-               if (is_valid_meta_blkaddr(sbi, dest, META_POR)) {
+               if (f2fs_is_valid_blkaddr(sbi, dest, META_POR)) {
 
                        if (src == NULL_ADDR) {
                                err = reserve_new_block(&dn);
@@ -527,7 +527,7 @@ static int recover_data(struct f2fs_sb_info *sbi, struct list_head *inode_list,
        while (1) {
                struct fsync_inode_entry *entry;
 
-               if (!is_valid_meta_blkaddr(sbi, blkaddr, META_POR))
+               if (!f2fs_is_valid_blkaddr(sbi, blkaddr, META_POR))
                        break;
 
                ra_meta_pages_cond(sbi, blkaddr);
index 45e244e8d919e34cb3f480b225426b647bbd3c63..2fb99a081de8f0aa3fd1f1c1bb04b751545ad126 100644 (file)
@@ -944,7 +944,7 @@ bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr)
        struct seg_entry *se;
        bool is_cp = false;
 
-       if (!is_valid_blkaddr(blkaddr))
+       if (!is_valid_data_blkaddr(sbi, blkaddr))
                return true;
 
        mutex_lock(&sit_i->sentry_lock);
@@ -1668,7 +1668,7 @@ void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi,
 {
        struct page *cpage;
 
-       if (!is_valid_blkaddr(blkaddr))
+       if (!is_valid_data_blkaddr(sbi, blkaddr))
                return;
 
        cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
index d0be0131f1bdefdf398e7caf3cd5ab7d0e4b612f..970f017645f25b3a5fa5b4fd3dbcc5af79c14b3b 100644 (file)
@@ -81,7 +81,7 @@
        (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) & (sbi->blocks_per_seg - 1))
 
 #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),                 \
                GET_SEGNO_FROM_SEG0(sbi, blk_addr)))
 #define GET_SECNO(sbi, segno)                                  \
@@ -603,11 +603,9 @@ static inline void verify_block_addr(struct f2fs_io_info *fio, block_t blk_addr)
 
        if (PAGE_TYPE_OF_BIO(fio->type) == META &&
                                (!is_read_io(fio->op) || fio->is_meta))
-               BUG_ON(blk_addr < SEG0_BLKADDR(sbi) ||
-                               blk_addr >= MAIN_BLKADDR(sbi));
+               verify_blkaddr(sbi, blk_addr, META_GENERIC);
        else
-               BUG_ON(blk_addr < MAIN_BLKADDR(sbi) ||
-                               blk_addr >= MAX_BLKADDR(sbi));
+               verify_blkaddr(sbi, blk_addr, DATA_GENERIC);
 }
 
 /*