OSDN Git Service

Merge android-4.4-p.195 (4af3204) into msm-4.4
[sagit-ice-cold/kernel_xiaomi_msm8998.git] / fs / f2fs / segment.c
index 1c7b75a..8f1af57 100644 (file)
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * fs/f2fs/segment.c
  *
  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
  *             http://www.samsung.com/
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
  */
 #include <linux/fs.h>
 #include <linux/f2fs_fs.h>
@@ -169,7 +166,7 @@ found:
        return result - size + __reverse_ffz(tmp);
 }
 
-bool need_SSR(struct f2fs_sb_info *sbi)
+bool f2fs_need_SSR(struct f2fs_sb_info *sbi)
 {
        int node_secs = get_blocktype_secs(sbi, F2FS_DIRTY_NODES);
        int dent_secs = get_blocktype_secs(sbi, F2FS_DIRTY_DENTS);
@@ -177,14 +174,16 @@ 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;
+       if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
                return true;
 
        return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs +
                        SM_I(sbi)->min_ssr_sections + reserved_sections(sbi));
 }
 
-void register_inmem_page(struct inode *inode, struct page *page)
+void f2fs_register_inmem_page(struct inode *inode, struct page *page)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct f2fs_inode_info *fi = F2FS_I(inode);
@@ -192,8 +191,7 @@ void register_inmem_page(struct inode *inode, struct page *page)
 
        f2fs_trace_pid(page);
 
-       set_page_private(page, (unsigned long)ATOMIC_WRITTEN_PAGE);
-       SetPagePrivate(page);
+       f2fs_set_page_private(page, (unsigned long)ATOMIC_WRITTEN_PAGE);
 
        new = f2fs_kmem_cache_alloc(inmem_entry_slab, GFP_NOFS);
 
@@ -216,7 +214,8 @@ void register_inmem_page(struct inode *inode, struct page *page)
 }
 
 static int __revoke_inmem_pages(struct inode *inode,
-                               struct list_head *head, bool drop, bool recover)
+                               struct list_head *head, bool drop, bool recover,
+                               bool trylock)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct inmem_pages *cur, *tmp;
@@ -228,7 +227,18 @@ static int __revoke_inmem_pages(struct inode *inode,
                if (drop)
                        trace_f2fs_commit_inmem_page(page, INMEM_DROP);
 
-               lock_page(page);
+               if (trylock) {
+                       /*
+                        * to avoid deadlock in between page lock and
+                        * inmem_lock.
+                        */
+                       if (!trylock_page(page))
+                               continue;
+               } else {
+                       lock_page(page);
+               }
+
+               f2fs_wait_on_page_writeback(page, DATA, true, true);
 
                if (recover) {
                        struct dnode_of_data dn;
@@ -237,7 +247,8 @@ static int __revoke_inmem_pages(struct inode *inode,
                        trace_f2fs_commit_inmem_page(page, INMEM_REVOKE);
 retry:
                        set_new_dnode(&dn, inode, NULL, NULL, 0);
-                       err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
+                       err = f2fs_get_dnode_of_data(&dn, page->index,
+                                                               LOOKUP_NODE);
                        if (err) {
                                if (err == -ENOMEM) {
                                        congestion_wait(BLK_RW_ASYNC, HZ/50);
@@ -247,9 +258,15 @@ retry:
                                err = -EAGAIN;
                                goto next;
                        }
-                       get_node_info(sbi, dn.nid, &ni);
+
+                       err = f2fs_get_node_info(sbi, dn.nid, &ni);
+                       if (err) {
+                               f2fs_put_dnode(&dn);
+                               return err;
+                       }
+
                        if (cur->old_addr == NEW_ADDR) {
-                               invalidate_blocks(sbi, dn.data_blkaddr);
+                               f2fs_invalidate_blocks(sbi, dn.data_blkaddr);
                                f2fs_update_data_blkaddr(&dn, NEW_ADDR);
                        } else
                                f2fs_replace_block(sbi, &dn, dn.data_blkaddr,
@@ -258,10 +275,11 @@ retry:
                }
 next:
                /* we don't need to invalidate this in the sccessful status */
-               if (drop || recover)
+               if (drop || recover) {
                        ClearPageUptodate(page);
-               set_page_private(page, 0);
-               ClearPagePrivate(page);
+                       clear_cold_data(page);
+               }
+               f2fs_clear_page_private(page);
                f2fs_put_page(page, 1);
 
                list_del(&cur->list);
@@ -271,7 +289,7 @@ next:
        return err;
 }
 
-void drop_inmem_pages_all(struct f2fs_sb_info *sbi)
+void f2fs_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,33 +305,47 @@ next:
        spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
 
        if (inode) {
-               drop_inmem_pages(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);
+               f2fs_drop_inmem_pages(inode);
                iput(inode);
        }
+skip:
        congestion_wait(BLK_RW_ASYNC, HZ/50);
        cond_resched();
        goto next;
 }
 
-void drop_inmem_pages(struct inode *inode)
+void f2fs_drop_inmem_pages(struct inode *inode)
 {
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct f2fs_inode_info *fi = F2FS_I(inode);
 
-       mutex_lock(&fi->inmem_lock);
-       __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);
-       spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
-       mutex_unlock(&fi->inmem_lock);
+       while (!list_empty(&fi->inmem_pages)) {
+               mutex_lock(&fi->inmem_lock);
+               __revoke_inmem_pages(inode, &fi->inmem_pages,
+                                               true, false, true);
+
+               if (list_empty(&fi->inmem_pages)) {
+                       spin_lock(&sbi->inode_lock[ATOMIC_FILE]);
+                       if (!list_empty(&fi->inmem_ilist))
+                               list_del_init(&fi->inmem_ilist);
+                       spin_unlock(&sbi->inode_lock[ATOMIC_FILE]);
+               }
+               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);
 }
 
-void drop_inmem_page(struct inode *inode, struct page *page)
+void f2fs_drop_inmem_page(struct inode *inode, struct page *page)
 {
        struct f2fs_inode_info *fi = F2FS_I(inode);
        struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
@@ -328,7 +360,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);
 
@@ -336,15 +368,13 @@ void drop_inmem_page(struct inode *inode, struct page *page)
        kmem_cache_free(inmem_entry_slab, cur);
 
        ClearPageUptodate(page);
-       set_page_private(page, 0);
-       ClearPagePrivate(page);
+       f2fs_clear_page_private(page);
        f2fs_put_page(page, 0);
 
        trace_f2fs_commit_inmem_page(page, INMEM_INVALIDATE);
 }
 
-static int __commit_inmem_pages(struct inode *inode,
-                                       struct list_head *revoke_list)
+static int __f2fs_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 +387,12 @@ static int __commit_inmem_pages(struct inode *inode,
                .op_flags = REQ_SYNC | REQ_PRIO,
                .io_type = FS_DATA_IO,
        };
-       pgoff_t last_idx = ULONG_MAX;
+       struct list_head revoke_list;
+       bool submit_bio = false;
        int err = 0;
 
+       INIT_LIST_HEAD(&revoke_list);
+
        list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) {
                struct page *page = cur->page;
 
@@ -367,18 +400,19 @@ static int __commit_inmem_pages(struct inode *inode,
                if (page->mapping == inode->i_mapping) {
                        trace_f2fs_commit_inmem_page(page, INMEM);
 
+                       f2fs_wait_on_page_writeback(page, DATA, true, true);
+
                        set_page_dirty(page);
-                       f2fs_wait_on_page_writeback(page, DATA, true);
                        if (clear_page_dirty_for_io(page)) {
                                inode_dec_dirty_pages(inode);
-                               remove_dirty_inode(inode);
+                               f2fs_remove_dirty_inode(inode);
                        }
 retry:
                        fio.page = page;
                        fio.old_blkaddr = NULL_ADDR;
                        fio.encrypted_page = NULL;
                        fio.need_lock = LOCK_DONE;
-                       err = do_write_data_page(&fio);
+                       err = f2fs_do_write_data_page(&fio);
                        if (err) {
                                if (err == -ENOMEM) {
                                        congestion_wait(BLK_RW_ASYNC, HZ/50);
@@ -390,53 +424,54 @@ retry:
                        }
                        /* record old blkaddr for revoking */
                        cur->old_addr = fio.old_blkaddr;
-                       last_idx = page->index;
+                       submit_bio = true;
                }
                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 (submit_bio)
+               f2fs_submit_merged_write_cond(sbi, inode, NULL, 0, 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, false);
+
+               /* drop all uncommitted pages */
+               __revoke_inmem_pages(inode, &fi->inmem_pages,
+                                               true, false, false);
+       } else {
+               __revoke_inmem_pages(inode, &revoke_list,
+                                               false, false, false);
+       }
 
        return err;
 }
 
-int commit_inmem_pages(struct inode *inode)
+int f2fs_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);
 
+       down_write(&fi->i_gc_rwsem[WRITE]);
+
+       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 = __f2fs_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);
@@ -446,6 +481,8 @@ int commit_inmem_pages(struct inode *inode)
        clear_inode_flag(inode, FI_ATOMIC_COMMIT);
 
        f2fs_unlock_op(sbi);
+       up_write(&fi->i_gc_rwsem[WRITE]);
+
        return err;
 }
 
@@ -455,17 +492,18 @@ int commit_inmem_pages(struct inode *inode)
  */
 void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
 {
-#ifdef CONFIG_F2FS_FAULT_INJECTION
        if (time_to_inject(sbi, FAULT_CHECKPOINT)) {
                f2fs_show_injection_info(FAULT_CHECKPOINT);
                f2fs_stop_checkpoint(sbi, false);
        }
-#endif
 
        /* balance_fs_bg is able to be pending */
        if (need && excess_cached_nats(sbi))
                f2fs_balance_fs_bg(sbi);
 
+       if (f2fs_is_checkpoint_ready(sbi))
+               return;
+
        /*
         * We should do GC or end up with checkpoint, if there are so many dirty
         * dir/node pages without enough free segments.
@@ -482,33 +520,39 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
                return;
 
        /* try to shrink extent cache when there is no enough memory */
-       if (!available_free_memory(sbi, EXTENT_CACHE))
+       if (!f2fs_available_free_memory(sbi, EXTENT_CACHE))
                f2fs_shrink_extent_tree(sbi, EXTENT_CACHE_SHRINK_NUMBER);
 
        /* check the # of cached NAT entries */
-       if (!available_free_memory(sbi, NAT_ENTRIES))
-               try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK);
+       if (!f2fs_available_free_memory(sbi, NAT_ENTRIES))
+               f2fs_try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK);
 
-       if (!available_free_memory(sbi, FREE_NIDS))
-               try_to_free_nids(sbi, MAX_FREE_NIDS);
+       if (!f2fs_available_free_memory(sbi, FREE_NIDS))
+               f2fs_try_to_free_nids(sbi, MAX_FREE_NIDS);
        else
-               build_free_nids(sbi, false, false);
+               f2fs_build_free_nids(sbi, false, false);
 
-       if (!is_idle(sbi) && !excess_dirty_nats(sbi))
+       if (!is_idle(sbi, REQ_TIME) &&
+               (!excess_dirty_nats(sbi) && !excess_dirty_nodes(sbi)))
                return;
 
        /* checkpoint is the only way to shrink partial cached entries */
-       if (!available_free_memory(sbi, NAT_ENTRIES) ||
-                       !available_free_memory(sbi, INO_ENTRIES) ||
+       if (!f2fs_available_free_memory(sbi, NAT_ENTRIES) ||
+                       !f2fs_available_free_memory(sbi, INO_ENTRIES) ||
                        excess_prefree_segs(sbi) ||
                        excess_dirty_nats(sbi) ||
+                       excess_dirty_nodes(sbi) ||
                        f2fs_time_over(sbi, CP_TIME)) {
                if (test_opt(sbi, DATA_FLUSH)) {
                        struct blk_plug plug;
 
+                       mutex_lock(&sbi->flush_lock);
+
                        blk_start_plug(&plug);
-                       sync_dirty_inodes(sbi, FILE_INODE);
+                       f2fs_sync_dirty_inodes(sbi, FILE_INODE);
                        blk_finish_plug(&plug);
+
+                       mutex_unlock(&sbi->flush_lock);
                }
                f2fs_sync_fs(sbi->sb, true);
                stat_inc_bg_cp_count(sbi->stat_info);
@@ -518,9 +562,13 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
 static int __submit_flush_wait(struct f2fs_sb_info *sbi,
                                struct block_device *bdev)
 {
-       struct bio *bio = f2fs_bio_alloc(sbi, 0, true);
+       struct bio *bio;
        int ret;
 
+       bio = f2fs_bio_alloc(sbi, 0, false);
+       if (!bio)
+               return -ENOMEM;
+
        bio->bi_rw = REQ_OP_WRITE;
        bio->bi_bdev = bdev;
        ret = submit_bio_wait(WRITE_FLUSH, bio);
@@ -536,11 +584,11 @@ static int submit_flush_wait(struct f2fs_sb_info *sbi, nid_t ino)
        int ret = 0;
        int i;
 
-       if (!sbi->s_ndevs)
+       if (!f2fs_is_multi_device(sbi))
                return __submit_flush_wait(sbi, sbi->sb->s_bdev);
 
        for (i = 0; i < sbi->s_ndevs; i++) {
-               if (!is_dirty_device(sbi, ino, i, FLUSH_INO))
+               if (!f2fs_is_dirty_device(sbi, ino, i, FLUSH_INO))
                        continue;
                ret = __submit_flush_wait(sbi, FDEV(i).bdev);
                if (ret)
@@ -597,14 +645,17 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino)
                return 0;
 
        if (!test_opt(sbi, FLUSH_MERGE)) {
+               atomic_inc(&fcc->queued_flush);
                ret = submit_flush_wait(sbi, ino);
+               atomic_dec(&fcc->queued_flush);
                atomic_inc(&fcc->issued_flush);
                return ret;
        }
 
-       if (atomic_inc_return(&fcc->issing_flush) == 1 || sbi->s_ndevs > 1) {
+       if (atomic_inc_return(&fcc->queued_flush) == 1 ||
+           f2fs_is_multi_device(sbi)) {
                ret = submit_flush_wait(sbi, ino);
-               atomic_dec(&fcc->issing_flush);
+               atomic_dec(&fcc->queued_flush);
 
                atomic_inc(&fcc->issued_flush);
                return ret;
@@ -623,14 +674,14 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino)
 
        if (fcc->f2fs_issue_flush) {
                wait_for_completion(&cmd.wait);
-               atomic_dec(&fcc->issing_flush);
+               atomic_dec(&fcc->queued_flush);
        } else {
                struct llist_node *list;
 
                list = llist_del_all(&fcc->issue_list);
                if (!list) {
                        wait_for_completion(&cmd.wait);
-                       atomic_dec(&fcc->issing_flush);
+                       atomic_dec(&fcc->queued_flush);
                } else {
                        struct flush_cmd *tmp, *next;
 
@@ -639,7 +690,7 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino)
                        llist_for_each_entry_safe(tmp, next, list, llnode) {
                                if (tmp == &cmd) {
                                        cmd.ret = ret;
-                                       atomic_dec(&fcc->issing_flush);
+                                       atomic_dec(&fcc->queued_flush);
                                        continue;
                                }
                                tmp->ret = ret;
@@ -651,7 +702,7 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi, nid_t ino)
        return cmd.ret;
 }
 
-int create_flush_cmd_control(struct f2fs_sb_info *sbi)
+int f2fs_create_flush_cmd_control(struct f2fs_sb_info *sbi)
 {
        dev_t dev = sbi->sb->s_bdev->bd_dev;
        struct flush_cmd_control *fcc;
@@ -668,7 +719,7 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi)
        if (!fcc)
                return -ENOMEM;
        atomic_set(&fcc->issued_flush, 0);
-       atomic_set(&fcc->issing_flush, 0);
+       atomic_set(&fcc->queued_flush, 0);
        init_waitqueue_head(&fcc->flush_wait_queue);
        init_llist_head(&fcc->issue_list);
        SM_I(sbi)->fcc_info = fcc;
@@ -680,7 +731,7 @@ init_thread:
                                "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev));
        if (IS_ERR(fcc->f2fs_issue_flush)) {
                err = PTR_ERR(fcc->f2fs_issue_flush);
-               kfree(fcc);
+               kvfree(fcc);
                SM_I(sbi)->fcc_info = NULL;
                return err;
        }
@@ -688,7 +739,7 @@ init_thread:
        return err;
 }
 
-void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free)
+void f2fs_destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free)
 {
        struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info;
 
@@ -699,7 +750,7 @@ void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free)
                kthread_stop(flush_thread);
        }
        if (free) {
-               kfree(fcc);
+               kvfree(fcc);
                SM_I(sbi)->fcc_info = NULL;
        }
 }
@@ -708,7 +759,7 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi)
 {
        int ret = 0, i;
 
-       if (!sbi->s_ndevs)
+       if (!f2fs_is_multi_device(sbi))
                return 0;
 
        for (i = 1; i < sbi->s_ndevs; i++) {
@@ -780,7 +831,7 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
 static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
 {
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
-       unsigned short valid_blocks;
+       unsigned short valid_blocks, ckpt_valid_blocks;
 
        if (segno == NULL_SEGNO || IS_CURSEG(sbi, segno))
                return;
@@ -788,8 +839,10 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
        mutex_lock(&dirty_i->seglist_lock);
 
        valid_blocks = get_valid_blocks(sbi, segno, false);
+       ckpt_valid_blocks = get_ckpt_valid_blocks(sbi, segno);
 
-       if (valid_blocks == 0) {
+       if (valid_blocks == 0 && (!is_sbi_flag_set(sbi, SBI_CP_DISABLED) ||
+                               ckpt_valid_blocks == sbi->blocks_per_seg)) {
                __locate_dirty_segment(sbi, segno, PRE);
                __remove_dirty_segment(sbi, segno, DIRTY);
        } else if (valid_blocks < sbi->blocks_per_seg) {
@@ -802,6 +855,82 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
        mutex_unlock(&dirty_i->seglist_lock);
 }
 
+/* This moves currently empty dirty blocks to prefree. Must hold seglist_lock */
+void f2fs_dirty_to_prefree(struct f2fs_sb_info *sbi)
+{
+       struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
+       unsigned int segno;
+
+       mutex_lock(&dirty_i->seglist_lock);
+       for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) {
+               if (get_valid_blocks(sbi, segno, false))
+                       continue;
+               if (IS_CURSEG(sbi, segno))
+                       continue;
+               __locate_dirty_segment(sbi, segno, PRE);
+               __remove_dirty_segment(sbi, segno, DIRTY);
+       }
+       mutex_unlock(&dirty_i->seglist_lock);
+}
+
+block_t f2fs_get_unusable_blocks(struct f2fs_sb_info *sbi)
+{
+       int ovp_hole_segs =
+               (overprovision_segments(sbi) - reserved_segments(sbi));
+       block_t ovp_holes = ovp_hole_segs << sbi->log_blocks_per_seg;
+       struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
+       block_t holes[2] = {0, 0};      /* DATA and NODE */
+       block_t unusable;
+       struct seg_entry *se;
+       unsigned int segno;
+
+       mutex_lock(&dirty_i->seglist_lock);
+       for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) {
+               se = get_seg_entry(sbi, segno);
+               if (IS_NODESEG(se->type))
+                       holes[NODE] += sbi->blocks_per_seg - se->valid_blocks;
+               else
+                       holes[DATA] += sbi->blocks_per_seg - se->valid_blocks;
+       }
+       mutex_unlock(&dirty_i->seglist_lock);
+
+       unusable = holes[DATA] > holes[NODE] ? holes[DATA] : holes[NODE];
+       if (unusable > ovp_holes)
+               return unusable - ovp_holes;
+       return 0;
+}
+
+int f2fs_disable_cp_again(struct f2fs_sb_info *sbi, block_t unusable)
+{
+       int ovp_hole_segs =
+               (overprovision_segments(sbi) - reserved_segments(sbi));
+       if (unusable > F2FS_OPTION(sbi).unusable_cap)
+               return -EAGAIN;
+       if (is_sbi_flag_set(sbi, SBI_CP_DISABLED_QUICK) &&
+               dirty_segments(sbi) > ovp_hole_segs)
+               return -EAGAIN;
+       return 0;
+}
+
+/* This is only used by SBI_CP_DISABLED */
+static unsigned int get_free_segment(struct f2fs_sb_info *sbi)
+{
+       struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
+       unsigned int segno = 0;
+
+       mutex_lock(&dirty_i->seglist_lock);
+       for_each_set_bit(segno, dirty_i->dirty_segmap[DIRTY], MAIN_SEGS(sbi)) {
+               if (get_valid_blocks(sbi, segno, false))
+                       continue;
+               if (get_ckpt_valid_blocks(sbi, segno))
+                       continue;
+               mutex_unlock(&dirty_i->seglist_lock);
+               return segno;
+       }
+       mutex_unlock(&dirty_i->seglist_lock);
+       return NULL_SEGNO;
+}
+
 static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi,
                struct block_device *bdev, block_t lstart,
                block_t start, block_t len)
@@ -822,9 +951,12 @@ static struct discard_cmd *__create_discard_cmd(struct f2fs_sb_info *sbi,
        dc->len = len;
        dc->ref = 0;
        dc->state = D_PREP;
+       dc->queued = 0;
        dc->error = 0;
        init_completion(&dc->wait);
        list_add_tail(&dc->list, pend_list);
+       spin_lock_init(&dc->lock);
+       dc->bio_ref = 0;
        atomic_inc(&dcc->discard_cmd_cnt);
        dcc->undiscard_blks += len;
 
@@ -851,7 +983,7 @@ static void __detach_discard_cmd(struct discard_cmd_control *dcc,
                                                        struct discard_cmd *dc)
 {
        if (dc->state == D_DONE)
-               atomic_dec(&dcc->issing_discard);
+               atomic_sub(dc->queued, &dcc->queued_discard);
 
        list_del(&dc->list);
        rb_erase(&dc->rb_node, &dcc->root);
@@ -866,28 +998,43 @@ static void __remove_discard_cmd(struct f2fs_sb_info *sbi,
                                                        struct discard_cmd *dc)
 {
        struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       unsigned long flags;
 
        trace_f2fs_remove_discard(dc->bdev, dc->start, dc->len);
 
+       spin_lock_irqsave(&dc->lock, flags);
+       if (dc->bio_ref) {
+               spin_unlock_irqrestore(&dc->lock, flags);
+               return;
+       }
+       spin_unlock_irqrestore(&dc->lock, flags);
+
        f2fs_bug_on(sbi, dc->ref);
 
        if (dc->error == -EOPNOTSUPP)
                dc->error = 0;
 
        if (dc->error)
-               f2fs_msg(sbi->sb, KERN_INFO,
-                       "Issue discard(%u, %u, %u) failed, ret: %d",
-                       dc->lstart, dc->start, dc->len, dc->error);
+               printk_ratelimited(
+                       "%sF2FS-fs: Issue discard(%u, %u, %u) failed, ret: %d",
+                       KERN_INFO, dc->lstart, dc->start, dc->len, dc->error);
        __detach_discard_cmd(dcc, dc);
 }
 
 static void f2fs_submit_discard_endio(struct bio *bio)
 {
        struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private;
+       unsigned long flags;
 
        dc->error = bio->bi_error;
-       dc->state = D_DONE;
-       complete_all(&dc->wait);
+
+       spin_lock_irqsave(&dc->lock, flags);
+       dc->bio_ref--;
+       if (!dc->bio_ref && dc->state == D_SUBMIT) {
+               dc->state = D_DONE;
+               complete_all(&dc->wait);
+       }
+       spin_unlock_irqrestore(&dc->lock, flags);
        bio_put(bio);
 }
 
@@ -1006,69 +1153,147 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi,
        /* common policy */
        dpolicy->type = discard_type;
        dpolicy->sync = true;
+       dpolicy->ordered = false;
        dpolicy->granularity = granularity;
 
        dpolicy->max_requests = DEF_MAX_DISCARD_REQUEST;
        dpolicy->io_aware_gran = MAX_PLIST_NUM;
+       dpolicy->timeout = 0;
 
        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;
+               dpolicy->ordered = true;
                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;
+               /* we need to issue all to keep CP_TRIMMED_FLAG */
+               dpolicy->granularity = 1;
        }
 }
 
-
+static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
+                               struct block_device *bdev, block_t lstart,
+                               block_t start, block_t len);
 /* this function is copied from blkdev_issue_discard from block/blk-lib.c */
-static void __submit_discard_cmd(struct f2fs_sb_info *sbi,
+static int __submit_discard_cmd(struct f2fs_sb_info *sbi,
                                                struct discard_policy *dpolicy,
-                                               struct discard_cmd *dc)
+                                               struct discard_cmd *dc,
+                                               unsigned int *issued)
 {
+       struct block_device *bdev = dc->bdev;
+       struct request_queue *q = bdev_get_queue(bdev);
+       unsigned int max_discard_blocks =
+                       SECTOR_TO_BLOCK(q->limits.max_discard_sectors);
        struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
        struct list_head *wait_list = (dpolicy->type == DPOLICY_FSTRIM) ?
                                        &(dcc->fstrim_list) : &(dcc->wait_list);
-       struct bio *bio = NULL;
        int flag = dpolicy->sync ? REQ_SYNC : 0;
+       block_t lstart, start, len, total_len;
+       int err = 0;
 
        if (dc->state != D_PREP)
-               return;
+               return 0;
 
-       trace_f2fs_issue_discard(dc->bdev, dc->start, dc->len);
+       if (is_sbi_flag_set(sbi, SBI_NEED_FSCK))
+               return 0;
 
-       dc->error = __blkdev_issue_discard(dc->bdev,
-                               SECTOR_FROM_BLOCK(dc->start),
-                               SECTOR_FROM_BLOCK(dc->len),
-                               GFP_NOFS, 0, &bio);
-       if (!dc->error) {
-               /* should keep before submission to avoid D_DONE right away */
-               dc->state = D_SUBMIT;
-               atomic_inc(&dcc->issued_discard);
-               atomic_inc(&dcc->issing_discard);
-               if (bio) {
-                       bio->bi_private = dc;
-                       bio->bi_end_io = f2fs_submit_discard_endio;
-                       submit_bio(flag, bio);
-                       list_move_tail(&dc->list, wait_list);
-                       __check_sit_bitmap(sbi, dc->start, dc->start + dc->len);
+       trace_f2fs_issue_discard(bdev, dc->start, dc->len);
+
+       lstart = dc->lstart;
+       start = dc->start;
+       len = dc->len;
+       total_len = len;
+
+       dc->len = 0;
 
-                       f2fs_update_iostat(sbi, FS_DISCARD, 1);
+       while (total_len && *issued < dpolicy->max_requests && !err) {
+               struct bio *bio = NULL;
+               unsigned long flags;
+               bool last = true;
+
+               if (len > max_discard_blocks) {
+                       len = max_discard_blocks;
+                       last = false;
                }
-       } else {
-               __remove_discard_cmd(sbi, dc);
+
+               (*issued)++;
+               if (*issued == dpolicy->max_requests)
+                       last = true;
+
+               dc->len += len;
+
+               if (time_to_inject(sbi, FAULT_DISCARD)) {
+                       f2fs_show_injection_info(FAULT_DISCARD);
+                       err = -EIO;
+                       goto submit;
+               }
+               err = __blkdev_issue_discard(bdev,
+                                       SECTOR_FROM_BLOCK(start),
+                                       SECTOR_FROM_BLOCK(len),
+                                       GFP_NOFS, 0, &bio);
+submit:
+               if (err) {
+                       spin_lock_irqsave(&dc->lock, flags);
+                       if (dc->state == D_PARTIAL)
+                               dc->state = D_SUBMIT;
+                       spin_unlock_irqrestore(&dc->lock, flags);
+
+                       break;
+               }
+
+               f2fs_bug_on(sbi, !bio);
+
+               /*
+                * should keep before submission to avoid D_DONE
+                * right away
+                */
+               spin_lock_irqsave(&dc->lock, flags);
+               if (last)
+                       dc->state = D_SUBMIT;
+               else
+                       dc->state = D_PARTIAL;
+               dc->bio_ref++;
+               spin_unlock_irqrestore(&dc->lock, flags);
+
+               atomic_inc(&dcc->queued_discard);
+               dc->queued++;
+               list_move_tail(&dc->list, wait_list);
+
+               /* sanity check on discard range */
+               __check_sit_bitmap(sbi, lstart, lstart + len);
+
+               bio->bi_private = dc;
+               bio->bi_end_io = f2fs_submit_discard_endio;
+               submit_bio(flag, bio);
+
+               atomic_inc(&dcc->issued_discard);
+
+               f2fs_update_iostat(sbi, FS_DISCARD, 1);
+
+               lstart += len;
+               start += len;
+               total_len -= len;
+               len = total_len;
        }
+
+       if (!err && len)
+               __update_discard_tree_range(sbi, bdev, lstart, start, len);
+       return err;
 }
 
 static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,
@@ -1088,7 +1313,7 @@ static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,
                goto do_insert;
        }
 
-       p = __lookup_rb_tree_for_insert(sbi, &dcc->root, &parent, lstart);
+       p = f2fs_lookup_rb_tree_for_insert(sbi, &dcc->root, &parent, lstart);
 do_insert:
        dc = __attach_discard_cmd(sbi, bdev, lstart, start, len, parent, p);
        if (!dc)
@@ -1149,11 +1374,12 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
        struct discard_cmd *dc;
        struct discard_info di = {0};
        struct rb_node **insert_p = NULL, *insert_parent = NULL;
+       struct request_queue *q = bdev_get_queue(bdev);
+       unsigned int max_discard_blocks =
+                       SECTOR_TO_BLOCK(q->limits.max_discard_sectors);
        block_t end = lstart + len;
 
-       mutex_lock(&dcc->cmd_lock);
-
-       dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root,
+       dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root,
                                        NULL, lstart,
                                        (struct rb_entry **)&prev_dc,
                                        (struct rb_entry **)&next_dc,
@@ -1192,7 +1418,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
 
                if (prev_dc && prev_dc->state == D_PREP &&
                        prev_dc->bdev == bdev &&
-                       __is_discard_back_mergeable(&di, &prev_dc->di)) {
+                       __is_discard_back_mergeable(&di, &prev_dc->di,
+                                                       max_discard_blocks)) {
                        prev_dc->di.len += di.len;
                        dcc->undiscard_blks += di.len;
                        __relocate_discard_cmd(dcc, prev_dc);
@@ -1203,7 +1430,8 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
 
                if (next_dc && next_dc->state == D_PREP &&
                        next_dc->bdev == bdev &&
-                       __is_discard_front_mergeable(&di, &next_dc->di)) {
+                       __is_discard_front_mergeable(&di, &next_dc->di,
+                                                       max_discard_blocks)) {
                        next_dc->di.lstart = di.lstart;
                        next_dc->di.len += di.len;
                        next_dc->di.start = di.start;
@@ -1226,8 +1454,6 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
                node = rb_next(&prev_dc->rb_node);
                next_dc = rb_entry_safe(node, struct discard_cmd, rb_node);
        }
-
-       mutex_unlock(&dcc->cmd_lock);
 }
 
 static int __queue_discard_cmd(struct f2fs_sb_info *sbi,
@@ -1235,17 +1461,82 @@ static int __queue_discard_cmd(struct f2fs_sb_info *sbi,
 {
        block_t lblkstart = blkstart;
 
+       if (!f2fs_bdev_support_discard(bdev))
+               return 0;
+
        trace_f2fs_queue_discard(bdev, blkstart, blklen);
 
-       if (sbi->s_ndevs) {
+       if (f2fs_is_multi_device(sbi)) {
                int devi = f2fs_target_device_index(sbi, blkstart);
 
                blkstart -= FDEV(devi).start_blk;
        }
+       mutex_lock(&SM_I(sbi)->dcc_info->cmd_lock);
        __update_discard_tree_range(sbi, bdev, lblkstart, blkstart, blklen);
+       mutex_unlock(&SM_I(sbi)->dcc_info->cmd_lock);
        return 0;
 }
 
+static unsigned int __issue_discard_cmd_orderly(struct f2fs_sb_info *sbi,
+                                       struct discard_policy *dpolicy)
+{
+       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;
+       unsigned int pos = dcc->next_pos;
+       unsigned int issued = 0;
+       bool io_interrupted = false;
+
+       mutex_lock(&dcc->cmd_lock);
+       dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root,
+                                       NULL, pos,
+                                       (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) {
+               struct rb_node *node;
+               int err = 0;
+
+               if (dc->state != D_PREP)
+                       goto next;
+
+               if (dpolicy->io_aware && !is_idle(sbi, DISCARD_TIME)) {
+                       io_interrupted = true;
+                       break;
+               }
+
+               dcc->next_pos = dc->lstart + dc->len;
+               err = __submit_discard_cmd(sbi, dpolicy, dc, &issued);
+
+               if (issued >= dpolicy->max_requests)
+                       break;
+next:
+               node = rb_next(&dc->rb_node);
+               if (err)
+                       __remove_discard_cmd(sbi, dc);
+               dc = rb_entry_safe(node, struct discard_cmd, rb_node);
+       }
+
+       blk_finish_plug(&plug);
+
+       if (!dc)
+               dcc->next_pos = 0;
+
+       mutex_unlock(&dcc->cmd_lock);
+
+       if (!issued && io_interrupted)
+               issued = -1;
+
+       return issued;
+}
+
 static int __issue_discard_cmd(struct f2fs_sb_info *sbi,
                                        struct discard_policy *dpolicy)
 {
@@ -1253,39 +1544,55 @@ static int __issue_discard_cmd(struct f2fs_sb_info *sbi,
        struct list_head *pend_list;
        struct discard_cmd *dc, *tmp;
        struct blk_plug plug;
-       int i, iter = 0, issued = 0;
+       int i, issued = 0;
        bool io_interrupted = false;
 
+       if (dpolicy->timeout != 0)
+               f2fs_update_time(sbi, dpolicy->timeout);
+
        for (i = MAX_PLIST_NUM - 1; i >= 0; i--) {
+               if (dpolicy->timeout != 0 &&
+                               f2fs_time_over(sbi, dpolicy->timeout))
+                       break;
+
                if (i + 1 < dpolicy->granularity)
                        break;
+
+               if (i < DEFAULT_DISCARD_GRANULARITY && dpolicy->ordered)
+                       return __issue_discard_cmd_orderly(sbi, dpolicy);
+
                pend_list = &dcc->pend_list[i];
 
                mutex_lock(&dcc->cmd_lock);
                if (list_empty(pend_list))
                        goto next;
-               f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root));
+               if (unlikely(dcc->rbtree_check))
+                       f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi,
+                                                               &dcc->root));
                blk_start_plug(&plug);
                list_for_each_entry_safe(dc, tmp, pend_list, list) {
                        f2fs_bug_on(sbi, dc->state != D_PREP);
 
+                       if (dpolicy->timeout != 0 &&
+                               f2fs_time_over(sbi, dpolicy->timeout))
+                               break;
+
                        if (dpolicy->io_aware && i < dpolicy->io_aware_gran &&
-                                                               !is_idle(sbi)) {
+                                               !is_idle(sbi, DISCARD_TIME)) {
                                io_interrupted = true;
-                               goto skip;
+                               break;
                        }
 
-                       __submit_discard_cmd(sbi, dpolicy, dc);
-                       issued++;
-skip:
-                       if (++iter >= dpolicy->max_requests)
+                       __submit_discard_cmd(sbi, dpolicy, dc, &issued);
+
+                       if (issued >= dpolicy->max_requests)
                                break;
                }
                blk_finish_plug(&plug);
 next:
                mutex_unlock(&dcc->cmd_lock);
 
-               if (iter >= dpolicy->max_requests)
+               if (issued >= dpolicy->max_requests || io_interrupted)
                        break;
        }
 
@@ -1317,7 +1624,7 @@ static bool __drop_discard_cmd(struct f2fs_sb_info *sbi)
        return dropped;
 }
 
-void drop_discard_cmd(struct f2fs_sb_info *sbi)
+void f2fs_drop_discard_cmd(struct f2fs_sb_info *sbi)
 {
        __drop_discard_cmd(sbi);
 }
@@ -1383,21 +1690,22 @@ next:
        return trimmed;
 }
 
-static void __wait_all_discard_cmd(struct f2fs_sb_info *sbi,
+static unsigned int __wait_all_discard_cmd(struct f2fs_sb_info *sbi,
                                                struct discard_policy *dpolicy)
 {
        struct discard_policy dp;
+       unsigned int discard_blks;
 
-       if (dpolicy) {
-               __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX);
-               return;
-       }
+       if (dpolicy)
+               return __wait_discard_cmd_range(sbi, dpolicy, 0, UINT_MAX);
 
        /* wait all */
        __init_discard_policy(sbi, &dp, DPOLICY_FSTRIM, 1);
-       __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
+       discard_blks = __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);
+       discard_blks += __wait_discard_cmd_range(sbi, &dp, 0, UINT_MAX);
+
+       return discard_blks;
 }
 
 /* This should be covered by global mutex, &sit_i->sentry_lock */
@@ -1408,7 +1716,8 @@ static void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr)
        bool need_wait = false;
 
        mutex_lock(&dcc->cmd_lock);
-       dc = (struct discard_cmd *)__lookup_rb_tree(&dcc->root, NULL, blkaddr);
+       dc = (struct discard_cmd *)f2fs_lookup_rb_tree(&dcc->root,
+                                                       NULL, blkaddr);
        if (dc) {
                if (dc->state == D_PREP) {
                        __punch_discard_cmd(sbi, dc, blkaddr);
@@ -1423,7 +1732,7 @@ static void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr)
                __wait_one_discard_bio(sbi, dc);
 }
 
-void stop_discard_thread(struct f2fs_sb_info *sbi)
+void f2fs_stop_discard_thread(struct f2fs_sb_info *sbi)
 {
        struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
 
@@ -1436,7 +1745,7 @@ void stop_discard_thread(struct f2fs_sb_info *sbi)
 }
 
 /* This comes from f2fs_put_super */
-bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi)
+bool f2fs_issue_discard_timeout(struct f2fs_sb_info *sbi)
 {
        struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
        struct discard_policy dpolicy;
@@ -1444,11 +1753,14 @@ bool f2fs_wait_discard_bios(struct f2fs_sb_info *sbi)
 
        __init_discard_policy(sbi, &dpolicy, DPOLICY_UMOUNT,
                                        dcc->discard_granularity);
+       dpolicy.timeout = UMOUNT_DISCARD_TIMEOUT;
        __issue_discard_cmd(sbi, &dpolicy);
        dropped = __drop_discard_cmd(sbi);
 
        /* just to make sure there is no pending discard commands */
        __wait_all_discard_cmd(sbi, NULL);
+
+       f2fs_bug_on(sbi, atomic_read(&dcc->discard_cmd_cnt));
        return dropped;
 }
 
@@ -1471,25 +1783,38 @@ static int issue_discard_thread(void *data)
                                kthread_should_stop() || freezing(current) ||
                                dcc->discard_wake,
                                msecs_to_jiffies(wait_ms));
+
+               if (dcc->discard_wake)
+                       dcc->discard_wake = 0;
+
+               /* clean up pending candidates before going to sleep */
+               if (atomic_read(&dcc->queued_discard))
+                       __wait_all_discard_cmd(sbi, NULL);
+
                if (try_to_freeze())
                        continue;
                if (f2fs_readonly(sbi->sb))
                        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)
+               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 = f2fs_time_to_wait(sbi, DISCARD_TIME);
+                       if (!wait_ms)
+                               wait_ms = dpolicy.mid_interval;
                } else {
                        wait_ms = dpolicy.max_interval;
                }
@@ -1508,42 +1833,34 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
        block_t lblkstart = blkstart;
        int devi = 0;
 
-       if (sbi->s_ndevs) {
+       if (f2fs_is_multi_device(sbi)) {
                devi = f2fs_target_device_index(sbi, blkstart);
+               if (blkstart < FDEV(devi).start_blk ||
+                   blkstart > FDEV(devi).end_blk) {
+                       f2fs_err(sbi, "Invalid block %x", blkstart);
+                       return -EIO;
+               }
                blkstart -= FDEV(devi).start_blk;
        }
 
-       /*
-        * We need to know the type of the zone: for conventional zones,
-        * use regular discard if the drive supports it. For sequential
-        * zones, reset the zone write pointer.
-        */
-       switch (get_blkz_type(sbi, bdev, blkstart)) {
-
-       case BLK_ZONE_TYPE_CONVENTIONAL:
-               if (!blk_queue_discard(bdev_get_queue(bdev)))
-                       return 0;
-               return __queue_discard_cmd(sbi, bdev, lblkstart, blklen);
-       case BLK_ZONE_TYPE_SEQWRITE_REQ:
-       case BLK_ZONE_TYPE_SEQWRITE_PREF:
+       /* For sequential zones, reset the zone write pointer */
+       if (f2fs_blkz_is_seq(sbi, devi, blkstart)) {
                sector = SECTOR_FROM_BLOCK(blkstart);
                nr_sects = SECTOR_FROM_BLOCK(blklen);
 
                if (sector & (bdev_zone_sectors(bdev) - 1) ||
                                nr_sects != bdev_zone_sectors(bdev)) {
-                       f2fs_msg(sbi->sb, KERN_INFO,
-                               "(%d) %s: Unaligned discard attempted (block %x + %x)",
-                               devi, sbi->s_ndevs ? FDEV(devi).path: "",
-                               blkstart, blklen);
+                       f2fs_err(sbi, "(%d) %s: Unaligned zone reset attempted (block %x + %x)",
+                                devi, sbi->s_ndevs ? FDEV(devi).path : "",
+                                blkstart, blklen);
                        return -EIO;
                }
                trace_f2fs_issue_reset_zone(bdev, blkstart);
-               return blkdev_reset_zones(bdev, sector,
-                                         nr_sects, GFP_NOFS);
-       default:
-               /* Unknown zone type: broken device ? */
-               return -EIO;
+               return blkdev_reset_zones(bdev, sector, nr_sects, GFP_NOFS);
        }
+
+       /* For conventional zones, use regular discard if supported */
+       return __queue_discard_cmd(sbi, bdev, lblkstart, blklen);
 }
 #endif
 
@@ -1551,8 +1868,7 @@ static int __issue_discard_async(struct f2fs_sb_info *sbi,
                struct block_device *bdev, block_t blkstart, block_t blklen)
 {
 #ifdef CONFIG_BLK_DEV_ZONED
-       if (f2fs_sb_has_blkzoned(sbi->sb) &&
-                               bdev_zoned_model(bdev) != BLK_ZONED_NONE)
+       if (f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(bdev))
                return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen);
 #endif
        return __queue_discard_cmd(sbi, bdev, blkstart, blklen);
@@ -1614,11 +1930,11 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc,
        struct list_head *head = &SM_I(sbi)->dcc_info->entry_list;
        int i;
 
-       if (se->valid_blocks == max_blocks || !f2fs_discard_en(sbi))
+       if (se->valid_blocks == max_blocks || !f2fs_hw_support_discard(sbi))
                return false;
 
        if (!force) {
-               if (!test_opt(sbi, DISCARD) || !se->valid_blocks ||
+               if (!f2fs_realtime_discard_enable(sbi) || !se->valid_blocks ||
                        SM_I(sbi)->dcc_info->nr_discards >=
                                SM_I(sbi)->dcc_info->max_discards)
                        return false;
@@ -1658,20 +1974,24 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc,
        return false;
 }
 
-void release_discard_addrs(struct f2fs_sb_info *sbi)
+static void release_discard_addr(struct discard_entry *entry)
+{
+       list_del(&entry->list);
+       kmem_cache_free(discard_entry_slab, entry);
+}
+
+void f2fs_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);
 }
 
 /*
- * Should call clear_prefree_segments after checkpoint is done.
+ * Should call f2fs_clear_prefree_segments after checkpoint is done.
  */
 static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
 {
@@ -1684,7 +2004,8 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
        mutex_unlock(&dirty_i->seglist_lock);
 }
 
-void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
+void f2fs_clear_prefree_segments(struct f2fs_sb_info *sbi,
+                                               struct cp_control *cpc)
 {
        struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
        struct list_head *head = &dcc->entry_list;
@@ -1694,30 +2015,39 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        unsigned int start = 0, end = -1;
        unsigned int secno, start_segno;
        bool force = (cpc->reason & CP_DISCARD);
+       bool need_align = test_opt(sbi, LFS) && __is_large_section(sbi);
 
        mutex_lock(&dirty_i->seglist_lock);
 
        while (1) {
                int i;
+
+               if (need_align && end != -1)
+                       end--;
                start = find_next_bit(prefree_map, MAIN_SEGS(sbi), end + 1);
                if (start >= MAIN_SEGS(sbi))
                        break;
                end = find_next_zero_bit(prefree_map, MAIN_SEGS(sbi),
                                                                start + 1);
 
-               for (i = start; i < end; i++)
-                       clear_bit(i, prefree_map);
+               if (need_align) {
+                       start = rounddown(start, sbi->segs_per_sec);
+                       end = roundup(end, sbi->segs_per_sec);
+               }
 
-               dirty_i->nr_dirty[PRE] -= end - start;
+               for (i = start; i < end; i++) {
+                       if (test_and_clear_bit(i, prefree_map))
+                               dirty_i->nr_dirty[PRE]--;
+               }
 
-               if (!test_opt(sbi, DISCARD))
+               if (!f2fs_realtime_discard_enable(sbi))
                        continue;
 
                if (force && start >= cpc->trim_start &&
                                        (end - 1) <= cpc->trim_end)
                                continue;
 
-               if (!test_opt(sbi, LFS) || sbi->segs_per_sec == 1) {
+               if (!test_opt(sbi, LFS) || !__is_large_section(sbi)) {
                        f2fs_issue_discard(sbi, START_BLOCK(sbi, start),
                                (end - start) << sbi->log_blocks_per_seg);
                        continue;
@@ -1749,7 +2079,7 @@ find_next:
                                        sbi->blocks_per_seg, cur_pos);
                        len = next_pos - cur_pos;
 
-                       if (f2fs_sb_has_blkzoned(sbi->sb) ||
+                       if (f2fs_sb_has_blkzoned(sbi) ||
                            (force && len < cpc->trim_minlen))
                                goto skip;
 
@@ -1767,9 +2097,8 @@ 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);
@@ -1798,12 +2127,14 @@ static int create_discard_cmd_control(struct f2fs_sb_info *sbi)
        INIT_LIST_HEAD(&dcc->fstrim_list);
        mutex_init(&dcc->cmd_lock);
        atomic_set(&dcc->issued_discard, 0);
-       atomic_set(&dcc->issing_discard, 0);
+       atomic_set(&dcc->queued_discard, 0);
        atomic_set(&dcc->discard_cmd_cnt, 0);
        dcc->nr_discards = 0;
        dcc->max_discards = MAIN_SEGS(sbi) << sbi->log_blocks_per_seg;
        dcc->undiscard_blks = 0;
+       dcc->next_pos = 0;
        dcc->root = RB_ROOT;
+       dcc->rbtree_check = false;
 
        init_waitqueue_head(&dcc->discard_wait_queue);
        SM_I(sbi)->dcc_info = dcc;
@@ -1812,7 +2143,7 @@ init_thread:
                                "f2fs_discard-%u:%u", MAJOR(dev), MINOR(dev));
        if (IS_ERR(dcc->f2fs_issue_discard)) {
                err = PTR_ERR(dcc->f2fs_issue_discard);
-               kfree(dcc);
+               kvfree(dcc);
                SM_I(sbi)->dcc_info = NULL;
                return err;
        }
@@ -1827,9 +2158,9 @@ static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi)
        if (!dcc)
                return;
 
-       stop_discard_thread(sbi);
+       f2fs_stop_discard_thread(sbi);
 
-       kfree(dcc);
+       kvfree(dcc);
        SM_I(sbi)->dcc_info = NULL;
 }
 
@@ -1874,8 +2205,9 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
                                (new_vblocks > sbi->blocks_per_seg)));
 
        se->valid_blocks = new_vblocks;
-       se->mtime = get_mtime(sbi);
-       SIT_I(sbi)->max_mtime = se->mtime;
+       se->mtime = get_mtime(sbi, false);
+       if (se->mtime > SIT_I(sbi)->max_mtime)
+               SIT_I(sbi)->max_mtime = se->mtime;
 
        /* Update valid block bitmap */
        if (del > 0) {
@@ -1884,26 +2216,25 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
                mir_exist = f2fs_test_and_set_bit(offset,
                                                se->cur_valid_map_mir);
                if (unlikely(exist != mir_exist)) {
-                       f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error "
-                               "when setting bitmap, blk:%u, old bit:%d",
-                               blkaddr, exist);
+                       f2fs_err(sbi, "Inconsistent error when setting bitmap, blk:%u, old bit:%d",
+                                blkaddr, exist);
                        f2fs_bug_on(sbi, 1);
                }
 #endif
                if (unlikely(exist)) {
-                       f2fs_msg(sbi->sb, KERN_ERR,
-                               "Bitmap was wrongly set, blk:%u", blkaddr);
+                       f2fs_err(sbi, "Bitmap was wrongly set, blk:%u",
+                                blkaddr);
                        f2fs_bug_on(sbi, 1);
                        se->valid_blocks--;
                        del = 0;
                }
 
-               if (f2fs_discard_en(sbi) &&
-                       !f2fs_test_and_set_bit(offset, se->discard_map))
+               if (!f2fs_test_and_set_bit(offset, se->discard_map))
                        sbi->discard_blks--;
 
                /* don't overwrite by SSR to keep node chain */
-               if (IS_NODESEG(se->type)) {
+               if (IS_NODESEG(se->type) &&
+                               !is_sbi_flag_set(sbi, SBI_CP_DISABLED)) {
                        if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map))
                                se->ckpt_valid_blocks++;
                }
@@ -1913,22 +2244,32 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
                mir_exist = f2fs_test_and_clear_bit(offset,
                                                se->cur_valid_map_mir);
                if (unlikely(exist != mir_exist)) {
-                       f2fs_msg(sbi->sb, KERN_ERR, "Inconsistent error "
-                               "when clearing bitmap, blk:%u, old bit:%d",
-                               blkaddr, exist);
+                       f2fs_err(sbi, "Inconsistent error when clearing bitmap, blk:%u, old bit:%d",
+                                blkaddr, exist);
                        f2fs_bug_on(sbi, 1);
                }
 #endif
                if (unlikely(!exist)) {
-                       f2fs_msg(sbi->sb, KERN_ERR,
-                               "Bitmap was wrongly cleared, blk:%u", blkaddr);
+                       f2fs_err(sbi, "Bitmap was wrongly cleared, blk:%u",
+                                blkaddr);
                        f2fs_bug_on(sbi, 1);
                        se->valid_blocks++;
                        del = 0;
+               } else if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
+                       /*
+                        * If checkpoints are off, we must not reuse data that
+                        * was used in the previous checkpoint. If it was used
+                        * before, we must track that to know how much space we
+                        * really have.
+                        */
+                       if (f2fs_test_bit(offset, se->ckpt_valid_map)) {
+                               spin_lock(&sbi->stat_lock);
+                               sbi->unusable_block_count++;
+                               spin_unlock(&sbi->stat_lock);
+                       }
                }
 
-               if (f2fs_discard_en(sbi) &&
-                       f2fs_test_and_clear_bit(offset, se->discard_map))
+               if (f2fs_test_and_clear_bit(offset, se->discard_map))
                        sbi->discard_blks++;
        }
        if (!f2fs_test_bit(offset, se->ckpt_valid_map))
@@ -1939,11 +2280,11 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
        /* update total number of valid blocks to be written in ckpt area */
        SIT_I(sbi)->written_valid_blocks += del;
 
-       if (sbi->segs_per_sec > 1)
+       if (__is_large_section(sbi))
                get_sec_entry(sbi, segno)->valid_blocks += del;
 }
 
-void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
+void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
 {
        unsigned int segno = GET_SEGNO(sbi, addr);
        struct sit_info *sit_i = SIT_I(sbi);
@@ -1952,6 +2293,8 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
        if (addr == NEW_ADDR)
                return;
 
+       invalidate_mapping_pages(META_MAPPING(sbi), addr, addr);
+
        /* add it into sit main buffer */
        down_write(&sit_i->sentry_lock);
 
@@ -1963,14 +2306,14 @@ void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
        up_write(&sit_i->sentry_lock);
 }
 
-bool is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr)
+bool f2fs_is_checkpointed_data(struct f2fs_sb_info *sbi, block_t blkaddr)
 {
        struct sit_info *sit_i = SIT_I(sbi);
        unsigned int segno, offset;
        struct seg_entry *se;
        bool is_cp = false;
 
-       if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR)
+       if (!__is_valid_data_blkaddr(blkaddr))
                return true;
 
        down_read(&sit_i->sentry_lock);
@@ -2002,7 +2345,7 @@ static void __add_sum_entry(struct f2fs_sb_info *sbi, int type,
 /*
  * Calculate the number of current summary pages for writing
  */
-int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra)
+int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra)
 {
        int valid_sum_count = 0;
        int i, sum_in_page;
@@ -2032,14 +2375,15 @@ int npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra)
 /*
  * Caller should put this summary page
  */
-struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno)
+struct page *f2fs_get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno)
 {
-       return get_meta_page(sbi, GET_SUM_BLOCK(sbi, segno));
+       return f2fs_get_meta_page_nofail(sbi, GET_SUM_BLOCK(sbi, segno));
 }
 
-void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr)
+void f2fs_update_meta_page(struct f2fs_sb_info *sbi,
+                                       void *src, block_t blk_addr)
 {
-       struct page *page = grab_meta_page(sbi, blk_addr);
+       struct page *page = f2fs_grab_meta_page(sbi, blk_addr);
 
        memcpy(page_address(page), src, PAGE_SIZE);
        set_page_dirty(page);
@@ -2049,18 +2393,19 @@ void update_meta_page(struct f2fs_sb_info *sbi, void *src, block_t blk_addr)
 static void write_sum_page(struct f2fs_sb_info *sbi,
                        struct f2fs_summary_block *sum_blk, block_t blk_addr)
 {
-       update_meta_page(sbi, (void *)sum_blk, blk_addr);
+       f2fs_update_meta_page(sbi, (void *)sum_blk, blk_addr);
 }
 
 static void write_current_sum_page(struct f2fs_sb_info *sbi,
                                                int type, block_t blk_addr)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, type);
-       struct page *page = grab_meta_page(sbi, blk_addr);
+       struct page *page = f2fs_grab_meta_page(sbi, blk_addr);
        struct f2fs_summary_block *src = curseg->sum_blk;
        struct f2fs_summary_block *dst;
 
        dst = (struct f2fs_summary_block *)page_address(page);
+       memset(dst, 0, PAGE_SIZE);
 
        mutex_lock(&curseg->curseg_mutex);
 
@@ -2201,9 +2546,12 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified)
 static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type)
 {
        /* if segs_per_sec is large than 1, we need to keep original policy. */
-       if (sbi->segs_per_sec != 1)
+       if (__is_large_section(sbi))
                return CURSEG_I(sbi, type)->segno;
 
+       if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
+               return 0;
+
        if (test_opt(sbi, NOHEAP) &&
                (type == CURSEG_HOT_DATA || IS_NODESEG(type)))
                return 0;
@@ -2300,7 +2648,8 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type)
        curseg->alloc_type = SSR;
        __next_free_blkoff(sbi, curseg, 0);
 
-       sum_page = get_sum_page(sbi, new_segno);
+       sum_page = f2fs_get_sum_page(sbi, new_segno);
+       f2fs_bug_on(sbi, IS_ERR(sum_page));
        sum_node = (struct f2fs_summary_block *)page_address(sum_page);
        memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE);
        f2fs_put_page(sum_page, 1);
@@ -2314,7 +2663,7 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type)
        int i, cnt;
        bool reversed = false;
 
-       /* need_SSR() already forces to do this */
+       /* f2fs_need_SSR() already forces to do this */
        if (v_ops->get_victim(sbi, &segno, BG_GC, type, SSR)) {
                curseg->next_segno = segno;
                return 1;
@@ -2347,6 +2696,15 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type)
                        return 1;
                }
        }
+
+       /* find valid_blocks=0 in dirty list */
+       if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED))) {
+               segno = get_free_segment(sbi);
+               if (segno != NULL_SEGNO) {
+                       curseg->next_segno = segno;
+                       return 1;
+               }
+       }
        return 0;
 }
 
@@ -2364,9 +2722,10 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
        else if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) &&
                                        type == CURSEG_WARM_NODE)
                new_curseg(sbi, type, false);
-       else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type))
+       else if (curseg->alloc_type == LFS && is_next_segment_free(sbi, type) &&
+                       likely(!is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
                new_curseg(sbi, type, false);
-       else if (need_SSR(sbi) && get_ssr_segment(sbi, type))
+       else if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type))
                change_curseg(sbi, type);
        else
                new_curseg(sbi, type, false);
@@ -2374,7 +2733,40 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
        stat_inc_seg_type(sbi, curseg);
 }
 
-void allocate_new_segments(struct f2fs_sb_info *sbi)
+void allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type,
+                                       unsigned int start, unsigned int end)
+{
+       struct curseg_info *curseg = CURSEG_I(sbi, type);
+       unsigned int segno;
+
+       down_read(&SM_I(sbi)->curseg_lock);
+       mutex_lock(&curseg->curseg_mutex);
+       down_write(&SIT_I(sbi)->sentry_lock);
+
+       segno = CURSEG_I(sbi, type)->segno;
+       if (segno < start || segno > end)
+               goto unlock;
+
+       if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type))
+               change_curseg(sbi, type);
+       else
+               new_curseg(sbi, type, true);
+
+       stat_inc_seg_type(sbi, curseg);
+
+       locate_dirty_segment(sbi, segno);
+unlock:
+       up_write(&SIT_I(sbi)->sentry_lock);
+
+       if (segno != curseg->segno)
+               f2fs_notice(sbi, "For resize: curseg of type %d: %u ==> %u",
+                           type, segno, curseg->segno);
+
+       mutex_unlock(&curseg->curseg_mutex);
+       up_read(&SM_I(sbi)->curseg_lock);
+}
+
+void f2fs_allocate_new_segments(struct f2fs_sb_info *sbi)
 {
        struct curseg_info *curseg;
        unsigned int old_segno;
@@ -2396,7 +2788,8 @@ static const struct segment_allocation default_salloc_ops = {
        .allocate_segment = allocate_segment_by_default,
 };
 
-bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc)
+bool f2fs_exist_trim_candidates(struct f2fs_sb_info *sbi,
+                                               struct cp_control *cpc)
 {
        __u64 trim_start = cpc->trim_start;
        bool has_candidate = false;
@@ -2414,7 +2807,7 @@ 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,
+static unsigned int __issue_discard_cmd_range(struct f2fs_sb_info *sbi,
                                        struct discard_policy *dpolicy,
                                        unsigned int start, unsigned int end)
 {
@@ -2424,14 +2817,17 @@ static void __issue_discard_cmd_range(struct f2fs_sb_info *sbi,
        struct discard_cmd *dc;
        struct blk_plug plug;
        int issued;
+       unsigned int trimmed = 0;
 
 next:
        issued = 0;
 
        mutex_lock(&dcc->cmd_lock);
-       f2fs_bug_on(sbi, !__check_rb_tree_consistence(sbi, &dcc->root));
+       if (unlikely(dcc->rbtree_check))
+               f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi,
+                                                               &dcc->root));
 
-       dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root,
+       dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root,
                                        NULL, start,
                                        (struct rb_entry **)&prev_dc,
                                        (struct rb_entry **)&next_dc,
@@ -2443,6 +2839,7 @@ next:
 
        while (dc && dc->lstart <= end) {
                struct rb_node *node;
+               int err = 0;
 
                if (dc->len < dpolicy->granularity)
                        goto skip;
@@ -2452,19 +2849,24 @@ next:
                        goto skip;
                }
 
-               __submit_discard_cmd(sbi, dpolicy, dc);
+               err = __submit_discard_cmd(sbi, dpolicy, dc, &issued);
 
-               if (++issued >= dpolicy->max_requests) {
+               if (issued >= dpolicy->max_requests) {
                        start = dc->lstart + dc->len;
 
+                       if (err)
+                               __remove_discard_cmd(sbi, dc);
+
                        blk_finish_plug(&plug);
                        mutex_unlock(&dcc->cmd_lock);
-                       __wait_all_discard_cmd(sbi, NULL);
+                       trimmed += __wait_all_discard_cmd(sbi, NULL);
                        congestion_wait(BLK_RW_ASYNC, HZ/50);
                        goto next;
                }
 skip:
                node = rb_next(&dc->rb_node);
+               if (err)
+                       __remove_discard_cmd(sbi, dc);
                dc = rb_entry_safe(node, struct discard_cmd, rb_node);
 
                if (fatal_signal_pending(current))
@@ -2473,6 +2875,8 @@ skip:
 
        blk_finish_plug(&plug);
        mutex_unlock(&dcc->cmd_lock);
+
+       return trimmed;
 }
 
 int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
@@ -2485,23 +2889,27 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
        struct discard_policy dpolicy;
        unsigned long long trimmed = 0;
        int err = 0;
+       bool need_align = test_opt(sbi, LFS) && __is_large_section(sbi);
 
        if (start >= MAX_BLKADDR(sbi) || range->len < sbi->blocksize)
                return -EINVAL;
 
-       if (end <= MAIN_BLKADDR(sbi))
+       if (end < MAIN_BLKADDR(sbi))
                goto out;
 
        if (is_sbi_flag_set(sbi, SBI_NEED_FSCK)) {
-               f2fs_msg(sbi->sb, KERN_WARNING,
-                       "Found FS corruption, run fsck to fix.");
-               goto out;
+               f2fs_warn(sbi, "Found FS corruption, run fsck to fix.");
+               return -EFSCORRUPTED;
        }
 
        /* start/end segment number in main_area */
        start_segno = (start <= MAIN_BLKADDR(sbi)) ? 0 : GET_SEGNO(sbi, start);
        end_segno = (end >= MAX_BLKADDR(sbi)) ? MAIN_SEGS(sbi) - 1 :
                                                GET_SEGNO(sbi, end);
+       if (need_align) {
+               start_segno = rounddown(start_segno, sbi->segs_per_sec);
+               end_segno = roundup(end_segno + 1, sbi->segs_per_sec) - 1;
+       }
 
        cpc.reason = CP_DISCARD;
        cpc.trim_minlen = max_t(__u64, 1, F2FS_BYTES_TO_BLK(range->minlen));
@@ -2512,29 +2920,32 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
                goto out;
 
        mutex_lock(&sbi->gc_mutex);
-       err = write_checkpoint(sbi, &cpc);
+       err = f2fs_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, end_segno + 1);
-
-       __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen);
-       __issue_discard_cmd_range(sbi, &dpolicy, start_block, end_block);
-
        /*
         * 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,
+       if (f2fs_realtime_discard_enable(sbi))
+               goto out;
+
+       start_block = START_BLOCK(sbi, start_segno);
+       end_block = START_BLOCK(sbi, end_segno + 1);
+
+       __init_discard_policy(sbi, &dpolicy, DPOLICY_FSTRIM, cpc.trim_minlen);
+       trimmed = __issue_discard_cmd_range(sbi, &dpolicy,
+                                       start_block, end_block);
+
+       trimmed += __wait_discard_cmd_range(sbi, &dpolicy,
                                        start_block, end_block);
-               range->len = F2FS_BLK_TO_BYTES(trimmed);
-       }
 out:
+       if (!err)
+               range->len = F2FS_BLK_TO_BYTES(trimmed);
        return err;
 }
 
@@ -2546,7 +2957,7 @@ static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type)
        return false;
 }
 
-int rw_hint_to_seg_type(enum rw_hint hint)
+int f2fs_rw_hint_to_seg_type(enum rw_hint hint)
 {
        switch (hint) {
        case WRITE_LIFE_SHORT:
@@ -2619,7 +3030,7 @@ int rw_hint_to_seg_type(enum rw_hint hint)
  * WRITE_LIFE_LONG       "                        WRITE_LIFE_LONG
  */
 
-enum rw_hint io_type_to_rw_hint(struct f2fs_sb_info *sbi,
+enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi,
                                enum page_type type, enum temp_type temp)
 {
        if (F2FS_OPTION(sbi).whint_mode == WHINT_MODE_USER) {
@@ -2686,9 +3097,11 @@ 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) ||
+                               f2fs_is_atomic_file(inode) ||
+                               f2fs_is_volatile_file(inode))
                        return CURSEG_HOT_DATA;
-               /* rw_hint_to_seg_type(inode->i_write_hint); */
+               /* f2fs_rw_hint_to_seg_type(inode->i_write_hint); */
                return CURSEG_WARM_DATA;
        } else {
                if (IS_DNODE(fio->page))
@@ -2725,7 +3138,7 @@ static int __get_segment_type(struct f2fs_io_info *fio)
        return type;
 }
 
-void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
+void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
                block_t old_blkaddr, block_t *new_blkaddr,
                struct f2fs_summary *sum, int type,
                struct f2fs_io_info *fio, bool add_list)
@@ -2785,6 +3198,7 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
 
                INIT_LIST_HEAD(&fio->list);
                fio->in_list = true;
+               fio->retry = false;
                io = sbi->write_io[fio->type] + fio->temp;
                spin_lock(&io->io_lock);
                list_add_tail(&fio->list, &io->io_list);
@@ -2801,13 +3215,13 @@ static void update_device_state(struct f2fs_io_info *fio)
        struct f2fs_sb_info *sbi = fio->sbi;
        unsigned int devidx;
 
-       if (!sbi->s_ndevs)
+       if (!f2fs_is_multi_device(sbi))
                return;
 
        devidx = f2fs_target_device_index(sbi, fio->new_blkaddr);
 
        /* update device state for fsync */
-       set_dirty_device(sbi, fio->ino, devidx, FLUSH_INO);
+       f2fs_set_dirty_device(sbi, fio->ino, devidx, FLUSH_INO);
 
        /* update device state for checkpoint */
        if (!f2fs_test_bit(devidx, (char *)&sbi->dirty_device)) {
@@ -2820,23 +3234,31 @@ static void update_device_state(struct f2fs_io_info *fio)
 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,
+       f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
                        &fio->new_blkaddr, sum, type, fio, true);
+       if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO)
+               invalidate_mapping_pages(META_MAPPING(fio->sbi),
+                                       fio->old_blkaddr, fio->old_blkaddr);
 
        /* writeout dirty page into bdev */
-       err = f2fs_submit_page_write(fio);
-       if (err == -EAGAIN) {
+       f2fs_submit_page_write(fio);
+       if (fio->retry) {
                fio->old_blkaddr = fio->new_blkaddr;
                goto reallocate;
-       } else if (!err) {
-               update_device_state(fio);
        }
+
+       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,
+void f2fs_do_write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
                                        enum iostat_type io_type)
 {
        struct f2fs_io_info fio = {
@@ -2859,10 +3281,11 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
        ClearPageError(page);
        f2fs_submit_page_write(&fio);
 
+       stat_inc_meta_count(sbi, page->index);
        f2fs_update_iostat(sbi, io_type, F2FS_BLKSIZE);
 }
 
-void write_node_page(unsigned int nid, struct f2fs_io_info *fio)
+void f2fs_do_write_node_page(unsigned int nid, struct f2fs_io_info *fio)
 {
        struct f2fs_summary sum;
 
@@ -2872,40 +3295,49 @@ void write_node_page(unsigned int nid, struct f2fs_io_info *fio)
        f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE);
 }
 
-void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio)
+void f2fs_outplace_write_data(struct dnode_of_data *dn,
+                                       struct f2fs_io_info *fio)
 {
        struct f2fs_sb_info *sbi = fio->sbi;
        struct f2fs_summary sum;
-       struct node_info ni;
 
        f2fs_bug_on(sbi, dn->data_blkaddr == NULL_ADDR);
-       get_node_info(sbi, dn->nid, &ni);
-       set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
+       set_summary(&sum, dn->nid, dn->ofs_in_node, fio->version);
        do_write_page(&sum, fio);
        f2fs_update_data_blkaddr(dn, fio->new_blkaddr);
 
        f2fs_update_iostat(sbi, fio->io_type, F2FS_BLKSIZE);
 }
 
-int rewrite_data_page(struct f2fs_io_info *fio)
+int f2fs_inplace_write_data(struct f2fs_io_info *fio)
 {
        int err;
        struct f2fs_sb_info *sbi = fio->sbi;
+       unsigned int segno;
 
        fio->new_blkaddr = fio->old_blkaddr;
        /* i/o temperature is needed for passing down write hints */
        __get_segment_type(fio);
 
-       f2fs_bug_on(sbi, !IS_DATASEG(get_seg_entry(sbi,
-                       GET_SEGNO(sbi, fio->new_blkaddr))->type));
+       segno = GET_SEGNO(sbi, fio->new_blkaddr);
+
+       if (!IS_DATASEG(get_seg_entry(sbi, segno)->type)) {
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               f2fs_warn(sbi, "%s: incorrect segment(%u) type, run fsck to fix.",
+                         __func__, segno);
+               return -EFSCORRUPTED;
+       }
 
        stat_inc_inplace_blocks(fio->sbi);
 
-       err = f2fs_submit_page_bio(fio);
-       if (!err)
+       if (fio->bio)
+               err = f2fs_merge_page_bio(fio);
+       else
+               err = f2fs_submit_page_bio(fio);
+       if (!err) {
                update_device_state(fio);
-
-       f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE);
+               f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE);
+       }
 
        return err;
 }
@@ -2922,7 +3354,7 @@ static inline int __f2fs_get_curseg(struct f2fs_sb_info *sbi,
        return i;
 }
 
-void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
+void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
                                block_t old_blkaddr, block_t new_blkaddr,
                                bool recover_curseg, bool recover_newaddr)
 {
@@ -2977,8 +3409,11 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
 
        if (!recover_curseg || recover_newaddr)
                update_sit_entry(sbi, new_blkaddr, 1);
-       if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
+       if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) {
+               invalidate_mapping_pages(META_MAPPING(sbi),
+                                       old_blkaddr, old_blkaddr);
                update_sit_entry(sbi, old_blkaddr, -1);
+       }
 
        locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr));
        locate_dirty_segment(sbi, GET_SEGNO(sbi, new_blkaddr));
@@ -3007,42 +3442,56 @@ void f2fs_replace_block(struct f2fs_sb_info *sbi, struct dnode_of_data *dn,
 
        set_summary(&sum, dn->nid, dn->ofs_in_node, version);
 
-       __f2fs_replace_block(sbi, &sum, old_addr, new_addr,
+       f2fs_do_replace_block(sbi, &sum, old_addr, new_addr,
                                        recover_curseg, recover_newaddr);
 
        f2fs_update_data_blkaddr(dn, new_addr);
 }
 
 void f2fs_wait_on_page_writeback(struct page *page,
-                               enum page_type type, bool ordered)
+                               enum page_type type, bool ordered, bool locked)
 {
        if (PageWriteback(page)) {
                struct f2fs_sb_info *sbi = F2FS_P_SB(page);
 
-               f2fs_submit_merged_write_cond(sbi, page->mapping->host,
-                                               0, page->index, type);
-               if (ordered)
+               f2fs_submit_merged_write_cond(sbi, NULL, page, 0, type);
+               if (ordered) {
                        wait_on_page_writeback(page);
-               else
+                       f2fs_bug_on(sbi, locked && PageWriteback(page));
+               } else {
                        wait_for_stable_page(page);
+               }
        }
 }
 
-void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr)
+void f2fs_wait_on_block_writeback(struct inode *inode, block_t blkaddr)
 {
+       struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
        struct page *cpage;
 
-       if (blkaddr == NEW_ADDR || blkaddr == NULL_ADDR)
+       if (!f2fs_post_read_required(inode))
+               return;
+
+       if (!__is_valid_data_blkaddr(blkaddr))
                return;
 
        cpage = find_lock_page(META_MAPPING(sbi), blkaddr);
        if (cpage) {
-               f2fs_wait_on_page_writeback(cpage, DATA, true);
+               f2fs_wait_on_page_writeback(cpage, DATA, true, true);
                f2fs_put_page(cpage, 1);
        }
 }
 
-static void read_compacted_summaries(struct f2fs_sb_info *sbi)
+void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr,
+                                                               block_t len)
+{
+       block_t i;
+
+       for (i = 0; i < len; i++)
+               f2fs_wait_on_block_writeback(inode, blkaddr + i);
+}
+
+static int read_compacted_summaries(struct f2fs_sb_info *sbi)
 {
        struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
        struct curseg_info *seg_i;
@@ -3053,7 +3502,9 @@ static void read_compacted_summaries(struct f2fs_sb_info *sbi)
 
        start = start_sum_block(sbi);
 
-       page = get_meta_page(sbi, start++);
+       page = f2fs_get_meta_page(sbi, start++);
+       if (IS_ERR(page))
+               return PTR_ERR(page);
        kaddr = (unsigned char *)page_address(page);
 
        /* Step 1: restore nat cache */
@@ -3093,12 +3544,15 @@ static void read_compacted_summaries(struct f2fs_sb_info *sbi)
                        f2fs_put_page(page, 1);
                        page = NULL;
 
-                       page = get_meta_page(sbi, start++);
+                       page = f2fs_get_meta_page(sbi, start++);
+                       if (IS_ERR(page))
+                               return PTR_ERR(page);
                        kaddr = (unsigned char *)page_address(page);
                        offset = 0;
                }
        }
        f2fs_put_page(page, 1);
+       return 0;
 }
 
 static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
@@ -3110,6 +3564,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
        unsigned short blk_off;
        unsigned int segno = 0;
        block_t blk_addr = 0;
+       int err = 0;
 
        /* get segment number and block addr */
        if (IS_DATASEG(type)) {
@@ -3132,7 +3587,9 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
                        blk_addr = GET_SUM_BLOCK(sbi, segno);
        }
 
-       new = get_meta_page(sbi, blk_addr);
+       new = f2fs_get_meta_page(sbi, blk_addr);
+       if (IS_ERR(new))
+               return PTR_ERR(new);
        sum = (struct f2fs_summary_block *)page_address(new);
 
        if (IS_NODESEG(type)) {
@@ -3144,7 +3601,9 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
                                ns->ofs_in_node = 0;
                        }
                } else {
-                       restore_node_summary(sbi, segno, sum);
+                       err = f2fs_restore_node_summary(sbi, segno, sum);
+                       if (err)
+                               goto out;
                }
        }
 
@@ -3164,8 +3623,9 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
        curseg->alloc_type = ckpt->alloc_type[type];
        curseg->next_blkoff = blk_off;
        mutex_unlock(&curseg->curseg_mutex);
+out:
        f2fs_put_page(new, 1);
-       return 0;
+       return err;
 }
 
 static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
@@ -3176,19 +3636,21 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
        int err;
 
        if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG)) {
-               int npages = npages_for_summary_flush(sbi, true);
+               int npages = f2fs_npages_for_summary_flush(sbi, true);
 
                if (npages >= 2)
-                       ra_meta_pages(sbi, start_sum_block(sbi), npages,
+                       f2fs_ra_meta_pages(sbi, start_sum_block(sbi), npages,
                                                        META_CP, true);
 
                /* restore for compacted data summary */
-               read_compacted_summaries(sbi);
+               err = read_compacted_summaries(sbi);
+               if (err)
+                       return err;
                type = CURSEG_HOT_NODE;
        }
 
        if (__exist_node_summaries(sbi))
-               ra_meta_pages(sbi, sum_blk_addr(sbi, NR_CURSEG_TYPE, type),
+               f2fs_ra_meta_pages(sbi, sum_blk_addr(sbi, NR_CURSEG_TYPE, type),
                                        NR_CURSEG_TYPE - type, META_CP, true);
 
        for (; type <= CURSEG_COLD_NODE; type++) {
@@ -3199,8 +3661,11 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
 
        /* sanity check for summary blocks */
        if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES ||
-                       sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES)
+                       sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) {
+               f2fs_err(sbi, "invalid journal entries nats %u sits %u\n",
+                        nats_in_cursum(nat_j), sits_in_cursum(sit_j));
                return -EINVAL;
+       }
 
        return 0;
 }
@@ -3214,8 +3679,9 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr)
        int written_size = 0;
        int i, j;
 
-       page = grab_meta_page(sbi, blkaddr++);
+       page = f2fs_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);
@@ -3238,8 +3704,9 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr)
 
                for (j = 0; j < blkoff; j++) {
                        if (!page) {
-                               page = grab_meta_page(sbi, blkaddr++);
+                               page = f2fs_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);
@@ -3274,7 +3741,7 @@ static void write_normal_summaries(struct f2fs_sb_info *sbi,
                write_current_sum_page(sbi, i, blkaddr + (i - type));
 }
 
-void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
+void f2fs_write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
 {
        if (is_set_ckpt_flags(sbi, CP_COMPACT_SUM_FLAG))
                write_compacted_summaries(sbi, start_blk);
@@ -3282,12 +3749,12 @@ void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
                write_normal_summaries(sbi, start_blk, CURSEG_HOT_DATA);
 }
 
-void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
+void f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk)
 {
        write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE);
 }
 
-int lookup_journal_in_cursum(struct f2fs_journal *journal, int type,
+int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type,
                                        unsigned int val, int alloc)
 {
        int i;
@@ -3312,7 +3779,7 @@ int lookup_journal_in_cursum(struct f2fs_journal *journal, int type,
 static struct page *get_current_sit_page(struct f2fs_sb_info *sbi,
                                        unsigned int segno)
 {
-       return get_meta_page(sbi, current_sit_addr(sbi, segno));
+       return f2fs_get_meta_page_nofail(sbi, current_sit_addr(sbi, segno));
 }
 
 static struct page *get_next_sit_page(struct f2fs_sb_info *sbi,
@@ -3325,7 +3792,7 @@ static struct page *get_next_sit_page(struct f2fs_sb_info *sbi,
        src_off = current_sit_addr(sbi, start);
        dst_off = next_sit_addr(sbi, src_off);
 
-       page = grab_meta_page(sbi, dst_off);
+       page = f2fs_grab_meta_page(sbi, dst_off);
        seg_info_to_sit_page(sbi, page, start);
 
        set_page_dirty(page);
@@ -3421,7 +3888,7 @@ static void remove_sits_in_journal(struct f2fs_sb_info *sbi)
  * CP calls this function, which flushes SIT entries including sit_journal,
  * and moves prefree segs to free segs.
  */
-void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
+void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
 {
        struct sit_info *sit_i = SIT_I(sbi);
        unsigned long *bitmap = sit_i->dirty_sentries_bitmap;
@@ -3429,7 +3896,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        struct f2fs_journal *journal = curseg->journal;
        struct sit_entry_set *ses, *tmp;
        struct list_head *head = &SM_I(sbi)->sit_entry_set;
-       bool to_journal = true;
+       bool to_journal = !is_sbi_flag_set(sbi, SBI_IS_RESIZEFS);
        struct seg_entry *se;
 
        down_write(&sit_i->sentry_lock);
@@ -3448,7 +3915,8 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
         * entries, remove all entries from journal and add and account
         * them in sit entry set.
         */
-       if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL))
+       if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL) ||
+                                                               !to_journal)
                remove_sits_in_journal(sbi);
 
        /*
@@ -3480,6 +3948,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)) {
@@ -3488,17 +3961,21 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
                        }
 
                        if (to_journal) {
-                               offset = lookup_journal_in_cursum(journal,
+                               offset = f2fs_lookup_journal_in_cursum(journal,
                                                        SIT_JOURNAL, segno, 1);
                                f2fs_bug_on(sbi, offset < 0);
                                segno_in_journal(journal, offset) =
                                                        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);
@@ -3546,8 +4023,10 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
 
        SM_I(sbi)->sit_info = sit_i;
 
-       sit_i->sentries = f2fs_kvzalloc(sbi, MAIN_SEGS(sbi) *
-                                       sizeof(struct seg_entry), GFP_KERNEL);
+       sit_i->sentries =
+               f2fs_kvzalloc(sbi, array_size(sizeof(struct seg_entry),
+                                             MAIN_SEGS(sbi)),
+                             GFP_KERNEL);
        if (!sit_i->sentries)
                return -ENOMEM;
 
@@ -3573,22 +4052,22 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
                        return -ENOMEM;
 #endif
 
-               if (f2fs_discard_en(sbi)) {
-                       sit_i->sentries[start].discard_map
-                               = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE,
-                                                               GFP_KERNEL);
-                       if (!sit_i->sentries[start].discard_map)
-                               return -ENOMEM;
-               }
+               sit_i->sentries[start].discard_map
+                       = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE,
+                                                       GFP_KERNEL);
+               if (!sit_i->sentries[start].discard_map)
+                       return -ENOMEM;
        }
 
        sit_i->tmp_map = f2fs_kzalloc(sbi, SIT_VBLOCK_MAP_SIZE, GFP_KERNEL);
        if (!sit_i->tmp_map)
                return -ENOMEM;
 
-       if (sbi->segs_per_sec > 1) {
-               sit_i->sec_entries = f2fs_kvzalloc(sbi, MAIN_SECS(sbi) *
-                                       sizeof(struct sec_entry), GFP_KERNEL);
+       if (__is_large_section(sbi)) {
+               sit_i->sec_entries =
+                       f2fs_kvzalloc(sbi, array_size(sizeof(struct sec_entry),
+                                                     MAIN_SECS(sbi)),
+                                     GFP_KERNEL);
                if (!sit_i->sec_entries)
                        return -ENOMEM;
        }
@@ -3664,7 +4143,8 @@ static int build_curseg(struct f2fs_sb_info *sbi)
        struct curseg_info *array;
        int i;
 
-       array = f2fs_kzalloc(sbi, sizeof(*array) * NR_CURSEG_TYPE, GFP_KERNEL);
+       array = f2fs_kzalloc(sbi, array_size(NR_CURSEG_TYPE, sizeof(*array)),
+                            GFP_KERNEL);
        if (!array)
                return -ENOMEM;
 
@@ -3697,9 +4177,10 @@ 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,
+               readed = f2fs_ra_meta_pages(sbi, start_blk, BIO_MAX_PAGES,
                                                        META_SIT, true);
 
                start = start_blk * sit_i->sents_per_block;
@@ -3711,6 +4192,8 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
 
                        se = &sit_i->sentries[start];
                        page = get_current_sit_page(sbi, start);
+                       if (IS_ERR(page))
+                               return PTR_ERR(page);
                        sit_blk = (struct f2fs_sit_block *)page_address(page);
                        sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)];
                        f2fs_put_page(page, 1);
@@ -3719,23 +4202,23 @@ 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)) {
-                               if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) {
-                                       memset(se->discard_map, 0xff,
-                                               SIT_VBLOCK_MAP_SIZE);
-                               } else {
-                                       memcpy(se->discard_map,
-                                               se->cur_valid_map,
-                                               SIT_VBLOCK_MAP_SIZE);
-                                       sbi->discard_blks +=
-                                               sbi->blocks_per_seg -
-                                               se->valid_blocks;
-                               }
+                       if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) {
+                               memset(se->discard_map, 0xff,
+                                       SIT_VBLOCK_MAP_SIZE);
+                       } else {
+                               memcpy(se->discard_map,
+                                       se->cur_valid_map,
+                                       SIT_VBLOCK_MAP_SIZE);
+                               sbi->discard_blks +=
+                                       sbi->blocks_per_seg -
+                                       se->valid_blocks;
                        }
 
-                       if (sbi->segs_per_sec > 1)
+                       if (__is_large_section(sbi))
                                get_sec_entry(sbi, start)->valid_blocks +=
                                                        se->valid_blocks;
                }
@@ -3747,33 +4230,53 @@ 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_err(sbi, "Wrong journal entry on segno %u",
+                                start);
+                       set_sbi_flag(sbi, SBI_NEED_FSCK);
+                       err = -EFSCORRUPTED;
+                       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)) {
-                               memset(se->discard_map, 0xff,
-                                                       SIT_VBLOCK_MAP_SIZE);
-                       } else {
-                               memcpy(se->discard_map, se->cur_valid_map,
-                                                       SIT_VBLOCK_MAP_SIZE);
-                               sbi->discard_blks += old_valid_blocks -
-                                                       se->valid_blocks;
-                       }
+               if (is_set_ckpt_flags(sbi, CP_TRIMMED_FLAG)) {
+                       memset(se->discard_map, 0xff, SIT_VBLOCK_MAP_SIZE);
+               } else {
+                       memcpy(se->discard_map, se->cur_valid_map,
+                                               SIT_VBLOCK_MAP_SIZE);
+                       sbi->discard_blks += old_valid_blocks;
+                       sbi->discard_blks -= se->valid_blocks;
                }
 
-               if (sbi->segs_per_sec > 1)
+               if (__is_large_section(sbi)) {
                        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_err(sbi, "SIT is corrupted node# %u vs %u",
+                        total_node_blocks, valid_node_count(sbi));
+               set_sbi_flag(sbi, SBI_NEED_FSCK);
+               err = -EFSCORRUPTED;
+       }
+
        return err;
 }
 
@@ -3862,6 +4365,41 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi)
        return init_victim_secmap(sbi);
 }
 
+static int sanity_check_curseg(struct f2fs_sb_info *sbi)
+{
+       int i;
+
+       /*
+        * In LFS/SSR curseg, .next_blkoff should point to an unused blkaddr;
+        * In LFS curseg, all blkaddr after .next_blkoff should be unused.
+        */
+       for (i = 0; i < NO_CHECK_TYPE; i++) {
+               struct curseg_info *curseg = CURSEG_I(sbi, i);
+               struct seg_entry *se = get_seg_entry(sbi, curseg->segno);
+               unsigned int blkofs = curseg->next_blkoff;
+
+               if (f2fs_test_bit(blkofs, se->cur_valid_map))
+                       goto out;
+
+               if (curseg->alloc_type == SSR)
+                       continue;
+
+               for (blkofs += 1; blkofs < sbi->blocks_per_seg; blkofs++) {
+                       if (!f2fs_test_bit(blkofs, se->cur_valid_map))
+                               continue;
+out:
+                       f2fs_err(sbi,
+                                "Current segment's next free block offset is "
+                                "inconsistent with bitmap, logtype:%u, "
+                                "segno:%u, type:%u, next_blkoff:%u, blkofs:%u",
+                                i, curseg->segno, curseg->alloc_type,
+                                curseg->next_blkoff, blkofs);
+                       return -EFSCORRUPTED;
+               }
+       }
+       return 0;
+}
+
 /*
  * Update min, max modified time for cost-benefit GC algorithm
  */
@@ -3872,7 +4410,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;
@@ -3886,11 +4424,11 @@ static void init_min_max_mtime(struct f2fs_sb_info *sbi)
                if (sit_i->min_mtime > mtime)
                        sit_i->min_mtime = mtime;
        }
-       sit_i->max_mtime = get_mtime(sbi);
+       sit_i->max_mtime = get_mtime(sbi, false);
        up_write(&sit_i->sentry_lock);
 }
 
-int build_segment_manager(struct f2fs_sb_info *sbi)
+int f2fs_build_segment_manager(struct f2fs_sb_info *sbi)
 {
        struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
        struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
@@ -3919,6 +4457,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
                sm_info->ipu_policy = 1 << F2FS_IPU_FSYNC;
        sm_info->min_ipu_util = DEF_MIN_IPU_UTIL;
        sm_info->min_fsync_blocks = DEF_MIN_FSYNC_BLOCKS;
+       sm_info->min_seq_blocks = sbi->blocks_per_seg * sbi->segs_per_sec;
        sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS;
        sm_info->min_ssr_sections = reserved_sections(sbi);
 
@@ -3927,7 +4466,7 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
        init_rwsem(&sm_info->curseg_lock);
 
        if (!f2fs_readonly(sbi->sb)) {
-               err = create_flush_cmd_control(sbi);
+               err = f2fs_create_flush_cmd_control(sbi);
                if (err)
                        return err;
        }
@@ -3956,6 +4495,10 @@ int build_segment_manager(struct f2fs_sb_info *sbi)
        if (err)
                return err;
 
+       err = sanity_check_curseg(sbi);
+       if (err)
+               return err;
+
        init_min_max_mtime(sbi);
        return 0;
 }
@@ -3991,7 +4534,7 @@ static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
 
        destroy_victim_secmap(sbi);
        SM_I(sbi)->dirty_info = NULL;
-       kfree(dirty_i);
+       kvfree(dirty_i);
 }
 
 static void destroy_curseg(struct f2fs_sb_info *sbi)
@@ -4003,10 +4546,10 @@ static void destroy_curseg(struct f2fs_sb_info *sbi)
                return;
        SM_I(sbi)->curseg_array = NULL;
        for (i = 0; i < NR_CURSEG_TYPE; i++) {
-               kfree(array[i].sum_blk);
-               kfree(array[i].journal);
+               kvfree(array[i].sum_blk);
+               kvfree(array[i].journal);
        }
-       kfree(array);
+       kvfree(array);
 }
 
 static void destroy_free_segmap(struct f2fs_sb_info *sbi)
@@ -4017,7 +4560,7 @@ static void destroy_free_segmap(struct f2fs_sb_info *sbi)
        SM_I(sbi)->free_info = NULL;
        kvfree(free_i->free_segmap);
        kvfree(free_i->free_secmap);
-       kfree(free_i);
+       kvfree(free_i);
 }
 
 static void destroy_sit_info(struct f2fs_sb_info *sbi)
@@ -4030,45 +4573,45 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi)
 
        if (sit_i->sentries) {
                for (start = 0; start < MAIN_SEGS(sbi); start++) {
-                       kfree(sit_i->sentries[start].cur_valid_map);
+                       kvfree(sit_i->sentries[start].cur_valid_map);
 #ifdef CONFIG_F2FS_CHECK_FS
-                       kfree(sit_i->sentries[start].cur_valid_map_mir);
+                       kvfree(sit_i->sentries[start].cur_valid_map_mir);
 #endif
-                       kfree(sit_i->sentries[start].ckpt_valid_map);
-                       kfree(sit_i->sentries[start].discard_map);
+                       kvfree(sit_i->sentries[start].ckpt_valid_map);
+                       kvfree(sit_i->sentries[start].discard_map);
                }
        }
-       kfree(sit_i->tmp_map);
+       kvfree(sit_i->tmp_map);
 
        kvfree(sit_i->sentries);
        kvfree(sit_i->sec_entries);
        kvfree(sit_i->dirty_sentries_bitmap);
 
        SM_I(sbi)->sit_info = NULL;
-       kfree(sit_i->sit_bitmap);
+       kvfree(sit_i->sit_bitmap);
 #ifdef CONFIG_F2FS_CHECK_FS
-       kfree(sit_i->sit_bitmap_mir);
+       kvfree(sit_i->sit_bitmap_mir);
 #endif
-       kfree(sit_i);
+       kvfree(sit_i);
 }
 
-void destroy_segment_manager(struct f2fs_sb_info *sbi)
+void f2fs_destroy_segment_manager(struct f2fs_sb_info *sbi)
 {
        struct f2fs_sm_info *sm_info = SM_I(sbi);
 
        if (!sm_info)
                return;
-       destroy_flush_cmd_control(sbi, true);
+       f2fs_destroy_flush_cmd_control(sbi, true);
        destroy_discard_cmd_control(sbi);
        destroy_dirty_segmap(sbi);
        destroy_curseg(sbi);
        destroy_free_segmap(sbi);
        destroy_sit_info(sbi);
        sbi->sm_info = NULL;
-       kfree(sm_info);
+       kvfree(sm_info);
 }
 
-int __init create_segment_manager_caches(void)
+int __init f2fs_create_segment_manager_caches(void)
 {
        discard_entry_slab = f2fs_kmem_cache_create("discard_entry",
                        sizeof(struct discard_entry));
@@ -4101,7 +4644,7 @@ fail:
        return -ENOMEM;
 }
 
-void destroy_segment_manager_caches(void)
+void f2fs_destroy_segment_manager_caches(void)
 {
        kmem_cache_destroy(sit_entry_set_slab);
        kmem_cache_destroy(discard_cmd_slab);