f2fs: let discard thread wait a little longer if dev is busy
[linux-2.6-block.git] / fs / f2fs / segment.c
index 5854cc4e1d67c01045f41b99403aaaefbfc27894..d61f5ca661ce5fcce6d1276181fc825620a6c074 100644 (file)
@@ -177,7 +177,7 @@ bool need_SSR(struct f2fs_sb_info *sbi)
 
        if (test_opt(sbi, LFS))
                return false;
-       if (sbi->gc_thread && sbi->gc_thread->gc_urgent)
+       if (sbi->gc_mode == GC_URGENT)
                return true;
 
        return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs +
@@ -230,6 +230,8 @@ static int __revoke_inmem_pages(struct inode *inode,
 
                lock_page(page);
 
+               f2fs_wait_on_page_writeback(page, DATA, true);
+
                if (recover) {
                        struct dnode_of_data dn;
                        struct node_info ni;
@@ -271,7 +273,7 @@ next:
        return err;
 }
 
-void drop_inmem_pages_all(struct f2fs_sb_info *sbi)
+void drop_inmem_pages_all(struct f2fs_sb_info *sbi, bool gc_failure)
 {
        struct list_head *head = &sbi->inode_list[ATOMIC_FILE];
        struct inode *inode;
@@ -287,9 +289,17 @@ next:
        spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
 
        if (inode) {
+               if (gc_failure) {
+                       if (fi->i_gc_failures[GC_FAILURE_ATOMIC])
+                               goto drop;
+                       goto skip;
+               }
+drop:
+               set_inode_flag(inode, FI_ATOMIC_REVOKE_REQUEST);
                drop_inmem_pages(inode);
                iput(inode);
        }
+skip:
        congestion_wait(BLK_RW_ASYNC, HZ/50);
        cond_resched();
        goto next;
@@ -309,7 +319,7 @@ void drop_inmem_pages(struct inode *inode)
        mutex_unlock(&fi->inmem_lock);
 
        clear_inode_flag(inode, FI_ATOMIC_FILE);
-       clear_inode_flag(inode, FI_HOT_DATA);
+       fi->i_gc_failures[GC_FAILURE_ATOMIC] = 0;
        stat_dec_atomic_write(inode);
 }
 
@@ -328,7 +338,7 @@ void drop_inmem_page(struct inode *inode, struct page *page)
                        break;
        }
 
-       f2fs_bug_on(sbi, !cur || cur->page != page);
+       f2fs_bug_on(sbi, list_empty(head) || cur->page != page);
        list_del(&cur->list);
        mutex_unlock(&fi->inmem_lock);
 
@@ -343,8 +353,7 @@ void drop_inmem_page(struct inode *inode, struct page *page)
        trace_f2fs_commit_inmem_page(page, INMEM_INVALIDATE);
 }
 
-static int __commit_inmem_pages(struct inode *inode,
-                                       struct list_head *revoke_list)
+static int __commit_inmem_pages(struct inode *inode)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct f2fs_inode_info *fi = F2FS_I(inode);
@@ -357,9 +366,12 @@ static int __commit_inmem_pages(struct inode *inode,
                .op_flags = REQ_SYNC | REQ_PRIO,
                .io_type = FS_DATA_IO,
        };
+       struct list_head revoke_list;
        pgoff_t last_idx = ULONG_MAX;
        int err = 0;
 
+       INIT_LIST_HEAD(&revoke_list);
+
        list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) {
                struct page *page = cur->page;
 
@@ -393,14 +405,28 @@ retry:
                        last_idx = page->index;
                }
                unlock_page(page);
-               list_move_tail(&cur->list, revoke_list);
+               list_move_tail(&cur->list, &revoke_list);
        }
 
        if (last_idx != ULONG_MAX)
                f2fs_submit_merged_write_cond(sbi, inode, 0, last_idx, DATA);
 
-       if (!err)
-               __revoke_inmem_pages(inode, revoke_list, false, false);
+       if (err) {
+               /*
+                * try to revoke all committed pages, but still we could fail
+                * due to no memory or other reason, if that happened, EAGAIN
+                * will be returned, which means in such case, transaction is
+                * already not integrity, caller should use journal to do the
+                * recovery or rewrite & commit last transaction. For other
+                * error number, revoking was done by filesystem itself.
+                */
+               err = __revoke_inmem_pages(inode, &revoke_list, false, true);
+
+               /* drop all uncommitted pages */
+               __revoke_inmem_pages(inode, &fi->inmem_pages, true, false);
+       } else {
+               __revoke_inmem_pages(inode, &revoke_list, false, false);
+       }
 
        return err;
 }
@@ -409,34 +435,16 @@ int commit_inmem_pages(struct inode *inode)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct f2fs_inode_info *fi = F2FS_I(inode);
-       struct list_head revoke_list;
        int err;
 
-       INIT_LIST_HEAD(&revoke_list);
        f2fs_balance_fs(sbi, true);
        f2fs_lock_op(sbi);
 
        set_inode_flag(inode, FI_ATOMIC_COMMIT);
 
        mutex_lock(&fi->inmem_lock);
-       err = __commit_inmem_pages(inode, &revoke_list);
-       if (err) {
-               int ret;
-               /*
-                * try to revoke all committed pages, but still we could fail
-                * due to no memory or other reason, if that happened, EAGAIN
-                * will be returned, which means in such case, transaction is
-                * already not integrity, caller should use journal to do the
-                * recovery or rewrite & commit last transaction. For other
-                * error number, revoking was done by filesystem itself.
-                */
-               ret = __revoke_inmem_pages(inode, &revoke_list, false, true);
-               if (ret)
-                       err = ret;
+       err = __commit_inmem_pages(inode);
 
-               /* drop all uncommitted pages */
-               __revoke_inmem_pages(inode, &fi->inmem_pages, true, false);
-       }
        spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
        if (!list_empty(&fi->inmem_ilist))
                list_del_init(&fi->inmem_ilist);
@@ -915,6 +923,42 @@ static void __check_sit_bitmap(struct f2fs_sb_info *sbi,
 #endif
 }
 
+static void __init_discard_policy(struct f2fs_sb_info *sbi,
+                               struct discard_policy *dpolicy,
+                               int discard_type, unsigned int granularity)
+{
+       /* common policy */
+       dpolicy->type = discard_type;
+       dpolicy->sync = true;
+       dpolicy->granularity = granularity;
+
+       dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST;
+       dpolicy->io_aware_gran = MAX_PLIST_NUM;
+
+       if (discard_type == DPOLICY_BG) {
+               dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
+               dpolicy->mid_interval = DEF_MID_DISCARD_ISSUE_TIME;
+               dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
+               dpolicy->io_aware = true;
+               dpolicy->sync = false;
+               if (utilization(sbi) > DEF_DISCARD_URGENT_UTIL) {
+                       dpolicy->granularity = 1;
+                       dpolicy->max_interval = DEF_MIN_DISCARD_ISSUE_TIME;
+               }
+       } else if (discard_type == DPOLICY_FORCE) {
+               dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
+               dpolicy->mid_interval = DEF_MID_DISCARD_ISSUE_TIME;
+               dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
+               dpolicy->io_aware = false;
+       } else if (discard_type == DPOLICY_FSTRIM) {
+               dpolicy->io_aware = false;
+       } else if (discard_type == DPOLICY_UMOUNT) {
+               dpolicy->max_requests = UINT_MAX;
+               dpolicy->io_aware = false;
+       }
+}
+
+
 /* this function is copied from blkdev_issue_discard from block/blk-lib.c */
 static void __submit_discard_cmd(struct f2fs_sb_info *sbi,
                                                struct discard_policy *dpolicy,
@@ -929,6 +973,9 @@ static void __submit_discard_cmd(struct f2fs_sb_info *sbi,
        if (dc->state != D_PREP)
                return;
 
+       if (is_sbi_flag_set(sbi, SBI_NEED_FSCK))
+               return;
+
        trace_f2fs_issue_discard(dc->bdev, dc->start, dc->len);
 
        dc->error = __blkdev_issue_discard(dc->bdev,
@@ -1130,68 +1177,6 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi,
        return 0;
 }
 
-static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi,
-                                       struct discard_policy *dpolicy,
-                                       unsigned int start, unsigned int end)
-{
-       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
-       struct discard_cmd *prev_dc = NULL, *next_dc = NULL;
-       struct rb_node **insert_p = NULL, *insert_parent = NULL;
-       struct discard_cmd *dc;
-       struct blk_plug plug;
-       int issued;
-
-next:
-       issued = 0;
-
-       mutex_lock(&dcc->cmd_lock);
-       f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root));
-
-       dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root,
-                                       NULL, start,
-                                       (struct rb_entry **)&prev_dc,
-                                       (struct rb_entry **)&next_dc,
-                                       &insert_p, &insert_parent, true);
-       if (!dc)
-               dc = next_dc;
-
-       blk_start_plug(&plug);
-
-       while (dc && dc->lstart <= end) {
-               struct rb_node *node;
-
-               if (dc->len < dpolicy->granularity)
-                       goto skip;
-
-               if (dc->state != D_PREP) {
-                       list_move_tail(&dc->list, &dcc->fstrim_list);
-                       goto skip;
-               }
-
-               __submit_discard_cmd(sbi, dpolicy, dc);
-
-               if (++issued >= dpolicy->max_requests) {
-                       start = dc->lstart + dc->len;
-
-                       blk_finish_plug(&plug);
-                       mutex_unlock(&dcc->cmd_lock);
-
-                       schedule();
-
-                       goto next;
-               }
-skip:
-               node = rb_next(&dc->rb_node);
-               dc = rb_entry_safe(node, struct discard_cmd, rb_node);
-
-               if (fatal_signal_pending(current))
-                       break;
-       }
-
-       blk_finish_plug(&plug);
-       mutex_unlock(&dcc->cmd_lock);
-}
-
 static int __issue_discard_cmd(struct f2fs_sb_info *sbi,
                                        struct discard_policy *dpolicy)
 {
@@ -1332,7 +1317,18 @@ next:
 static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi,
                                                struct discard_policy *dpolicy)
 {
-       __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX);
+       struct discard_policy dp;
+
+       if (dpolicy) {
+               __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX);
+               return;
+       }
+
+       /* wait all */
+       __init_discard_policy(sbi, &dp, DPOLICY_FSTRIM, 1);
+       __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
+       __init_discard_policy(sbi, &dp, DPOLICY_UMOUNT, 1);
+       __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
 }
 
 /* This should be covered by global mutex, &sit_i->sentry_lock */
@@ -1377,11 +1373,13 @@ bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi)
        struct discard_policy dpolicy;
        bool dropped;
 
-       init_discard_policy(&dpolicy, DPOLICY_UMOUNT, dcc->discard_granularity);
+       __init_discard_policy(sbi, &dpolicy, DPOLICY_UMOUNT,
+                                       dcc->discard_granularity);
        __issue_discard_cmd(sbi, &dpolicy);
        dropped = __drop_discard_cmd(sbi);
-       __wait_all_discard_cmd(sbi, &dpolicy);
 
+       /* just to make sure there is no pending discard commands */
+       __wait_all_discard_cmd(sbi, NULL);
        return dropped;
 }
 
@@ -1397,7 +1395,7 @@ static int issue_discard_thread(void *data)
        set_freezable();
 
        do {
-               init_discard_policy(&dpolicy, DPOLICY_BG,
+               __init_discard_policy(sbi, &dpolicy, DPOLICY_BG,
                                        dcc->discard_granularity);
 
                wait_event_interruptible_timeout(*q,
@@ -1410,19 +1408,25 @@ static int issue_discard_thread(void *data)
                        continue;
                if (kthread_should_stop())
                        return 0;
+               if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) {
+                       wait_ms = dpolicy.max_interval;
+                       continue;
+               }
 
                if (dcc->discard_wake)
                        dcc->discard_wake = 0;
 
-               if (sbi->gc_thread && sbi->gc_thread->gc_urgent)
-                       init_discard_policy(&dpolicy, DPOLICY_FORCE, 1);
+               if (sbi->gc_mode == GC_URGENT)
+                       __init_discard_policy(sbi, &dpolicy, DPOLICY_FORCE, 1);
 
                sb_start_intwrite(sbi->sb);
 
                issued = __issue_discard_cmd(sbi, &dpolicy);
-               if (issued) {
+               if (issued > 0) {
                        __wait_all_discard_cmd(sbi, &dpolicy);
                        wait_ms = dpolicy.min_interval;
+               } else if (issued == -1){
+                       wait_ms = dpolicy.mid_interval;
                } else {
                        wait_ms = dpolicy.max_interval;
                }
@@ -1591,16 +1595,20 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc,
        return false;
 }
 
+static void release_discard_addr(struct discard_entry *entry)
+{
+       list_del(&entry->list);
+       kmem_cache_free(discard_entry_slab, entry);
+}
+
 void release_discard_addrs(struct f2fs_sb_info *sbi)
 {
        struct list_head *head = &(SM_I(sbi)->dcc_info->entry_list);
        struct discard_entry *entry, *this;
 
        /* drop caches */
-       list_for_each_entry_safe(entry, this, head, list) {
-               list_del(&entry->list);
-               kmem_cache_free(discard_entry_slab, entry);
-       }
+       list_for_each_entry_safe(entry, this, head, list)
+               release_discard_addr(entry);
 }
 
 /*
@@ -1700,40 +1708,13 @@ skip:
                if (cur_pos < sbi->blocks_per_seg)
                        goto find_next;
 
-               list_del(&entry->list);
+               release_discard_addr(entry);
                dcc->nr_discards -= total_len;
-               kmem_cache_free(discard_entry_slab, entry);
        }
 
        wake_up_discard_thread(sbi, false);
 }
 
-void init_discard_policy(struct discard_policy *dpolicy,
-                               int discard_type, unsigned int granularity)
-{
-       /* common policy */
-       dpolicy->type = discard_type;
-       dpolicy->sync = true;
-       dpolicy->granularity = granularity;
-
-       dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST;
-       dpolicy->io_aware_gran = MAX_PLIST_NUM;
-
-       if (discard_type == DPOLICY_BG) {
-               dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
-               dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
-               dpolicy->io_aware = true;
-       } else if (discard_type == DPOLICY_FORCE) {
-               dpolicy->min_interval = DEF_MIN_DISCARD_ISSUE_TIME;
-               dpolicy->max_interval = DEF_MAX_DISCARD_ISSUE_TIME;
-               dpolicy->io_aware = false;
-       } else if (discard_type == DPOLICY_FSTRIM) {
-               dpolicy->io_aware = false;
-       } else if (discard_type == DPOLICY_UMOUNT) {
-               dpolicy->io_aware = false;
-       }
-}
-
 static int create_discard_cmd_control(struct f2fs_sb_info *sbi)
 {
        dev_t dev = sbi->sb->s_bdev->bd_dev;
@@ -1929,7 +1910,7 @@ bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr)
        struct seg_entry *se;
        bool is_cp = false;
 
-       if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR)
+       if (!is_valid_blkaddr(blkaddr))
                return true;
 
        down_read(&sit_i->sentry_lock);
@@ -2020,6 +2001,7 @@ static void write_current_sum_page(struct f2fs_sb_info *sbi,
        struct f2fs_summary_block *dst;
 
        dst = (struct f2fs_summary_block *)page_address(page);
+       memset(dst, 0, PAGE_SIZE);
 
        mutex_lock(&curseg->curseg_mutex);
 
@@ -2373,11 +2355,72 @@ bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        return has_candidate;
 }
 
+static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi,
+                                       struct discard_policy *dpolicy,
+                                       unsigned int start, unsigned int end)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct discard_cmd *prev_dc = NULL, *next_dc = NULL;
+       struct rb_node **insert_p = NULL, *insert_parent = NULL;
+       struct discard_cmd *dc;
+       struct blk_plug plug;
+       int issued;
+
+next:
+       issued = 0;
+
+       mutex_lock(&dcc->cmd_lock);
+       f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root));
+
+       dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root,
+                                       NULL, start,
+                                       (struct rb_entry **)&prev_dc,
+                                       (struct rb_entry **)&next_dc,
+                                       &insert_p, &insert_parent, true);
+       if (!dc)
+               dc = next_dc;
+
+       blk_start_plug(&plug);
+
+       while (dc && dc->lstart <= end) {
+               struct rb_node *node;
+
+               if (dc->len < dpolicy->granularity)
+                       goto skip;
+
+               if (dc->state != D_PREP) {
+                       list_move_tail(&dc->list, &dcc->fstrim_list);
+                       goto skip;
+               }
+
+               __submit_discard_cmd(sbi, dpolicy, dc);
+
+               if (++issued >= dpolicy->max_requests) {
+                       start = dc->lstart + dc->len;
+
+                       blk_finish_plug(&plug);
+                       mutex_unlock(&dcc->cmd_lock);
+                       __wait_all_discard_cmd(sbi, NULL);
+                       congestion_wait(BLK_RW_ASYNC, HZ/50);
+                       goto next;
+               }
+skip:
+               node = rb_next(&dc->rb_node);
+               dc = rb_entry_safe(node, struct discard_cmd, rb_node);
+
+               if (fatal_signal_pending(current))
+                       break;
+       }
+
+       blk_finish_plug(&plug);
+       mutex_unlock(&dcc->cmd_lock);
+}
+
 int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
 {
        __u64 start = F2FS_BYTES_TO_BLK(range->start);
        __u64 end = start + F2FS_BYTES_TO_BLK(range->len) - 1;
-       unsigned int start_segno, end_segno, cur_segno;
+       unsigned int start_segno, end_segno;
        block_t start_block, end_block;
        struct cp_control cpc;
        struct discard_policy dpolicy;
@@ -2388,12 +2431,12 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
                return -EINVAL;
 
        if (end <= MAIN_BLKADDR(sbi))
-               goto out;
+               return -EINVAL;
 
        if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) {
                f2fs_msg(sbi->sb, KERN_WARNING,
                        "Found FS corruption, run fsck to fix.");
-               goto out;
+               return -EIO;
        }
 
        /* start/end segment number in main_area */
@@ -2403,40 +2446,36 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
 
        cpc.reason = CP_DISCARD;
        cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen));
+       cpc.trim_start = start_segno;
+       cpc.trim_end = end_segno;
 
-       /* do checkpoint to issue discard commands safely */
-       for (cur_segno = start_segno; cur_segno <= end_segno;
-                                       cur_segno = cpc.trim_end + 1) {
-               cpc.trim_start = cur_segno;
-
-               if (sbi->discard_blks == 0)
-                       break;
-               else if (sbi->discard_blks < BATCHED_TRIM_BLOCKS(sbi))
-                       cpc.trim_end = end_segno;
-               else
-                       cpc.trim_end = min_t(unsigned int,
-                               rounddown(cur_segno +
-                               BATCHED_TRIM_SEGMENTS(sbi),
-                               sbi->segs_per_sec) - 1, end_segno);
-
-               mutex_lock(&sbi->gc_mutex);
-               err = write_checkpoint(sbi, &cpc);
-               mutex_unlock(&sbi->gc_mutex);
-               if (err)
-                       break;
+       if (sbi->discard_blks == 0)
+               goto out;
 
-               schedule();
-       }
+       mutex_lock(&sbi->gc_mutex);
+       err = write_checkpoint(sbi, &cpc);
+       mutex_unlock(&sbi->gc_mutex);
+       if (err)
+               goto out;
 
        start_block = START_BLOCK(sbi, start_segno);
-       end_block = START_BLOCK(sbi, min(cur_segno, end_segno) + 1);
+       end_block = START_BLOCK(sbi, end_segno + 1);
 
-       init_discard_policy(&dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen);
+       __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen);
        __issue_discard_cmd_range(sbi, &dpolicy, start_block, end_block);
-       trimmed = __wait_discard_cmd_range(sbi, &dpolicy,
+
+       /*
+        * We filed discard candidates, but actually we don't need to wait for
+        * all of them, since they'll be issued in idle time along with runtime
+        * discard option. User configuration looks like using runtime discard
+        * or periodic fstrim instead of it.
+        */
+       if (!test_opt(sbi, DISCARD)) {
+               trimmed = __wait_discard_cmd_range(sbi, &dpolicy,
                                        start_block, end_block);
+               range->len = F2FS_BLK_TO_BYTES(trimmed);
+       }
 out:
-       range->len = F2FS_BLK_TO_BYTES(trimmed);
        return err;
 }
 
@@ -2588,7 +2627,9 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
                if (is_cold_data(fio->page) || file_is_cold(inode))
                        return CURSEG_COLD_DATA;
                if (file_is_hot(inode) ||
-                               is_inode_flag_set(inode, FI_HOT_DATA))
+                               is_inode_flag_set(inode, FI_HOT_DATA) ||
+                               is_inode_flag_set(inode, FI_ATOMIC_FILE) ||
+                               is_inode_flag_set(inode, FI_VOLATILE_FILE))
                        return CURSEG_HOT_DATA;
                return rw_hint_to_seg_type(inode->i_write_hint);
        } else {
@@ -2722,7 +2763,10 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio)
 {
        int type = __get_segment_type(fio);
        int err;
+       bool keep_order = (test_opt(fio->sbi, LFS) && type == CURSEG_COLD_DATA);
 
+       if (keep_order)
+               down_read(&fio->sbi->io_order_lock);
 reallocate:
        allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
                        &fio->new_blkaddr, sum, type, fio, true);
@@ -2735,6 +2779,8 @@ reallocate:
        } else if (!err) {
                update_device_state(fio);
        }
+       if (keep_order)
+               up_read(&fio->sbi->io_order_lock);
 }
 
 void write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
@@ -2757,6 +2803,7 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
                fio.op_flags &= ~REQ_META;
 
        set_page_writeback(page);
+       ClearPageError(page);
        f2fs_submit_page_write(&fio);
 
        f2fs_update_iostat(sbi, io_type, F2FS_BLKSIZE);
@@ -2932,7 +2979,7 @@ void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr)
 {
        struct page *cpage;
 
-       if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR)
+       if (!is_valid_blkaddr(blkaddr))
                return;
 
        cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
@@ -3116,6 +3163,7 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr)
 
        page = grab_meta_page(sbi, blkaddr++);
        kaddr = (unsigned char *)page_address(page);
+       memset(kaddr, 0, PAGE_SIZE);
 
        /* Step 1: write nat cache */
        seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA);
@@ -3140,6 +3188,7 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr)
                        if (!page) {
                                page = grab_meta_page(sbi, blkaddr++);
                                kaddr = (unsigned char *)page_address(page);
+                               memset(kaddr, 0, PAGE_SIZE);
                                written_size = 0;
                        }
                        summary = (struct f2fs_summary *)(kaddr + written_size);
@@ -3380,6 +3429,11 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
                        int offset, sit_offset;
 
                        se = get_seg_entry(sbi, segno);
+#ifdef CONFIG_F2FS_CHECK_FS
+                       if (memcmp(se->cur_valid_map, se->cur_valid_map_mir,
+                                               SIT_VBLOCK_MAP_SIZE))
+                               f2fs_bug_on(sbi, 1);
+#endif
 
                        /* add discard candidates */
                        if (!(cpc->reason & CP_DISCARD)) {
@@ -3395,10 +3449,14 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
                                                        cpu_to_le32(segno);
                                seg_info_to_raw_sit(se,
                                        &sit_in_journal(journal, offset));
+                               check_block_count(sbi, segno,
+                                       &sit_in_journal(journal, offset));
                        } else {
                                sit_offset = SIT_ENTRY_OFFSET(sit_i, segno);
                                seg_info_to_raw_sit(se,
                                                &raw_sit->entries[sit_offset]);
+                               check_block_count(sbi, segno,
+                                               &raw_sit->entries[sit_offset]);
                        }
 
                        __clear_bit(segno, bitmap);
@@ -3597,6 +3655,7 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
        unsigned int i, start, end;
        unsigned int readed, start_blk = 0;
        int err = 0;
+       block_t total_node_blocks = 0;
 
        do {
                readed = ra_meta_pages(sbi, start_blk, BIO_MAX_PAGES,
@@ -3619,6 +3678,8 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
                        if (err)
                                return err;
                        seg_info_from_raw_sit(se, &sit);
+                       if (IS_NODESEG(se->type))
+                               total_node_blocks += se->valid_blocks;
 
                        /* build discard map only one time */
                        if (f2fs_discard_en(sbi)) {
@@ -3647,15 +3708,28 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
                unsigned int old_valid_blocks;
 
                start = le32_to_cpu(segno_in_journal(journal, i));
+               if (start >= MAIN_SEGS(sbi)) {
+                       f2fs_msg(sbi->sb, KERN_ERR,
+                                       "Wrong journal entry on segno %u",
+                                       start);
+                       set_sbi_flag(sbi, SBI_NEED_FSCK);
+                       err = -EINVAL;
+                       break;
+               }
+
                se = &sit_i->sentries[start];
                sit = sit_in_journal(journal, i);
 
                old_valid_blocks = se->valid_blocks;
+               if (IS_NODESEG(se->type))
+                       total_node_blocks -= old_valid_blocks;
 
                err = check_block_count(sbi, start, &sit);
                if (err)
                        break;
                seg_info_from_raw_sit(se, &sit);
+               if (IS_NODESEG(se->type))
+                       total_node_blocks += se->valid_blocks;
 
                if (f2fs_discard_en(sbi)) {
                        if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) {
@@ -3664,16 +3738,28 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
                        } else {
                                memcpy(se->discard_map, se->cur_valid_map,
                                                        SIT_VBLOCK_MAP_SIZE);
-                               sbi->discard_blks += old_valid_blocks -
-                                                       se->valid_blocks;
+                               sbi->discard_blks += old_valid_blocks;
+                               sbi->discard_blks -= se->valid_blocks;
                        }
                }
 
-               if (sbi->segs_per_sec > 1)
+               if (sbi->segs_per_sec > 1) {
                        get_sec_entry(sbi, start)->valid_blocks +=
-                               se->valid_blocks - old_valid_blocks;
+                                                       se->valid_blocks;
+                       get_sec_entry(sbi, start)->valid_blocks -=
+                                                       old_valid_blocks;
+               }
        }
        up_read(&curseg->journal_rwsem);
+
+       if (!err && total_node_blocks != valid_node_count(sbi)) {
+               f2fs_msg(sbi->sb, KERN_ERR,
+                       "SIT is corrupted node# %u vs %u",
+                       total_node_blocks, valid_node_count(sbi));
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               err = -EINVAL;
+       }
+
        return err;
 }
 
@@ -3772,7 +3858,7 @@ static void init_min_max_mtime(struct f2fs_sb_info *sbi)
 
        down_write(&sit_i->sentry_lock);
 
-       sit_i->min_mtime = LLONG_MAX;
+       sit_i->min_mtime = ULLONG_MAX;
 
        for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
                unsigned int i;
@@ -3822,8 +3908,6 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
        sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS;
        sm_info->min_ssr_sections = reserved_sections(sbi);
 
-       sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS;
-
        INIT_LIST_HEAD(&sm_info->sit_entry_set);
 
        init_rwsem(&sm_info->curseg_lock);