f2fs: catch up to v4.14-rc1
[GitHub/exynos8895/android_kernel_samsung_universal8895.git] / fs / f2fs / segment.c
index 289b3facd2d833ff9b9ff1fd9573b8a9db4942f0..059a219b7740c5b736a62bc8901f36dd99527638 100644 (file)
 #include <linux/kthread.h>
 #include <linux/swap.h>
 #include <linux/timer.h>
+#include <linux/freezer.h>
+#include <linux/sched.h>
 
 #include "f2fs.h"
 #include "segment.h"
 #include "node.h"
+#include "gc.h"
 #include "trace.h"
 #include <trace/events/f2fs.h>
 
@@ -166,6 +169,21 @@ found:
        return result - size + __reverse_ffz(tmp);
 }
 
+bool 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);
+       int imeta_secs = get_blocktype_secs(sbi, F2FS_DIRTY_IMETA);
+
+       if (test_opt(sbi, LFS))
+               return false;
+       if (sbi->gc_thread && sbi->gc_thread->gc_urgent)
+               return true;
+
+       return free_sections(sbi) <= (node_secs + 2 * dent_secs + imeta_secs +
+                                               2 * reserved_sections(sbi));
+}
+
 void register_inmem_page(struct inode *inode, struct page *page)
 {
        struct f2fs_inode_info *fi = F2FS_I(inode);
@@ -212,9 +230,15 @@ static int __revoke_inmem_pages(struct inode *inode,
                        struct node_info ni;
 
                        trace_f2fs_commit_inmem_page(page, INMEM_REVOKE);
-
+retry:
                        set_new_dnode(&dn, inode, NULL, NULL, 0);
-                       if (get_dnode_of_data(&dn, page->index, LOOKUP_NODE)) {
+                       err = get_dnode_of_data(&dn, page->index, LOOKUP_NODE);
+                       if (err) {
+                               if (err == -ENOMEM) {
+                                       congestion_wait(BLK_RW_ASYNC, HZ/50);
+                                       cond_resched();
+                                       goto retry;
+                               }
                                err = -EAGAIN;
                                goto next;
                        }
@@ -247,9 +271,40 @@ void drop_inmem_pages(struct inode *inode)
        mutex_unlock(&fi->inmem_lock);
 
        clear_inode_flag(inode, FI_ATOMIC_FILE);
+       clear_inode_flag(inode, FI_HOT_DATA);
        stat_dec_atomic_write(inode);
 }
 
+void 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);
+       struct list_head *head = &fi->inmem_pages;
+       struct inmem_pages *cur = NULL;
+
+       f2fs_bug_on(sbi, !IS_ATOMIC_WRITTEN_PAGE(page));
+
+       mutex_lock(&fi->inmem_lock);
+       list_for_each_entry(cur, head, list) {
+               if (cur->page == page)
+                       break;
+       }
+
+       f2fs_bug_on(sbi, !cur || cur->page != page);
+       list_del(&cur->list);
+       mutex_unlock(&fi->inmem_lock);
+
+       dec_page_count(sbi, F2FS_INMEM_PAGES);
+       kmem_cache_free(inmem_entry_slab, cur);
+
+       ClearPageUptodate(page);
+       set_page_private(page, 0);
+       ClearPagePrivate(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)
 {
@@ -260,10 +315,10 @@ static int __commit_inmem_pages(struct inode *inode,
                .sbi = sbi,
                .type = DATA,
                .op = REQ_OP_WRITE,
-               .op_flags = REQ_SYNC | REQ_NOIDLE | REQ_PRIO,
-               .encrypted_page = NULL,
+               .op_flags = REQ_SYNC | REQ_PRIO,
+               .io_type = FS_DATA_IO,
        };
-       bool submit_bio = false;
+       pgoff_t last_idx = ULONG_MAX;
        int err = 0;
 
        list_for_each_entry_safe(cur, tmp, &fi->inmem_pages, list) {
@@ -279,25 +334,31 @@ static int __commit_inmem_pages(struct inode *inode,
                                inode_dec_dirty_pages(inode);
                                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);
                        if (err) {
+                               if (err == -ENOMEM) {
+                                       congestion_wait(BLK_RW_ASYNC, HZ/50);
+                                       cond_resched();
+                                       goto retry;
+                               }
                                unlock_page(page);
                                break;
                        }
-
                        /* record old blkaddr for revoking */
                        cur->old_addr = fio.old_blkaddr;
-
-                       submit_bio = true;
+                       last_idx = page->index;
                }
                unlock_page(page);
                list_move_tail(&cur->list, revoke_list);
        }
 
-       if (submit_bio)
-               f2fs_submit_merged_bio_cond(sbi, inode, NULL, 0, DATA, WRITE);
+       if (last_idx != ULONG_MAX)
+               f2fs_submit_merged_write_cond(sbi, inode, 0, last_idx, DATA);
 
        if (!err)
                __revoke_inmem_pages(inode, revoke_list, false, false);
@@ -352,15 +413,14 @@ 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))
+       if (time_to_inject(sbi, FAULT_CHECKPOINT)) {
+               f2fs_show_injection_info(FAULT_CHECKPOINT);
                f2fs_stop_checkpoint(sbi, false);
+       }
 #endif
 
-       if (!need)
-               return;
-
        /* balance_fs_bg is able to be pending */
-       if (excess_cached_nats(sbi))
+       if (need && excess_cached_nats(sbi))
                f2fs_balance_fs_bg(sbi);
 
        /*
@@ -369,7 +429,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
         */
        if (has_not_enough_free_secs(sbi, 0, 0)) {
                mutex_lock(&sbi->gc_mutex);
-               f2fs_gc(sbi, false, false);
+               f2fs_gc(sbi, false, false, NULL_SEGNO);
        }
 }
 
@@ -386,9 +446,9 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
        if (!available_free_memory(sbi, FREE_NIDS))
                try_to_free_nids(sbi, MAX_FREE_NIDS);
        else
-               build_free_nids(sbi, false);
+               build_free_nids(sbi, false, false);
 
-       if (!is_idle(sbi))
+       if (!is_idle(sbi) && !excess_dirty_nats(sbi))
                return;
 
        /* checkpoint is the only way to shrink partial cached entries */
@@ -409,7 +469,8 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)
        }
 }
 
-static int __submit_flush_wait(struct block_device *bdev)
+static int __submit_flush_wait(struct f2fs_sb_info *sbi,
+                               struct block_device *bdev)
 {
        struct bio *bio = f2fs_bio_alloc(0);
        int ret;
@@ -418,20 +479,24 @@ static int __submit_flush_wait(struct block_device *bdev)
        bio->bi_bdev = bdev;
        ret = submit_bio_wait(WRITE_FLUSH, bio);
        bio_put(bio);
+
+       trace_f2fs_issue_flush(bdev, test_opt(sbi, NOBARRIER),
+                               test_opt(sbi, FLUSH_MERGE), ret);
        return ret;
 }
 
 static int submit_flush_wait(struct f2fs_sb_info *sbi)
 {
-       int ret = __submit_flush_wait(sbi->sb->s_bdev);
+       int ret = __submit_flush_wait(sbi, sbi->sb->s_bdev);
        int i;
 
-       if (sbi->s_ndevs && !ret) {
-               for (i = 1; i < sbi->s_ndevs; i++) {
-                       ret = __submit_flush_wait(FDEV(i).bdev);
-                       if (ret)
-                               break;
-               }
+       if (!sbi->s_ndevs || ret)
+               return ret;
+
+       for (i = 1; i < sbi->s_ndevs; i++) {
+               ret = __submit_flush_wait(sbi, FDEV(i).bdev);
+               if (ret)
+                       break;
        }
        return ret;
 }
@@ -445,6 +510,8 @@ repeat:
        if (kthread_should_stop())
                return 0;
 
+       sb_start_intwrite(sbi->sb);
+
        if (!llist_empty(&fcc->issue_list)) {
                struct flush_cmd *cmd, *next;
                int ret;
@@ -453,6 +520,8 @@ repeat:
                fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list);
 
                ret = submit_flush_wait(sbi);
+               atomic_inc(&fcc->issued_flush);
+
                llist_for_each_entry_safe(cmd, next,
                                          fcc->dispatch_list, llnode) {
                        cmd->ret = ret;
@@ -461,6 +530,8 @@ repeat:
                fcc->dispatch_list = NULL;
        }
 
+       sb_end_intwrite(sbi->sb);
+
        wait_event_interruptible(*q,
                kthread_should_stop() || !llist_empty(&fcc->issue_list));
        goto repeat;
@@ -470,36 +541,60 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi)
 {
        struct flush_cmd_control *fcc = SM_I(sbi)->fcc_info;
        struct flush_cmd cmd;
-
-       trace_f2fs_issue_flush(sbi->sb, test_opt(sbi, NOBARRIER),
-                                       test_opt(sbi, FLUSH_MERGE));
+       int ret;
 
        if (test_opt(sbi, NOBARRIER))
                return 0;
 
-       if (!test_opt(sbi, FLUSH_MERGE) || !atomic_read(&fcc->submit_flush)) {
-               int ret;
+       if (!test_opt(sbi, FLUSH_MERGE)) {
+               ret = submit_flush_wait(sbi);
+               atomic_inc(&fcc->issued_flush);
+               return ret;
+       }
 
-               atomic_inc(&fcc->submit_flush);
+       if (atomic_inc_return(&fcc->issing_flush) == 1) {
                ret = submit_flush_wait(sbi);
-               atomic_dec(&fcc->submit_flush);
+               atomic_dec(&fcc->issing_flush);
+
+               atomic_inc(&fcc->issued_flush);
                return ret;
        }
 
        init_completion(&cmd.wait);
 
-       atomic_inc(&fcc->submit_flush);
        llist_add(&cmd.llnode, &fcc->issue_list);
 
-       if (!fcc->dispatch_list)
+       /* update issue_list before we wake up issue_flush thread */
+       smp_mb();
+
+       if (waitqueue_active(&fcc->flush_wait_queue))
                wake_up(&fcc->flush_wait_queue);
 
        if (fcc->f2fs_issue_flush) {
                wait_for_completion(&cmd.wait);
-               atomic_dec(&fcc->submit_flush);
+               atomic_dec(&fcc->issing_flush);
        } else {
-               llist_del_all(&fcc->issue_list);
-               atomic_set(&fcc->submit_flush, 0);
+               struct llist_node *list;
+
+               list = llist_del_all(&fcc->issue_list);
+               if (!list) {
+                       wait_for_completion(&cmd.wait);
+                       atomic_dec(&fcc->issing_flush);
+               } else {
+                       struct flush_cmd *tmp, *next;
+
+                       ret = submit_flush_wait(sbi);
+
+                       llist_for_each_entry_safe(tmp, next, list, llnode) {
+                               if (tmp == &cmd) {
+                                       cmd.ret = ret;
+                                       atomic_dec(&fcc->issing_flush);
+                                       continue;
+                               }
+                               tmp->ret = ret;
+                               complete(&tmp->wait);
+                       }
+               }
        }
 
        return cmd.ret;
@@ -513,16 +608,22 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi)
 
        if (SM_I(sbi)->fcc_info) {
                fcc = SM_I(sbi)->fcc_info;
+               if (fcc->f2fs_issue_flush)
+                       return err;
                goto init_thread;
        }
 
        fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL);
        if (!fcc)
                return -ENOMEM;
-       atomic_set(&fcc->submit_flush, 0);
+       atomic_set(&fcc->issued_flush, 0);
+       atomic_set(&fcc->issing_flush, 0);
        init_waitqueue_head(&fcc->flush_wait_queue);
        init_llist_head(&fcc->issue_list);
        SM_I(sbi)->fcc_info = fcc;
+       if (!test_opt(sbi, FLUSH_MERGE))
+               return err;
+
 init_thread:
        fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi,
                                "f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev));
@@ -592,8 +693,8 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
                if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t]))
                        dirty_i->nr_dirty[t]--;
 
-               if (get_valid_blocks(sbi, segno, sbi->segs_per_sec) == 0)
-                       clear_bit(GET_SECNO(sbi, segno),
+               if (get_valid_blocks(sbi, segno, true) == 0)
+                       clear_bit(GET_SEC_FROM_SEG(sbi, segno),
                                                dirty_i->victim_secmap);
        }
 }
@@ -613,7 +714,7 @@ 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, 0);
+       valid_blocks = get_valid_blocks(sbi, segno, false);
 
        if (valid_blocks == 0) {
                __locate_dirty_segment(sbi, segno, PRE);
@@ -628,61 +729,91 @@ static void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno)
        mutex_unlock(&dirty_i->seglist_lock);
 }
 
-static struct discard_cmd *__add_discard_cmd(struct f2fs_sb_info *sbi,
-                       struct bio *bio, block_t lstart, block_t len)
+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)
 {
-       struct list_head *wait_list = &(SM_I(sbi)->discard_cmd_list);
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct list_head *pend_list;
        struct discard_cmd *dc;
 
+       f2fs_bug_on(sbi, !len);
+
+       pend_list = &dcc->pend_list[plist_idx(len)];
+
        dc = f2fs_kmem_cache_alloc(discard_cmd_slab, GFP_NOFS);
        INIT_LIST_HEAD(&dc->list);
-       dc->bio = bio;
+       dc->bdev = bdev;
        dc->lstart = lstart;
+       dc->start = start;
        dc->len = len;
+       dc->ref = 0;
+       dc->state = D_PREP;
+       dc->error = 0;
        init_completion(&dc->wait);
-       list_add_tail(&dc->list, wait_list);
+       list_add_tail(&dc->list, pend_list);
+       atomic_inc(&dcc->discard_cmd_cnt);
+       dcc->undiscard_blks += len;
 
        return dc;
 }
 
-/* This should be covered by global mutex, &sit_i->sentry_lock */
-void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr)
+static struct discard_cmd *__attach_discard_cmd(struct f2fs_sb_info *sbi,
+                               struct block_device *bdev, block_t lstart,
+                               block_t start, block_t len,
+                               struct rb_node *parent, struct rb_node **p)
 {
-       struct list_head *wait_list = &(SM_I(sbi)->discard_cmd_list);
-       struct discard_cmd *dc, *tmp;
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct discard_cmd *dc;
 
-       list_for_each_entry_safe(dc, tmp, wait_list, list) {
-               struct bio *bio = dc->bio;
-               int err;
-
-               if (!completion_done(&dc->wait)) {
-                       if ((dc->lstart <= blkaddr &&
-                                       blkaddr < dc->lstart + dc->len) ||
-                                       blkaddr == NULL_ADDR)
-                               wait_for_completion_io(&dc->wait);
-                       else
-                               continue;
-               }
+       dc = __create_discard_cmd(sbi, bdev, lstart, start, len);
 
-               err = bio->bi_error;
-               if (err == -EOPNOTSUPP)
-                       err = 0;
+       rb_link_node(&dc->rb_node, parent, p);
+       rb_insert_color(&dc->rb_node, &dcc->root);
 
-               if (err)
-                       f2fs_msg(sbi->sb, KERN_INFO,
-                               "Issue discard failed, ret: %d", err);
+       return dc;
+}
 
-               bio_put(bio);
-               list_del(&dc->list);
-               kmem_cache_free(discard_cmd_slab, dc);
-       }
+static void __detach_discard_cmd(struct discard_cmd_control *dcc,
+                                                       struct discard_cmd *dc)
+{
+       if (dc->state == D_DONE)
+               atomic_dec(&dcc->issing_discard);
+
+       list_del(&dc->list);
+       rb_erase(&dc->rb_node, &dcc->root);
+       dcc->undiscard_blks -= dc->len;
+
+       kmem_cache_free(discard_cmd_slab, dc);
+
+       atomic_dec(&dcc->discard_cmd_cnt);
+}
+
+static void __remove_discard_cmd(struct f2fs_sb_info *sbi,
+                                                       struct discard_cmd *dc)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+
+       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);
+       __detach_discard_cmd(dcc, dc);
 }
 
 static void f2fs_submit_discard_endio(struct bio *bio)
 {
        struct discard_cmd *dc = (struct discard_cmd *)bio->bi_private;
 
-       complete(&dc->wait);
+       dc->error = bio->bi_error;
+       dc->state = D_DONE;
+       complete_all(&dc->wait);
+       bio_put(bio);
 }
 
 /* copied from block/blk-lib.c in 4.10-rc1 */
@@ -739,12 +870,12 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
                }
 
                if (bio) {
-                       int ret = submit_bio_wait(0, bio);
+                       int ret = submit_bio_wait(op, bio);
                        bio_put(bio);
                        if (ret)
                                return ret;
                }
-               bio = f2fs_bio_alloc(0);
+               bio = f2fs_bio_alloc(1);
                bio->bi_iter.bi_sector = sector;
                bio->bi_bdev = bdev;
                bio_set_op_attrs(bio, op, 0);
@@ -766,58 +897,471 @@ static int __blkdev_issue_discard(struct block_device *bdev, sector_t sector,
        return 0;
 }
 
+void __check_sit_bitmap(struct f2fs_sb_info *sbi,
+                               block_t start, block_t end)
+{
+#ifdef CONFIG_F2FS_CHECK_FS
+       struct seg_entry *sentry;
+       unsigned int segno;
+       block_t blk = start;
+       unsigned long offset, size, max_blocks = sbi->blocks_per_seg;
+       unsigned long *map;
+
+       while (blk < end) {
+               segno = GET_SEGNO(sbi, blk);
+               sentry = get_seg_entry(sbi, segno);
+               offset = GET_BLKOFF_FROM_SEG0(sbi, blk);
+
+               if (end < START_BLOCK(sbi, segno + 1))
+                       size = GET_BLKOFF_FROM_SEG0(sbi, end);
+               else
+                       size = max_blocks;
+               map = (unsigned long *)(sentry->cur_valid_map);
+               offset = __find_rev_next_bit(map, size, offset);
+               f2fs_bug_on(sbi, offset != size);
+               blk = START_BLOCK(sbi, segno + 1);
+       }
+#endif
+}
+
 /* this function is copied from blkdev_issue_discard from block/blk-lib.c */
-static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi,
-               struct block_device *bdev, block_t blkstart, block_t blklen)
+static void __submit_discard_cmd(struct f2fs_sb_info *sbi,
+                               struct discard_cmd *dc)
 {
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
        struct bio *bio = NULL;
+
+       if (dc->state != D_PREP)
+               return;
+
+       trace_f2fs_issue_discard(dc->bdev, dc->start, dc->len);
+
+       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(REQ_SYNC, bio);
+                       list_move_tail(&dc->list, &dcc->wait_list);
+                       __check_sit_bitmap(sbi, dc->start, dc->start + dc->len);
+
+                       f2fs_update_iostat(sbi, FS_DISCARD, 1);
+               }
+       } else {
+               __remove_discard_cmd(sbi, dc);
+       }
+}
+
+static struct discard_cmd *__insert_discard_tree(struct f2fs_sb_info *sbi,
+                               struct block_device *bdev, block_t lstart,
+                               block_t start, block_t len,
+                               struct rb_node **insert_p,
+                               struct rb_node *insert_parent)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct rb_node **p = &dcc->root.rb_node;
+       struct rb_node *parent = NULL;
+       struct discard_cmd *dc = NULL;
+
+       if (insert_p && insert_parent) {
+               parent = insert_parent;
+               p = insert_p;
+               goto do_insert;
+       }
+
+       p = __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)
+               return NULL;
+
+       return dc;
+}
+
+static void __relocate_discard_cmd(struct discard_cmd_control *dcc,
+                                               struct discard_cmd *dc)
+{
+       list_move_tail(&dc->list, &dcc->pend_list[plist_idx(dc->len)]);
+}
+
+static void __punch_discard_cmd(struct f2fs_sb_info *sbi,
+                               struct discard_cmd *dc, block_t blkaddr)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct discard_info di = dc->di;
+       bool modified = false;
+
+       if (dc->state == D_DONE || dc->len == 1) {
+               __remove_discard_cmd(sbi, dc);
+               return;
+       }
+
+       dcc->undiscard_blks -= di.len;
+
+       if (blkaddr > di.lstart) {
+               dc->len = blkaddr - dc->lstart;
+               dcc->undiscard_blks += dc->len;
+               __relocate_discard_cmd(dcc, dc);
+               modified = true;
+       }
+
+       if (blkaddr < di.lstart + di.len - 1) {
+               if (modified) {
+                       __insert_discard_tree(sbi, dc->bdev, blkaddr + 1,
+                                       di.start + blkaddr + 1 - di.lstart,
+                                       di.lstart + di.len - 1 - blkaddr,
+                                       NULL, NULL);
+               } else {
+                       dc->lstart++;
+                       dc->len--;
+                       dc->start++;
+                       dcc->undiscard_blks += dc->len;
+                       __relocate_discard_cmd(dcc, dc);
+               }
+       }
+}
+
+static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
+                               struct block_device *bdev, block_t lstart,
+                               block_t start, block_t len)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct discard_cmd *prev_dc = NULL, *next_dc = NULL;
+       struct discard_cmd *dc;
+       struct discard_info di = {0};
+       struct rb_node **insert_p = NULL, *insert_parent = NULL;
+       block_t end = lstart + len;
+
+       mutex_lock(&dcc->cmd_lock);
+
+       dc = (struct discard_cmd *)__lookup_rb_tree_ret(&dcc->root,
+                                       NULL, lstart,
+                                       (struct rb_entry **)&prev_dc,
+                                       (struct rb_entry **)&next_dc,
+                                       &insert_p, &insert_parent, true);
+       if (dc)
+               prev_dc = dc;
+
+       if (!prev_dc) {
+               di.lstart = lstart;
+               di.len = next_dc ? next_dc->lstart - lstart : len;
+               di.len = min(di.len, len);
+               di.start = start;
+       }
+
+       while (1) {
+               struct rb_node *node;
+               bool merged = false;
+               struct discard_cmd *tdc = NULL;
+
+               if (prev_dc) {
+                       di.lstart = prev_dc->lstart + prev_dc->len;
+                       if (di.lstart < lstart)
+                               di.lstart = lstart;
+                       if (di.lstart >= end)
+                               break;
+
+                       if (!next_dc || next_dc->lstart > end)
+                               di.len = end - di.lstart;
+                       else
+                               di.len = next_dc->lstart - di.lstart;
+                       di.start = start + di.lstart - lstart;
+               }
+
+               if (!di.len)
+                       goto next;
+
+               if (prev_dc && prev_dc->state == D_PREP &&
+                       prev_dc->bdev == bdev &&
+                       __is_discard_back_mergeable(&di, &prev_dc->di)) {
+                       prev_dc->di.len += di.len;
+                       dcc->undiscard_blks += di.len;
+                       __relocate_discard_cmd(dcc, prev_dc);
+                       di = prev_dc->di;
+                       tdc = prev_dc;
+                       merged = true;
+               }
+
+               if (next_dc && next_dc->state == D_PREP &&
+                       next_dc->bdev == bdev &&
+                       __is_discard_front_mergeable(&di, &next_dc->di)) {
+                       next_dc->di.lstart = di.lstart;
+                       next_dc->di.len += di.len;
+                       next_dc->di.start = di.start;
+                       dcc->undiscard_blks += di.len;
+                       __relocate_discard_cmd(dcc, next_dc);
+                       if (tdc)
+                               __remove_discard_cmd(sbi, tdc);
+                       merged = true;
+               }
+
+               if (!merged) {
+                       __insert_discard_tree(sbi, bdev, di.lstart, di.start,
+                                                       di.len, NULL, NULL);
+               }
+ next:
+               prev_dc = next_dc;
+               if (!prev_dc)
+                       break;
+
+               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,
+               struct block_device *bdev, block_t blkstart, block_t blklen)
+{
        block_t lblkstart = blkstart;
-       int err;
 
-       trace_f2fs_issue_discard(sbi->sb, blkstart, blklen);
+       trace_f2fs_queue_discard(bdev, blkstart, blklen);
 
        if (sbi->s_ndevs) {
                int devi = f2fs_target_device_index(sbi, blkstart);
 
                blkstart -= FDEV(devi).start_blk;
        }
-       err = __blkdev_issue_discard(bdev,
-                               SECTOR_FROM_BLOCK(blkstart),
-                               SECTOR_FROM_BLOCK(blklen),
-                               GFP_NOFS, 0, &bio);
-       if (!err && bio) {
-               struct discard_cmd *dc = __add_discard_cmd(sbi, bio,
-                                               lblkstart, blklen);
+       __update_discard_tree_range(sbi, bdev, lblkstart, blkstart, blklen);
+       return 0;
+}
+
+static int __issue_discard_cmd(struct f2fs_sb_info *sbi, bool issue_cond)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct list_head *pend_list;
+       struct discard_cmd *dc, *tmp;
+       struct blk_plug plug;
+       int iter = 0, issued = 0;
+       int i;
+       bool io_interrupted = false;
+
+       mutex_lock(&dcc->cmd_lock);
+       f2fs_bug_on(sbi,
+               !__check_rb_tree_consistence(sbi, &dcc->root));
+       blk_start_plug(&plug);
+       for (i = MAX_PLIST_NUM - 1;
+                       i >= 0 && plist_issue(dcc->pend_list_tag[i]); i--) {
+               pend_list = &dcc->pend_list[i];
+               list_for_each_entry_safe(dc, tmp, pend_list, list) {
+                       f2fs_bug_on(sbi, dc->state != D_PREP);
+
+                       /* Hurry up to finish fstrim */
+                       if (dcc->pend_list_tag[i] & P_TRIM) {
+                               __submit_discard_cmd(sbi, dc);
+                               issued++;
+
+                               if (fatal_signal_pending(current))
+                                       break;
+                               continue;
+                       }
+
+                       if (!issue_cond) {
+                               __submit_discard_cmd(sbi, dc);
+                               issued++;
+                               continue;
+                       }
+
+                       if (is_idle(sbi)) {
+                               __submit_discard_cmd(sbi, dc);
+                               issued++;
+                       } else {
+                               io_interrupted = true;
+                       }
 
-               bio->bi_private = dc;
-               bio->bi_end_io = f2fs_submit_discard_endio;
-               submit_bio(REQ_SYNC, bio);
+                       if (++iter >= DISCARD_ISSUE_RATE)
+                               goto out;
+               }
+               if (list_empty(pend_list) && dcc->pend_list_tag[i] & P_TRIM)
+                       dcc->pend_list_tag[i] &= (~P_TRIM);
        }
-       return err;
+out:
+       blk_finish_plug(&plug);
+       mutex_unlock(&dcc->cmd_lock);
+
+       if (!issued && io_interrupted)
+               issued = -1;
+
+       return issued;
+}
+
+static void __drop_discard_cmd(struct f2fs_sb_info *sbi)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct list_head *pend_list;
+       struct discard_cmd *dc, *tmp;
+       int i;
+
+       mutex_lock(&dcc->cmd_lock);
+       for (i = MAX_PLIST_NUM - 1; i >= 0; i--) {
+               pend_list = &dcc->pend_list[i];
+               list_for_each_entry_safe(dc, tmp, pend_list, list) {
+                       f2fs_bug_on(sbi, dc->state != D_PREP);
+                       __remove_discard_cmd(sbi, dc);
+               }
+       }
+       mutex_unlock(&dcc->cmd_lock);
+}
+
+static void __wait_one_discard_bio(struct f2fs_sb_info *sbi,
+                                                       struct discard_cmd *dc)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+
+       wait_for_completion_io(&dc->wait);
+       mutex_lock(&dcc->cmd_lock);
+       f2fs_bug_on(sbi, dc->state != D_DONE);
+       dc->ref--;
+       if (!dc->ref)
+               __remove_discard_cmd(sbi, dc);
+       mutex_unlock(&dcc->cmd_lock);
+}
+
+static void __wait_discard_cmd(struct f2fs_sb_info *sbi, bool wait_cond)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct list_head *wait_list = &(dcc->wait_list);
+       struct discard_cmd *dc, *tmp;
+       bool need_wait;
+
+next:
+       need_wait = false;
+
+       mutex_lock(&dcc->cmd_lock);
+       list_for_each_entry_safe(dc, tmp, wait_list, list) {
+               if (!wait_cond || (dc->state == D_DONE && !dc->ref)) {
+                       wait_for_completion_io(&dc->wait);
+                       __remove_discard_cmd(sbi, dc);
+               } else {
+                       dc->ref++;
+                       need_wait = true;
+                       break;
+               }
+       }
+       mutex_unlock(&dcc->cmd_lock);
+
+       if (need_wait) {
+               __wait_one_discard_bio(sbi, dc);
+               goto next;
+       }
+}
+
+/* This should be covered by global mutex, &sit_i->sentry_lock */
+void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct discard_cmd *dc;
+       bool need_wait = false;
+
+       mutex_lock(&dcc->cmd_lock);
+       dc = (struct discard_cmd *)__lookup_rb_tree(&dcc->root, NULL, blkaddr);
+       if (dc) {
+               if (dc->state == D_PREP) {
+                       __punch_discard_cmd(sbi, dc, blkaddr);
+               } else {
+                       dc->ref++;
+                       need_wait = true;
+               }
+       }
+       mutex_unlock(&dcc->cmd_lock);
+
+       if (need_wait)
+               __wait_one_discard_bio(sbi, dc);
+}
+
+void stop_discard_thread(struct f2fs_sb_info *sbi)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+
+       if (dcc && dcc->f2fs_issue_discard) {
+               struct task_struct *discard_thread = dcc->f2fs_issue_discard;
+
+               dcc->f2fs_issue_discard = NULL;
+               kthread_stop(discard_thread);
+       }
+}
+
+/* This comes from f2fs_put_super and f2fs_trim_fs */
+void f2fs_wait_discard_bios(struct f2fs_sb_info *sbi)
+{
+       __issue_discard_cmd(sbi, false);
+       __drop_discard_cmd(sbi);
+       __wait_discard_cmd(sbi, false);
+}
+
+static void mark_discard_range_all(struct f2fs_sb_info *sbi)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       int i;
+
+       mutex_lock(&dcc->cmd_lock);
+       for (i = 0; i < MAX_PLIST_NUM; i++)
+               dcc->pend_list_tag[i] |= P_TRIM;
+       mutex_unlock(&dcc->cmd_lock);
+}
+
+static int issue_discard_thread(void *data)
+{
+       struct f2fs_sb_info *sbi = data;
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       wait_queue_head_t *q = &dcc->discard_wait_queue;
+       unsigned int wait_ms = DEF_MIN_DISCARD_ISSUE_TIME;
+       int issued;
+
+       set_freezable();
+
+       do {
+               wait_event_interruptible_timeout(*q,
+                               kthread_should_stop() || freezing(current) ||
+                               dcc->discard_wake,
+                               msecs_to_jiffies(wait_ms));
+               if (try_to_freeze())
+                       continue;
+               if (kthread_should_stop())
+                       return 0;
+
+               if (dcc->discard_wake) {
+                       dcc->discard_wake = 0;
+                       if (sbi->gc_thread && sbi->gc_thread->gc_urgent)
+                               mark_discard_range_all(sbi);
+               }
+
+               sb_start_intwrite(sbi->sb);
+
+               issued = __issue_discard_cmd(sbi, true);
+               if (issued) {
+                       __wait_discard_cmd(sbi, true);
+                       wait_ms = DEF_MIN_DISCARD_ISSUE_TIME;
+               } else {
+                       wait_ms = DEF_MAX_DISCARD_ISSUE_TIME;
+               }
+
+               sb_end_intwrite(sbi->sb);
+
+       } while (!kthread_should_stop());
+       return 0;
 }
 
 #ifdef CONFIG_BLK_DEV_ZONED
 static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
                struct block_device *bdev, block_t blkstart, block_t blklen)
 {
-       sector_t nr_sects = SECTOR_FROM_BLOCK(blklen);
-       sector_t sector;
+       sector_t sector, nr_sects;
+       block_t lblkstart = blkstart;
        int devi = 0;
 
        if (sbi->s_ndevs) {
                devi = f2fs_target_device_index(sbi, blkstart);
                blkstart -= FDEV(devi).start_blk;
        }
-       sector = SECTOR_FROM_BLOCK(blkstart);
-
-       if (sector & (bdev_zone_size(bdev) - 1) ||
-                               nr_sects != bdev_zone_size(bdev)) {
-               f2fs_msg(sbi->sb, KERN_INFO,
-                       "(%d) %s: Unaligned discard attempted (block %x + %x)",
-                       devi, sbi->s_ndevs ? FDEV(devi).path: "",
-                       blkstart, blklen);
-               return -EIO;
-       }
 
        /*
         * We need to know the type of the zone: for conventional zones,
@@ -829,10 +1373,21 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
        case BLK_ZONE_TYPE_CONVENTIONAL:
                if (!blk_queue_discard(bdev_get_queue(bdev)))
                        return 0;
-               return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen);
+               return __queue_discard_cmd(sbi, bdev, lblkstart, blklen);
        case BLK_ZONE_TYPE_SEQWRITE_REQ:
        case BLK_ZONE_TYPE_SEQWRITE_PREF:
-               trace_f2fs_issue_reset_zone(sbi->sb, 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);
+                       return -EIO;
+               }
+               trace_f2fs_issue_reset_zone(bdev, blkstart);
                return blkdev_reset_zones(bdev, sector,
                                          nr_sects, GFP_NOFS);
        default:
@@ -850,7 +1405,7 @@ static int __issue_discard_async(struct f2fs_sb_info *sbi,
                                bdev_zoned_model(bdev) != BLK_ZONED_NONE)
                return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen);
 #endif
-       return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen);
+       return __queue_discard_cmd(sbi, bdev, blkstart, blklen);
 }
 
 static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
@@ -893,31 +1448,6 @@ static int f2fs_issue_discard(struct f2fs_sb_info *sbi,
        return err;
 }
 
-static void __add_discard_entry(struct f2fs_sb_info *sbi,
-               struct cp_control *cpc, struct seg_entry *se,
-               unsigned int start, unsigned int end)
-{
-       struct list_head *head = &SM_I(sbi)->discard_entry_list;
-       struct discard_entry *new, *last;
-
-       if (!list_empty(head)) {
-               last = list_last_entry(head, struct discard_entry, list);
-               if (START_BLOCK(sbi, cpc->trim_start) + start ==
-                                               last->blkaddr + last->len) {
-                       last->len += end - start;
-                       goto done;
-               }
-       }
-
-       new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS);
-       INIT_LIST_HEAD(&new->list);
-       new->blkaddr = START_BLOCK(sbi, cpc->trim_start) + start;
-       new->len = end - start;
-       list_add_tail(&new->list, head);
-done:
-       SM_I(sbi)->nr_discards += end - start;
-}
-
 static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc,
                                                        bool check_only)
 {
@@ -929,7 +1459,9 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc,
        unsigned long *discard_map = (unsigned long *)se->discard_map;
        unsigned long *dmap = SIT_I(sbi)->tmp_map;
        unsigned int start = 0, end = -1;
-       bool force = (cpc->reason == CP_DISCARD);
+       bool force = (cpc->reason & CP_DISCARD);
+       struct discard_entry *de = NULL;
+       struct list_head *head = &SM_I(sbi)->dcc_info->entry_list;
        int i;
 
        if (se->valid_blocks == max_blocks || !f2fs_discard_en(sbi))
@@ -937,7 +1469,8 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc,
 
        if (!force) {
                if (!test_opt(sbi, DISCARD) || !se->valid_blocks ||
-                   SM_I(sbi)->nr_discards >= SM_I(sbi)->max_discards)
+                       SM_I(sbi)->dcc_info->nr_discards >=
+                               SM_I(sbi)->dcc_info->max_discards)
                        return false;
        }
 
@@ -946,7 +1479,8 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc,
                dmap[i] = force ? ~ckpt_map[i] & ~discard_map[i] :
                                (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i];
 
-       while (force || SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) {
+       while (force || SM_I(sbi)->dcc_info->nr_discards <=
+                               SM_I(sbi)->dcc_info->max_discards) {
                start = __find_rev_next_bit(dmap, max_blocks, end + 1);
                if (start >= max_blocks)
                        break;
@@ -959,14 +1493,24 @@ static bool add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc,
                if (check_only)
                        return true;
 
-               __add_discard_entry(sbi, cpc, se, start, end);
+               if (!de) {
+                       de = f2fs_kmem_cache_alloc(discard_entry_slab,
+                                                               GFP_F2FS_ZERO);
+                       de->start_blkaddr = START_BLOCK(sbi, cpc->trim_start);
+                       list_add_tail(&de->list, head);
+               }
+
+               for (i = start; i < end; i++)
+                       __set_bit_le(i, (void *)de->discard_map);
+
+               SM_I(sbi)->dcc_info->nr_discards += end - start;
        }
        return false;
 }
 
 void release_discard_addrs(struct f2fs_sb_info *sbi)
 {
-       struct list_head *head = &(SM_I(sbi)->discard_entry_list);
+       struct list_head *head = &(SM_I(sbi)->dcc_info->entry_list);
        struct discard_entry *entry, *this;
 
        /* drop caches */
@@ -992,16 +1536,14 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
 
 void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
 {
-       struct list_head *head = &(SM_I(sbi)->discard_entry_list);
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+       struct list_head *head = &dcc->entry_list;
        struct discard_entry *entry, *this;
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
-       struct blk_plug plug;
        unsigned long *prefree_map = dirty_i->dirty_segmap[PRE];
        unsigned int start = 0, end = -1;
        unsigned int secno, start_segno;
-       bool force = (cpc->reason == CP_DISCARD);
-
-       blk_start_plug(&plug);
+       bool force = (cpc->reason & CP_DISCARD);
 
        mutex_lock(&dirty_i->seglist_lock);
 
@@ -1031,32 +1573,117 @@ void clear_prefree_segments(struct f2fs_sb_info *sbi, struct cp_control *cpc)
                        continue;
                }
 next:
-               secno = GET_SECNO(sbi, start);
-               start_segno = secno * sbi->segs_per_sec;
+               secno = GET_SEC_FROM_SEG(sbi, start);
+               start_segno = GET_SEG_FROM_SEC(sbi, secno);
                if (!IS_CURSEC(sbi, secno) &&
-                       !get_valid_blocks(sbi, start, sbi->segs_per_sec))
+                       !get_valid_blocks(sbi, start, true))
                        f2fs_issue_discard(sbi, START_BLOCK(sbi, start_segno),
                                sbi->segs_per_sec << sbi->log_blocks_per_seg);
 
                start = start_segno + sbi->segs_per_sec;
                if (start < end)
                        goto next;
+               else
+                       end = start - 1;
        }
        mutex_unlock(&dirty_i->seglist_lock);
 
        /* send small discards */
        list_for_each_entry_safe(entry, this, head, list) {
-               if (force && entry->len < cpc->trim_minlen)
-                       goto skip;
-               f2fs_issue_discard(sbi, entry->blkaddr, entry->len);
-               cpc->trimmed += entry->len;
+               unsigned int cur_pos = 0, next_pos, len, total_len = 0;
+               bool is_valid = test_bit_le(0, entry->discard_map);
+
+find_next:
+               if (is_valid) {
+                       next_pos = find_next_zero_bit_le(entry->discard_map,
+                                       sbi->blocks_per_seg, cur_pos);
+                       len = next_pos - cur_pos;
+
+                       if (f2fs_sb_mounted_blkzoned(sbi->sb) ||
+                           (force && len < cpc->trim_minlen))
+                               goto skip;
+
+                       f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos,
+                                                                       len);
+                       cpc->trimmed += len;
+                       total_len += len;
+               } else {
+                       next_pos = find_next_bit_le(entry->discard_map,
+                                       sbi->blocks_per_seg, cur_pos);
+               }
 skip:
+               cur_pos = next_pos;
+               is_valid = !is_valid;
+
+               if (cur_pos < sbi->blocks_per_seg)
+                       goto find_next;
+
                list_del(&entry->list);
-               SM_I(sbi)->nr_discards -= entry->len;
+               dcc->nr_discards -= total_len;
                kmem_cache_free(discard_entry_slab, entry);
        }
 
-       blk_finish_plug(&plug);
+       wake_up_discard_thread(sbi, false);
+}
+
+static int create_discard_cmd_control(struct f2fs_sb_info *sbi)
+{
+       dev_t dev = sbi->sb->s_bdev->bd_dev;
+       struct discard_cmd_control *dcc;
+       int err = 0, i;
+
+       if (SM_I(sbi)->dcc_info) {
+               dcc = SM_I(sbi)->dcc_info;
+               goto init_thread;
+       }
+
+       dcc = kzalloc(sizeof(struct discard_cmd_control), GFP_KERNEL);
+       if (!dcc)
+               return -ENOMEM;
+
+       dcc->discard_granularity = DEFAULT_DISCARD_GRANULARITY;
+       INIT_LIST_HEAD(&dcc->entry_list);
+       for (i = 0; i < MAX_PLIST_NUM; i++) {
+               INIT_LIST_HEAD(&dcc->pend_list[i]);
+               if (i >= dcc->discard_granularity - 1)
+                       dcc->pend_list_tag[i] |= P_ACTIVE;
+       }
+       INIT_LIST_HEAD(&dcc->wait_list);
+       mutex_init(&dcc->cmd_lock);
+       atomic_set(&dcc->issued_discard, 0);
+       atomic_set(&dcc->issing_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->root = RB_ROOT;
+
+       init_waitqueue_head(&dcc->discard_wait_queue);
+       SM_I(sbi)->dcc_info = dcc;
+init_thread:
+       dcc->f2fs_issue_discard = kthread_run(issue_discard_thread, sbi,
+                               "f2fs_discard-%u:%u", MAJOR(dev), MINOR(dev));
+       if (IS_ERR(dcc->f2fs_issue_discard)) {
+               err = PTR_ERR(dcc->f2fs_issue_discard);
+               kfree(dcc);
+               SM_I(sbi)->dcc_info = NULL;
+               return err;
+       }
+
+       return err;
+}
+
+static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi)
+{
+       struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
+
+       if (!dcc)
+               return;
+
+       stop_discard_thread(sbi);
+
+       kfree(dcc);
+       SM_I(sbi)->dcc_info = NULL;
 }
 
 static bool __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno)
@@ -1085,6 +1712,10 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
        struct seg_entry *se;
        unsigned int segno, offset;
        long int new_vblocks;
+       bool exist;
+#ifdef CONFIG_F2FS_CHECK_FS
+       bool mir_exist;
+#endif
 
        segno = GET_SEGNO(sbi, blkaddr);
 
@@ -1101,32 +1732,54 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
 
        /* Update valid block bitmap */
        if (del > 0) {
-               if (f2fs_test_and_set_bit(offset, se->cur_valid_map)) {
+               exist = f2fs_test_and_set_bit(offset, se->cur_valid_map);
 #ifdef CONFIG_F2FS_CHECK_FS
-                       if (f2fs_test_and_set_bit(offset,
-                                               se->cur_valid_map_mir))
-                               f2fs_bug_on(sbi, 1);
-                       else
-                               WARN_ON(1);
-#else
+               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_bug_on(sbi, 1);
+               }
 #endif
+               if (unlikely(exist)) {
+                       f2fs_msg(sbi->sb, KERN_ERR,
+                               "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))
                        sbi->discard_blks--;
+
+               /* don't overwrite by SSR to keep node chain */
+               if (se->type == CURSEG_WARM_NODE) {
+                       if (!f2fs_test_and_set_bit(offset, se->ckpt_valid_map))
+                               se->ckpt_valid_blocks++;
+               }
        } else {
-               if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map)) {
+               exist = f2fs_test_and_clear_bit(offset, se->cur_valid_map);
 #ifdef CONFIG_F2FS_CHECK_FS
-                       if (!f2fs_test_and_clear_bit(offset,
-                                               se->cur_valid_map_mir))
-                               f2fs_bug_on(sbi, 1);
-                       else
-                               WARN_ON(1);
-#else
+               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_bug_on(sbi, 1);
+               }
 #endif
+               if (unlikely(!exist)) {
+                       f2fs_msg(sbi->sb, KERN_ERR,
+                               "Bitmap was wrongly cleared, blk:%u", blkaddr);
+                       f2fs_bug_on(sbi, 1);
+                       se->valid_blocks++;
+                       del = 0;
                }
+
                if (f2fs_discard_en(sbi) &&
                        f2fs_test_and_clear_bit(offset, se->discard_map))
                        sbi->discard_blks++;
@@ -1312,8 +1965,8 @@ static void get_new_segment(struct f2fs_sb_info *sbi,
        struct free_segmap_info *free_i = FREE_I(sbi);
        unsigned int segno, secno, zoneno;
        unsigned int total_zones = MAIN_SECS(sbi) / sbi->secs_per_zone;
-       unsigned int hint = *newseg / sbi->segs_per_sec;
-       unsigned int old_zoneno = GET_ZONENO_FROM_SEGNO(sbi, *newseg);
+       unsigned int hint = GET_SEC_FROM_SEG(sbi, *newseg);
+       unsigned int old_zoneno = GET_ZONE_FROM_SEG(sbi, *newseg);
        unsigned int left_start = hint;
        bool init = true;
        int go_left = 0;
@@ -1323,8 +1976,8 @@ static void get_new_segment(struct f2fs_sb_info *sbi,
 
        if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) {
                segno = find_next_zero_bit(free_i->free_segmap,
-                               (hint + 1) * sbi->segs_per_sec, *newseg + 1);
-               if (segno < (hint + 1) * sbi->segs_per_sec)
+                       GET_SEG_FROM_SEC(sbi, hint + 1), *newseg + 1);
+               if (segno < GET_SEG_FROM_SEC(sbi, hint + 1))
                        goto got_it;
        }
 find_other_zone:
@@ -1355,8 +2008,8 @@ find_other_zone:
        secno = left_start;
 skip_left:
        hint = secno;
-       segno = secno * sbi->segs_per_sec;
-       zoneno = secno / sbi->secs_per_zone;
+       segno = GET_SEG_FROM_SEC(sbi, secno);
+       zoneno = GET_ZONE_FROM_SEC(sbi, secno);
 
        /* give up on finding another zone */
        if (!init)
@@ -1400,7 +2053,7 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified)
        struct summary_footer *sum_footer;
 
        curseg->segno = curseg->next_segno;
-       curseg->zone = GET_ZONENO_FROM_SEGNO(sbi, curseg->segno);
+       curseg->zone = GET_ZONE_FROM_SEG(sbi, curseg->segno);
        curseg->next_blkoff = 0;
        curseg->next_segno = NULL_SEGNO;
 
@@ -1413,6 +2066,20 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified)
        __set_sit_entry_type(sbi, type, curseg->segno, 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)
+               return CURSEG_I(sbi, type)->segno;
+
+       if (type == CURSEG_HOT_DATA || IS_NODESEG(type))
+               return 0;
+
+       if (SIT_I(sbi)->last_victim[ALLOC_NEXT])
+               return SIT_I(sbi)->last_victim[ALLOC_NEXT];
+       return CURSEG_I(sbi, type)->segno;
+}
+
 /*
  * Allocate a current working segment.
  * This function always allocates a free segment in LFS manner.
@@ -1431,6 +2098,7 @@ static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec)
        if (test_opt(sbi, NOHEAP))
                dir = ALLOC_RIGHT;
 
+       segno = __get_next_segno(sbi, type);
        get_new_segment(sbi, &segno, new_sec, dir);
        curseg->next_segno = segno;
        reset_curseg(sbi, type, 1);
@@ -1473,7 +2141,7 @@ static void __refresh_next_blkoff(struct f2fs_sb_info *sbi,
  * This function always allocates a used segment(from dirty seglist) by SSR
  * manner, so it should recover the existing segment information of valid blocks
  */
-static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse)
+static void change_curseg(struct f2fs_sb_info *sbi, int type)
 {
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
        struct curseg_info *curseg = CURSEG_I(sbi, type);
@@ -1494,28 +2162,53 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse)
        curseg->alloc_type = SSR;
        __next_free_blkoff(sbi, curseg, 0);
 
-       if (reuse) {
-               sum_page = get_sum_page(sbi, new_segno);
-               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);
-       }
+       sum_page = get_sum_page(sbi, new_segno);
+       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);
 }
 
 static int get_ssr_segment(struct f2fs_sb_info *sbi, int type)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, type);
        const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops;
+       unsigned segno = NULL_SEGNO;
+       int i, cnt;
+       bool reversed = false;
+
+       /* need_SSR() already forces to do this */
+       if (v_ops->get_victim(sbi, &segno, BG_GC, type, SSR)) {
+               curseg->next_segno = segno;
+               return 1;
+       }
 
-       if (IS_NODESEG(type) || !has_not_enough_free_secs(sbi, 0, 0))
-               return v_ops->get_victim(sbi,
-                               &(curseg)->next_segno, BG_GC, type, SSR);
+       /* For node segments, let's do SSR more intensively */
+       if (IS_NODESEG(type)) {
+               if (type >= CURSEG_WARM_NODE) {
+                       reversed = true;
+                       i = CURSEG_COLD_NODE;
+               } else {
+                       i = CURSEG_HOT_NODE;
+               }
+               cnt = NR_CURSEG_NODE_TYPE;
+       } else {
+               if (type >= CURSEG_WARM_DATA) {
+                       reversed = true;
+                       i = CURSEG_COLD_DATA;
+               } else {
+                       i = CURSEG_HOT_DATA;
+               }
+               cnt = NR_CURSEG_DATA_TYPE;
+       }
 
-       /* For data segments, let's do SSR more intensively */
-       for (; type >= CURSEG_HOT_DATA; type--)
-               if (v_ops->get_victim(sbi, &(curseg)->next_segno,
-                                               BG_GC, type, SSR))
+       for (; cnt-- > 0; reversed ? i-- : i++) {
+               if (i == type)
+                       continue;
+               if (v_ops->get_victim(sbi, &segno, BG_GC, i, SSR)) {
+                       curseg->next_segno = segno;
                        return 1;
+               }
+       }
        return 0;
 }
 
@@ -1530,12 +2223,13 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
 
        if (force)
                new_curseg(sbi, type, true);
-       else if (type == CURSEG_WARM_NODE)
+       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))
                new_curseg(sbi, type, false);
        else if (need_SSR(sbi) && get_ssr_segment(sbi, type))
-               change_curseg(sbi, type, true);
+               change_curseg(sbi, type);
        else
                new_curseg(sbi, type, false);
 
@@ -1563,14 +2257,19 @@ static const struct segment_allocation default_salloc_ops = {
 bool exist_trim_candidates(struct f2fs_sb_info *sbi, struct cp_control *cpc)
 {
        __u64 trim_start = cpc->trim_start;
+       bool has_candidate = false;
 
        mutex_lock(&SIT_I(sbi)->sentry_lock);
-       for (; trim_start <= cpc->trim_end; trim_start++)
-               if (add_discard_addrs(sbi, cpc, true))
+       for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++) {
+               if (add_discard_addrs(sbi, cpc, true)) {
+                       has_candidate = true;
                        break;
+               }
+       }
        mutex_unlock(&SIT_I(sbi)->sentry_lock);
 
-       return trim_start <= cpc->trim_end;
+       cpc->trim_start = trim_start;
+       return has_candidate;
 }
 
 int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
@@ -1623,6 +2322,9 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)
 
                schedule();
        }
+       /* It's time to issue all the filed discards */
+       mark_discard_range_all(sbi);
+       f2fs_wait_discard_bios(sbi);
 out:
        range->len = F2FS_BLK_TO_BYTES(cpc.trimmed);
        return err;
@@ -1636,68 +2338,80 @@ static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type)
        return false;
 }
 
-static int __get_segment_type_2(struct page *page, enum page_type p_type)
+static int __get_segment_type_2(struct f2fs_io_info *fio)
 {
-       if (p_type == DATA)
+       if (fio->type == DATA)
                return CURSEG_HOT_DATA;
        else
                return CURSEG_HOT_NODE;
 }
 
-static int __get_segment_type_4(struct page *page, enum page_type p_type)
+static int __get_segment_type_4(struct f2fs_io_info *fio)
 {
-       if (p_type == DATA) {
-               struct inode *inode = page->mapping->host;
+       if (fio->type == DATA) {
+               struct inode *inode = fio->page->mapping->host;
 
                if (S_ISDIR(inode->i_mode))
                        return CURSEG_HOT_DATA;
                else
                        return CURSEG_COLD_DATA;
        } else {
-               if (IS_DNODE(page) && is_cold_node(page))
+               if (IS_DNODE(fio->page) && is_cold_node(fio->page))
                        return CURSEG_WARM_NODE;
                else
                        return CURSEG_COLD_NODE;
        }
 }
 
-static int __get_segment_type_6(struct page *page, enum page_type p_type)
+static int __get_segment_type_6(struct f2fs_io_info *fio)
 {
-       if (p_type == DATA) {
-               struct inode *inode = page->mapping->host;
+       if (fio->type == DATA) {
+               struct inode *inode = fio->page->mapping->host;
 
-               if (S_ISDIR(inode->i_mode))
-                       return CURSEG_HOT_DATA;
-               else if (is_cold_data(page) || file_is_cold(inode))
+               if (is_cold_data(fio->page) || file_is_cold(inode))
                        return CURSEG_COLD_DATA;
-               else
-                       return CURSEG_WARM_DATA;
+               if (is_inode_flag_set(inode, FI_HOT_DATA))
+                       return CURSEG_HOT_DATA;
+               return CURSEG_WARM_DATA;
        } else {
-               if (IS_DNODE(page))
-                       return is_cold_node(page) ? CURSEG_WARM_NODE :
+               if (IS_DNODE(fio->page))
+                       return is_cold_node(fio->page) ? CURSEG_WARM_NODE :
                                                CURSEG_HOT_NODE;
-               else
-                       return CURSEG_COLD_NODE;
+               return CURSEG_COLD_NODE;
        }
 }
 
-static int __get_segment_type(struct page *page, enum page_type p_type)
+static int __get_segment_type(struct f2fs_io_info *fio)
 {
-       switch (F2FS_P_SB(page)->active_logs) {
+       int type = 0;
+
+       switch (fio->sbi->active_logs) {
        case 2:
-               return __get_segment_type_2(page, p_type);
+               type = __get_segment_type_2(fio);
+               break;
        case 4:
-               return __get_segment_type_4(page, p_type);
+               type = __get_segment_type_4(fio);
+               break;
+       case 6:
+               type = __get_segment_type_6(fio);
+               break;
+       default:
+               f2fs_bug_on(fio->sbi, true);
        }
-       /* NR_CURSEG_TYPE(6) logs by default */
-       f2fs_bug_on(F2FS_P_SB(page),
-               F2FS_P_SB(page)->active_logs != NR_CURSEG_TYPE);
-       return __get_segment_type_6(page, p_type);
+
+       if (IS_HOT(type))
+               fio->temp = HOT;
+       else if (IS_WARM(type))
+               fio->temp = WARM;
+       else
+               fio->temp = COLD;
+       return type;
 }
 
 void 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_summary *sum, int type,
+               struct f2fs_io_info *fio, bool add_list)
 {
        struct sit_info *sit_i = SIT_I(sbi);
        struct curseg_info *curseg = CURSEG_I(sbi, type);
@@ -1723,42 +2437,52 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
        if (!__has_curseg_space(sbi, type))
                sit_i->s_ops->allocate_segment(sbi, type, false);
        /*
-        * SIT information should be updated before segment allocation,
-        * since SSR needs latest valid block information.
+        * SIT information should be updated after segment allocation,
+        * since we need to keep dirty segments precisely under SSR.
         */
        refresh_sit_entry(sbi, old_blkaddr, *new_blkaddr);
 
        mutex_unlock(&sit_i->sentry_lock);
 
-       if (page && IS_NODESEG(type))
+       if (page && IS_NODESEG(type)) {
                fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg));
 
+               f2fs_inode_chksum_set(sbi, page);
+       }
+
+       if (add_list) {
+               struct f2fs_bio_info *io;
+
+               INIT_LIST_HEAD(&fio->list);
+               fio->in_list = true;
+               io = sbi->write_io[fio->type] + fio->temp;
+               spin_lock(&io->io_lock);
+               list_add_tail(&fio->list, &io->io_list);
+               spin_unlock(&io->io_lock);
+       }
+
        mutex_unlock(&curseg->curseg_mutex);
 }
 
 static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio)
 {
-       int type = __get_segment_type(fio->page, fio->type);
+       int type = __get_segment_type(fio);
        int err;
 
-       if (fio->type == NODE || fio->type == DATA)
-               mutex_lock(&fio->sbi->wio_mutex[fio->type]);
 reallocate:
        allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
-                                       &fio->new_blkaddr, sum, type);
+                       &fio->new_blkaddr, sum, type, fio, true);
 
        /* writeout dirty page into bdev */
-       err = f2fs_submit_page_mbio(fio);
+       err = f2fs_submit_page_write(fio);
        if (err == -EAGAIN) {
                fio->old_blkaddr = fio->new_blkaddr;
                goto reallocate;
        }
-
-       if (fio->type == NODE || fio->type == DATA)
-               mutex_unlock(&fio->sbi->wio_mutex[fio->type]);
 }
 
-void write_meta_page(struct f2fs_sb_info *sbi, struct page *page)
+void write_meta_page(struct f2fs_sb_info *sbi, struct page *page,
+                                       enum iostat_type io_type)
 {
        struct f2fs_io_info fio = {
                .sbi = sbi,
@@ -1769,13 +2493,16 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page)
                .new_blkaddr = page->index,
                .page = page,
                .encrypted_page = NULL,
+               .in_list = false,
        };
 
        if (unlikely(page->index >= MAIN_BLKADDR(sbi)))
                fio.op_flags &= ~REQ_META;
 
        set_page_writeback(page);
-       f2fs_submit_page_mbio(&fio);
+       f2fs_submit_page_write(&fio);
+
+       f2fs_update_iostat(sbi, io_type, F2FS_BLKSIZE);
 }
 
 void write_node_page(unsigned int nid, struct f2fs_io_info *fio)
@@ -1784,6 +2511,8 @@ void write_node_page(unsigned int nid, struct f2fs_io_info *fio)
 
        set_summary(&sum, nid, 0, 0);
        do_write_page(&sum, 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)
@@ -1797,13 +2526,22 @@ void write_data_page(struct dnode_of_data *dn, struct f2fs_io_info *fio)
        set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version);
        do_write_page(&sum, fio);
        f2fs_update_data_blkaddr(dn, fio->new_blkaddr);
+
+       f2fs_update_iostat(sbi, fio->io_type, F2FS_BLKSIZE);
 }
 
-void rewrite_data_page(struct f2fs_io_info *fio)
+int rewrite_data_page(struct f2fs_io_info *fio)
 {
+       int err;
+
        fio->new_blkaddr = fio->old_blkaddr;
        stat_inc_inplace_blocks(fio->sbi);
-       f2fs_submit_page_mbio(fio);
+
+       err = f2fs_submit_page_bio(fio);
+
+       f2fs_update_iostat(fio->sbi, fio->io_type, F2FS_BLKSIZE);
+
+       return err;
 }
 
 void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
@@ -1845,7 +2583,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
        /* change the current segment */
        if (segno != curseg->segno) {
                curseg->next_segno = segno;
-               change_curseg(sbi, type, true);
+               change_curseg(sbi, type);
        }
 
        curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
@@ -1864,7 +2602,7 @@ void __f2fs_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
        if (recover_curseg) {
                if (old_cursegno != curseg->segno) {
                        curseg->next_segno = old_cursegno;
-                       change_curseg(sbi, type, true);
+                       change_curseg(sbi, type);
                }
                curseg->next_blkoff = old_blkoff;
        }
@@ -1894,7 +2632,8 @@ void f2fs_wait_on_page_writeback(struct page *page,
        if (PageWriteback(page)) {
                struct f2fs_sb_info *sbi = F2FS_P_SB(page);
 
-               f2fs_submit_merged_bio_cond(sbi, NULL, page, 0, type, WRITE);
+               f2fs_submit_merged_write_cond(sbi, page->mapping->host,
+                                               0, page->index, type);
                if (ordered)
                        wait_on_page_writeback(page);
                else
@@ -1902,8 +2641,7 @@ void f2fs_wait_on_page_writeback(struct page *page,
        }
 }
 
-void f2fs_wait_on_encrypted_page_writeback(struct f2fs_sb_info *sbi,
-                                                       block_t blkaddr)
+void f2fs_wait_on_block_writeback(struct f2fs_sb_info *sbi, block_t blkaddr)
 {
        struct page *cpage;
 
@@ -2052,6 +2790,8 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
 
 static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
 {
+       struct f2fs_journal *sit_j = CURSEG_I(sbi, CURSEG_COLD_DATA)->journal;
+       struct f2fs_journal *nat_j = CURSEG_I(sbi, CURSEG_HOT_DATA)->journal;
        int type = CURSEG_HOT_DATA;
        int err;
 
@@ -2078,6 +2818,11 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
                        return err;
        }
 
+       /* sanity check for summary blocks */
+       if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES ||
+                       sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES)
+               return -EINVAL;
+
        return 0;
 }
 
@@ -2367,7 +3112,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
                        se = get_seg_entry(sbi, segno);
 
                        /* add discard candidates */
-                       if (cpc->reason != CP_DISCARD) {
+                       if (!(cpc->reason & CP_DISCARD)) {
                                cpc->trim_start = segno;
                                add_discard_addrs(sbi, cpc, false);
                        }
@@ -2403,7 +3148,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
        f2fs_bug_on(sbi, !list_empty(head));
        f2fs_bug_on(sbi, sit_i->dirty_sentries);
 out:
-       if (cpc->reason == CP_DISCARD) {
+       if (cpc->reason & CP_DISCARD) {
                __u64 trim_start = cpc->trim_start;
 
                for (; cpc->trim_start <= cpc->trim_end; cpc->trim_start++)
@@ -2431,13 +3176,13 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
 
        SM_I(sbi)->sit_info = sit_i;
 
-       sit_i->sentries = f2fs_kvzalloc(MAIN_SEGS(sbi) *
+       sit_i->sentries = kvzalloc(MAIN_SEGS(sbi) *
                                        sizeof(struct seg_entry), GFP_KERNEL);
        if (!sit_i->sentries)
                return -ENOMEM;
 
        bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi));
-       sit_i->dirty_sentries_bitmap = f2fs_kvzalloc(bitmap_size, GFP_KERNEL);
+       sit_i->dirty_sentries_bitmap = kvzalloc(bitmap_size, GFP_KERNEL);
        if (!sit_i->dirty_sentries_bitmap)
                return -ENOMEM;
 
@@ -2470,7 +3215,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
                return -ENOMEM;
 
        if (sbi->segs_per_sec > 1) {
-               sit_i->sec_entries = f2fs_kvzalloc(MAIN_SECS(sbi) *
+               sit_i->sec_entries = kvzalloc(MAIN_SECS(sbi) *
                                        sizeof(struct sec_entry), GFP_KERNEL);
                if (!sit_i->sec_entries)
                        return -ENOMEM;
@@ -2521,12 +3266,12 @@ static int build_free_segmap(struct f2fs_sb_info *sbi)
        SM_I(sbi)->free_info = free_i;
 
        bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi));
-       free_i->free_segmap = f2fs_kvmalloc(bitmap_size, GFP_KERNEL);
+       free_i->free_segmap = kvmalloc(bitmap_size, GFP_KERNEL);
        if (!free_i->free_segmap)
                return -ENOMEM;
 
        sec_bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi));
-       free_i->free_secmap = f2fs_kvmalloc(sec_bitmap_size, GFP_KERNEL);
+       free_i->free_secmap = kvmalloc(sec_bitmap_size, GFP_KERNEL);
        if (!free_i->free_secmap)
                return -ENOMEM;
 
@@ -2602,10 +3347,17 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
 
                        /* build discard map only one time */
                        if (f2fs_discard_en(sbi)) {
-                               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)
@@ -2629,10 +3381,15 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)
                seg_info_from_raw_sit(se, &sit);
 
                if (f2fs_discard_en(sbi)) {
-                       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 -
+                                                       se->valid_blocks;
+                       }
                }
 
                if (sbi->segs_per_sec > 1)
@@ -2676,7 +3433,7 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
                if (segno >= MAIN_SEGS(sbi))
                        break;
                offset = segno + 1;
-               valid_blocks = get_valid_blocks(sbi, segno, 0);
+               valid_blocks = get_valid_blocks(sbi, segno, false);
                if (valid_blocks == sbi->blocks_per_seg || !valid_blocks)
                        continue;
                if (valid_blocks > sbi->blocks_per_seg) {
@@ -2694,7 +3451,7 @@ static int init_victim_secmap(struct f2fs_sb_info *sbi)
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
        unsigned int bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi));
 
-       dirty_i->victim_secmap = f2fs_kvzalloc(bitmap_size, GFP_KERNEL);
+       dirty_i->victim_secmap = kvzalloc(bitmap_size, GFP_KERNEL);
        if (!dirty_i->victim_secmap)
                return -ENOMEM;
        return 0;
@@ -2716,7 +3473,7 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi)
        bitmap_size = f2fs_bitmap_size(MAIN_SEGS(sbi));
 
        for (i = 0; i < NR_DIRTY_TYPE; i++) {
-               dirty_i->dirty_segmap[i] = f2fs_kvzalloc(bitmap_size, GFP_KERNEL);
+               dirty_i->dirty_segmap[i] = kvzalloc(bitmap_size, GFP_KERNEL);
                if (!dirty_i->dirty_segmap[i])
                        return -ENOMEM;
        }
@@ -2782,22 +3539,22 @@ 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;
-
-       INIT_LIST_HEAD(&sm_info->discard_entry_list);
-       INIT_LIST_HEAD(&sm_info->discard_cmd_list);
-       sm_info->nr_discards = 0;
-       sm_info->max_discards = 0;
+       sm_info->min_hot_blocks = DEF_MIN_HOT_BLOCKS;
 
        sm_info->trim_sections = DEF_BATCHED_TRIM_SECTIONS;
 
        INIT_LIST_HEAD(&sm_info->sit_entry_set);
 
-       if (test_opt(sbi, FLUSH_MERGE) && !f2fs_readonly(sbi->sb)) {
+       if (!f2fs_readonly(sbi->sb)) {
                err = create_flush_cmd_control(sbi);
                if (err)
                        return err;
        }
 
+       err = create_discard_cmd_control(sbi);
+       if (err)
+               return err;
+
        err = build_sit_info(sbi);
        if (err)
                return err;
@@ -2919,6 +3676,7 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi)
        if (!sm_info)
                return;
        destroy_flush_cmd_control(sbi, true);
+       destroy_discard_cmd_control(sbi);
        destroy_dirty_segmap(sbi);
        destroy_curseg(sbi);
        destroy_free_segmap(sbi);