epoll: simplify signal handling
[linux-2.6-microblaze.git] / fs / f2fs / segment.c
index e247a5e..deca74c 100644 (file)
@@ -189,7 +189,7 @@ void f2fs_register_inmem_page(struct inode *inode, struct page *page)
 
        f2fs_trace_pid(page);
 
-       f2fs_set_page_private(page, (unsigned long)ATOMIC_WRITTEN_PAGE);
+       f2fs_set_page_private(page, ATOMIC_WRITTEN_PAGE);
 
        new = f2fs_kmem_cache_alloc(inmem_entry_slab, GFP_NOFS);
 
@@ -529,31 +529,38 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi, bool from_bg)
        else
                f2fs_build_free_nids(sbi, false, false);
 
-       if (!is_idle(sbi, REQ_TIME) &&
-               (!excess_dirty_nats(sbi) && !excess_dirty_nodes(sbi)))
+       if (excess_dirty_nats(sbi) || excess_dirty_nodes(sbi) ||
+               excess_prefree_segs(sbi))
+               goto do_sync;
+
+       /* there is background inflight IO or foreground operation recently */
+       if (is_inflight_io(sbi, REQ_TIME) ||
+               (!f2fs_time_over(sbi, REQ_TIME) && rwsem_is_locked(&sbi->cp_rwsem)))
                return;
 
+       /* exceed periodical checkpoint timeout threshold */
+       if (f2fs_time_over(sbi, CP_TIME))
+               goto do_sync;
+
        /* checkpoint is the only way to shrink partial cached 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) && from_bg) {
-                       struct blk_plug plug;
-
-                       mutex_lock(&sbi->flush_lock);
-
-                       blk_start_plug(&plug);
-                       f2fs_sync_dirty_inodes(sbi, FILE_INODE);
-                       blk_finish_plug(&plug);
+       if (f2fs_available_free_memory(sbi, NAT_ENTRIES) ||
+               f2fs_available_free_memory(sbi, INO_ENTRIES))
+               return;
 
-                       mutex_unlock(&sbi->flush_lock);
-               }
-               f2fs_sync_fs(sbi->sb, true);
-               stat_inc_bg_cp_count(sbi->stat_info);
+do_sync:
+       if (test_opt(sbi, DATA_FLUSH) && from_bg) {
+               struct blk_plug plug;
+
+               mutex_lock(&sbi->flush_lock);
+
+               blk_start_plug(&plug);
+               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);
 }
 
 static int __submit_flush_wait(struct f2fs_sb_info *sbi,
@@ -728,7 +735,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);
-               kvfree(fcc);
+               kfree(fcc);
                SM_I(sbi)->fcc_info = NULL;
                return err;
        }
@@ -747,7 +754,7 @@ void f2fs_destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free)
                kthread_stop(flush_thread);
        }
        if (free) {
-               kvfree(fcc);
+               kfree(fcc);
                SM_I(sbi)->fcc_info = NULL;
        }
 }
@@ -759,6 +766,9 @@ int f2fs_flush_device_cache(struct f2fs_sb_info *sbi)
        if (!f2fs_is_multi_device(sbi))
                return 0;
 
+       if (test_opt(sbi, NOBARRIER))
+               return 0;
+
        for (i = 1; i < sbi->s_ndevs; i++) {
                if (!f2fs_test_bit(i, (char *)&sbi->dirty_device))
                        continue;
@@ -859,20 +869,22 @@ 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, ckpt_valid_blocks;
+       unsigned int usable_blocks;
 
        if (segno == NULL_SEGNO || IS_CURSEG(sbi, segno))
                return;
 
+       usable_blocks = f2fs_usable_blks_in_seg(sbi, 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 && (!is_sbi_flag_set(sbi, SBI_CP_DISABLED) ||
-                               ckpt_valid_blocks == sbi->blocks_per_seg)) {
+               ckpt_valid_blocks == usable_blocks)) {
                __locate_dirty_segment(sbi, segno, PRE);
                __remove_dirty_segment(sbi, segno, DIRTY);
-       } else if (valid_blocks < sbi->blocks_per_seg) {
+       } else if (valid_blocks < usable_blocks) {
                __locate_dirty_segment(sbi, segno, DIRTY);
        } else {
                /* Recovery routine with SSR needs this */
@@ -915,9 +927,11 @@ block_t f2fs_get_unusable_blocks(struct f2fs_sb_info *sbi)
        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;
+                       holes[NODE] += f2fs_usable_blks_in_seg(sbi, segno) -
+                                                       se->valid_blocks;
                else
-                       holes[DATA] += sbi->blocks_per_seg - se->valid_blocks;
+                       holes[DATA] += f2fs_usable_blks_in_seg(sbi, segno) -
+                                                       se->valid_blocks;
        }
        mutex_unlock(&dirty_i->seglist_lock);
 
@@ -1521,7 +1535,7 @@ retry:
                        goto next;
                if (unlikely(dcc->rbtree_check))
                        f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi,
-                                                               &dcc->root));
+                                                       &dcc->root, false));
                blk_start_plug(&plug);
                list_for_each_entry_safe(dc, tmp, pend_list, list) {
                        f2fs_bug_on(sbi, dc->state != D_PREP);
@@ -1958,7 +1972,7 @@ static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi)
 
        mutex_lock(&dirty_i->seglist_lock);
        for_each_set_bit(segno, dirty_i->dirty_segmap[PRE], MAIN_SEGS(sbi))
-               __set_test_and_free(sbi, segno);
+               __set_test_and_free(sbi, segno, false);
        mutex_unlock(&dirty_i->seglist_lock);
 }
 
@@ -2101,7 +2115,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);
-               kvfree(dcc);
+               kfree(dcc);
                SM_I(sbi)->dcc_info = NULL;
                return err;
        }
@@ -2125,7 +2139,7 @@ static void destroy_discard_cmd_control(struct f2fs_sb_info *sbi)
        if (unlikely(atomic_read(&dcc->discard_cmd_cnt)))
                f2fs_issue_discard_timeout(sbi);
 
-       kvfree(dcc);
+       kfree(dcc);
        SM_I(sbi)->dcc_info = NULL;
 }
 
@@ -2150,6 +2164,39 @@ static void __set_sit_entry_type(struct f2fs_sb_info *sbi, int type,
                __mark_sit_entry_dirty(sbi, segno);
 }
 
+static inline unsigned long long get_segment_mtime(struct f2fs_sb_info *sbi,
+                                                               block_t blkaddr)
+{
+       unsigned int segno = GET_SEGNO(sbi, blkaddr);
+
+       if (segno == NULL_SEGNO)
+               return 0;
+       return get_seg_entry(sbi, segno)->mtime;
+}
+
+static void update_segment_mtime(struct f2fs_sb_info *sbi, block_t blkaddr,
+                                               unsigned long long old_mtime)
+{
+       struct seg_entry *se;
+       unsigned int segno = GET_SEGNO(sbi, blkaddr);
+       unsigned long long ctime = get_mtime(sbi, false);
+       unsigned long long mtime = old_mtime ? old_mtime : ctime;
+
+       if (segno == NULL_SEGNO)
+               return;
+
+       se = get_seg_entry(sbi, segno);
+
+       if (!se->mtime)
+               se->mtime = mtime;
+       else
+               se->mtime = div_u64(se->mtime * se->valid_blocks + mtime,
+                                               se->valid_blocks + 1);
+
+       if (ctime > SIT_I(sbi)->max_mtime)
+               SIT_I(sbi)->max_mtime = ctime;
+}
+
 static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
 {
        struct seg_entry *se;
@@ -2167,12 +2214,9 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)
        offset = GET_BLKOFF_FROM_SEG0(sbi, blkaddr);
 
        f2fs_bug_on(sbi, (new_vblocks < 0 ||
-                               (new_vblocks > sbi->blocks_per_seg)));
+                       (new_vblocks > f2fs_usable_blks_in_seg(sbi, segno))));
 
        se->valid_blocks = new_vblocks;
-       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) {
@@ -2265,6 +2309,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
        /* add it into sit main buffer */
        down_write(&sit_i->sentry_lock);
 
+       update_segment_mtime(sbi, addr, 0);
        update_sit_entry(sbi, addr, -1);
 
        /* add it into dirty seglist */
@@ -2344,7 +2389,9 @@ int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra)
  */
 struct page *f2fs_get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno)
 {
-       return f2fs_get_meta_page_nofail(sbi, GET_SUM_BLOCK(sbi, segno));
+       if (unlikely(f2fs_cp_error(sbi)))
+               return ERR_PTR(-EIO);
+       return f2fs_get_meta_page_retry(sbi, GET_SUM_BLOCK(sbi, segno));
 }
 
 void f2fs_update_meta_page(struct f2fs_sb_info *sbi,
@@ -2389,9 +2436,9 @@ static void write_current_sum_page(struct f2fs_sb_info *sbi,
        f2fs_put_page(page, 1);
 }
 
-static int is_next_segment_free(struct f2fs_sb_info *sbi, int type)
+static int is_next_segment_free(struct f2fs_sb_info *sbi,
+                               struct curseg_info *curseg, int type)
 {
-       struct curseg_info *curseg = CURSEG_I(sbi, type);
        unsigned int segno = curseg->segno + 1;
        struct free_segmap_info *free_i = FREE_I(sbi);
 
@@ -2495,7 +2542,9 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, type);
        struct summary_footer *sum_footer;
+       unsigned short seg_type = curseg->seg_type;
 
+       curseg->inited = true;
        curseg->segno = curseg->next_segno;
        curseg->zone = GET_ZONE_FROM_SEG(sbi, curseg->segno);
        curseg->next_blkoff = 0;
@@ -2503,24 +2552,36 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified)
 
        sum_footer = &(curseg->sum_blk->footer);
        memset(sum_footer, 0, sizeof(struct summary_footer));
-       if (IS_DATASEG(type))
+
+       sanity_check_seg_type(sbi, seg_type);
+
+       if (IS_DATASEG(seg_type))
                SET_SUM_TYPE(sum_footer, SUM_TYPE_DATA);
-       if (IS_NODESEG(type))
+       if (IS_NODESEG(seg_type))
                SET_SUM_TYPE(sum_footer, SUM_TYPE_NODE);
-       __set_sit_entry_type(sbi, type, curseg->segno, modified);
+       __set_sit_entry_type(sbi, seg_type, curseg->segno, modified);
 }
 
 static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type)
 {
+       struct curseg_info *curseg = CURSEG_I(sbi, type);
+       unsigned short seg_type = curseg->seg_type;
+
+       sanity_check_seg_type(sbi, seg_type);
+
        /* if segs_per_sec is large than 1, we need to keep original policy. */
        if (__is_large_section(sbi))
-               return CURSEG_I(sbi, type)->segno;
+               return curseg->segno;
+
+       /* inmem log may not locate on any segment after mount */
+       if (!curseg->inited)
+               return 0;
 
        if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
                return 0;
 
        if (test_opt(sbi, NOHEAP) &&
-               (type == CURSEG_HOT_DATA || IS_NODESEG(type)))
+               (seg_type == CURSEG_HOT_DATA || IS_NODESEG(seg_type)))
                return 0;
 
        if (SIT_I(sbi)->last_victim[ALLOC_NEXT])
@@ -2530,7 +2591,7 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type)
        if (F2FS_OPTION(sbi).alloc_mode == ALLOC_MODE_REUSE)
                return 0;
 
-       return CURSEG_I(sbi, type)->segno;
+       return curseg->segno;
 }
 
 /*
@@ -2540,12 +2601,14 @@ static unsigned int __get_next_segno(struct f2fs_sb_info *sbi, int type)
 static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, type);
+       unsigned short seg_type = curseg->seg_type;
        unsigned int segno = curseg->segno;
        int dir = ALLOC_LEFT;
 
-       write_sum_page(sbi, curseg->sum_blk,
+       if (curseg->inited)
+               write_sum_page(sbi, curseg->sum_blk,
                                GET_SUM_BLOCK(sbi, segno));
-       if (type == CURSEG_WARM_DATA || type == CURSEG_COLD_DATA)
+       if (seg_type == CURSEG_WARM_DATA || seg_type == CURSEG_COLD_DATA)
                dir = ALLOC_RIGHT;
 
        if (test_opt(sbi, NOHEAP))
@@ -2594,7 +2657,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)
+static void change_curseg(struct f2fs_sb_info *sbi, int type, bool flush)
 {
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
        struct curseg_info *curseg = CURSEG_I(sbi, type);
@@ -2602,8 +2665,10 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type)
        struct f2fs_summary_block *sum_node;
        struct page *sum_page;
 
-       write_sum_page(sbi, curseg->sum_blk,
-                               GET_SUM_BLOCK(sbi, curseg->segno));
+       if (flush)
+               write_sum_page(sbi, curseg->sum_blk,
+                                       GET_SUM_BLOCK(sbi, curseg->segno));
+
        __set_test_and_inuse(sbi, new_segno);
 
        mutex_lock(&dirty_i->seglist_lock);
@@ -2616,29 +2681,139 @@ static void change_curseg(struct f2fs_sb_info *sbi, int type)
        __next_free_blkoff(sbi, curseg, 0);
 
        sum_page = f2fs_get_sum_page(sbi, new_segno);
-       f2fs_bug_on(sbi, IS_ERR(sum_page));
+       if (IS_ERR(sum_page)) {
+               /* GC won't be able to use stale summary pages by cp_error */
+               memset(curseg->sum_blk, 0, SUM_ENTRY_SIZE);
+               return;
+       }
        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)
+static int get_ssr_segment(struct f2fs_sb_info *sbi, int type,
+                               int alloc_mode, unsigned long long age);
+
+static void get_atssr_segment(struct f2fs_sb_info *sbi, int type,
+                                       int target_type, int alloc_mode,
+                                       unsigned long long age)
+{
+       struct curseg_info *curseg = CURSEG_I(sbi, type);
+
+       curseg->seg_type = target_type;
+
+       if (get_ssr_segment(sbi, type, alloc_mode, age)) {
+               struct seg_entry *se = get_seg_entry(sbi, curseg->next_segno);
+
+               curseg->seg_type = se->type;
+               change_curseg(sbi, type, true);
+       } else {
+               /* allocate cold segment by default */
+               curseg->seg_type = CURSEG_COLD_DATA;
+               new_curseg(sbi, type, true);
+       }
+       stat_inc_seg_type(sbi, curseg);
+}
+
+static void __f2fs_init_atgc_curseg(struct f2fs_sb_info *sbi)
+{
+       struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_ALL_DATA_ATGC);
+
+       if (!sbi->am.atgc_enabled)
+               return;
+
+       down_read(&SM_I(sbi)->curseg_lock);
+
+       mutex_lock(&curseg->curseg_mutex);
+       down_write(&SIT_I(sbi)->sentry_lock);
+
+       get_atssr_segment(sbi, CURSEG_ALL_DATA_ATGC, CURSEG_COLD_DATA, SSR, 0);
+
+       up_write(&SIT_I(sbi)->sentry_lock);
+       mutex_unlock(&curseg->curseg_mutex);
+
+       up_read(&SM_I(sbi)->curseg_lock);
+
+}
+void f2fs_init_inmem_curseg(struct f2fs_sb_info *sbi)
+{
+       __f2fs_init_atgc_curseg(sbi);
+}
+
+static void __f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi, int type)
+{
+       struct curseg_info *curseg = CURSEG_I(sbi, type);
+
+       mutex_lock(&curseg->curseg_mutex);
+       if (!curseg->inited)
+               goto out;
+
+       if (get_valid_blocks(sbi, curseg->segno, false)) {
+               write_sum_page(sbi, curseg->sum_blk,
+                               GET_SUM_BLOCK(sbi, curseg->segno));
+       } else {
+               mutex_lock(&DIRTY_I(sbi)->seglist_lock);
+               __set_test_and_free(sbi, curseg->segno, true);
+               mutex_unlock(&DIRTY_I(sbi)->seglist_lock);
+       }
+out:
+       mutex_unlock(&curseg->curseg_mutex);
+}
+
+void f2fs_save_inmem_curseg(struct f2fs_sb_info *sbi)
+{
+       __f2fs_save_inmem_curseg(sbi, CURSEG_COLD_DATA_PINNED);
+
+       if (sbi->am.atgc_enabled)
+               __f2fs_save_inmem_curseg(sbi, CURSEG_ALL_DATA_ATGC);
+}
+
+static void __f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi, int type)
+{
+       struct curseg_info *curseg = CURSEG_I(sbi, type);
+
+       mutex_lock(&curseg->curseg_mutex);
+       if (!curseg->inited)
+               goto out;
+       if (get_valid_blocks(sbi, curseg->segno, false))
+               goto out;
+
+       mutex_lock(&DIRTY_I(sbi)->seglist_lock);
+       __set_test_and_inuse(sbi, curseg->segno);
+       mutex_unlock(&DIRTY_I(sbi)->seglist_lock);
+out:
+       mutex_unlock(&curseg->curseg_mutex);
+}
+
+void f2fs_restore_inmem_curseg(struct f2fs_sb_info *sbi)
+{
+       __f2fs_restore_inmem_curseg(sbi, CURSEG_COLD_DATA_PINNED);
+
+       if (sbi->am.atgc_enabled)
+               __f2fs_restore_inmem_curseg(sbi, CURSEG_ALL_DATA_ATGC);
+}
+
+static int get_ssr_segment(struct f2fs_sb_info *sbi, int type,
+                               int alloc_mode, unsigned long long age)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, type);
        const struct victim_selection *v_ops = DIRTY_I(sbi)->v_ops;
        unsigned segno = NULL_SEGNO;
+       unsigned short seg_type = curseg->seg_type;
        int i, cnt;
        bool reversed = false;
 
+       sanity_check_seg_type(sbi, seg_type);
+
        /* f2fs_need_SSR() already forces to do this */
-       if (!v_ops->get_victim(sbi, &segno, BG_GC, type, SSR)) {
+       if (!v_ops->get_victim(sbi, &segno, BG_GC, seg_type, alloc_mode, age)) {
                curseg->next_segno = segno;
                return 1;
        }
 
        /* For node segments, let's do SSR more intensively */
-       if (IS_NODESEG(type)) {
-               if (type >= CURSEG_WARM_NODE) {
+       if (IS_NODESEG(seg_type)) {
+               if (seg_type >= CURSEG_WARM_NODE) {
                        reversed = true;
                        i = CURSEG_COLD_NODE;
                } else {
@@ -2646,7 +2821,7 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type)
                }
                cnt = NR_CURSEG_NODE_TYPE;
        } else {
-               if (type >= CURSEG_WARM_DATA) {
+               if (seg_type >= CURSEG_WARM_DATA) {
                        reversed = true;
                        i = CURSEG_COLD_DATA;
                } else {
@@ -2656,9 +2831,9 @@ static int get_ssr_segment(struct f2fs_sb_info *sbi, int type)
        }
 
        for (; cnt-- > 0; reversed ? i-- : i++) {
-               if (i == type)
+               if (i == seg_type)
                        continue;
-               if (!v_ops->get_victim(sbi, &segno, BG_GC, i, SSR)) {
+               if (!v_ops->get_victim(sbi, &segno, BG_GC, i, alloc_mode, age)) {
                        curseg->next_segno = segno;
                        return 1;
                }
@@ -2687,13 +2862,15 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
        if (force)
                new_curseg(sbi, type, true);
        else if (!is_set_ckpt_flags(sbi, CP_CRC_RECOVERY_FLAG) &&
-                                       type == CURSEG_WARM_NODE)
+                                       curseg->seg_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, curseg, type) &&
                        likely(!is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
                new_curseg(sbi, type, false);
-       else if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type))
-               change_curseg(sbi, type);
+       else if (f2fs_need_SSR(sbi) &&
+                       get_ssr_segment(sbi, type, SSR, 0))
+               change_curseg(sbi, type, true);
        else
                new_curseg(sbi, type, false);
 
@@ -2714,8 +2891,8 @@ void f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type,
        if (segno < start || segno > end)
                goto unlock;
 
-       if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type))
-               change_curseg(sbi, type);
+       if (f2fs_need_SSR(sbi) && get_ssr_segment(sbi, type, SSR, 0))
+               change_curseg(sbi, type, true);
        else
                new_curseg(sbi, type, true);
 
@@ -2738,11 +2915,15 @@ static void __allocate_new_segment(struct f2fs_sb_info *sbi, int type)
        struct curseg_info *curseg = CURSEG_I(sbi, type);
        unsigned int old_segno;
 
+       if (!curseg->inited)
+               goto alloc;
+
        if (!curseg->next_blkoff &&
                !get_valid_blocks(sbi, curseg->segno, false) &&
                !get_ckpt_valid_blocks(sbi, curseg->segno))
                return;
 
+alloc:
        old_segno = curseg->segno;
        SIT_I(sbi)->s_ops->allocate_segment(sbi, type, true);
        locate_dirty_segment(sbi, old_segno);
@@ -2806,7 +2987,7 @@ next:
        mutex_lock(&dcc->cmd_lock);
        if (unlikely(dcc->rbtree_check))
                f2fs_bug_on(sbi, !f2fs_check_rb_tree_consistence(sbi,
-                                                               &dcc->root));
+                                                       &dcc->root, false));
 
        dc = (struct discard_cmd *)f2fs_lookup_rb_tree_ret(&dcc->root,
                                        NULL, start,
@@ -2930,12 +3111,11 @@ out:
        return err;
 }
 
-static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type)
+static bool __has_curseg_space(struct f2fs_sb_info *sbi,
+                                       struct curseg_info *curseg)
 {
-       struct curseg_info *curseg = CURSEG_I(sbi, type);
-       if (curseg->next_blkoff < sbi->blocks_per_seg)
-               return true;
-       return false;
+       return curseg->next_blkoff < f2fs_usable_blks_in_seg(sbi,
+                                                       curseg->segno);
 }
 
 int f2fs_rw_hint_to_seg_type(enum rw_hint hint)
@@ -3075,8 +3255,13 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
        if (fio->type == DATA) {
                struct inode *inode = fio->page->mapping->host;
 
-               if (is_cold_data(fio->page) || file_is_cold(inode) ||
-                               f2fs_compressed_file(inode))
+               if (is_cold_data(fio->page)) {
+                       if (fio->sbi->am.atgc_enabled)
+                               return CURSEG_ALL_DATA_ATGC;
+                       else
+                               return CURSEG_COLD_DATA;
+               }
+               if (file_is_cold(inode) || f2fs_need_compress_data(inode))
                        return CURSEG_COLD_DATA;
                if (file_is_hot(inode) ||
                                is_inode_flag_set(inode, FI_HOT_DATA) ||
@@ -3126,27 +3311,25 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
 {
        struct sit_info *sit_i = SIT_I(sbi);
        struct curseg_info *curseg = CURSEG_I(sbi, type);
-       bool put_pin_sem = false;
-
-       if (type == CURSEG_COLD_DATA) {
-               /* GC during CURSEG_COLD_DATA_PINNED allocation */
-               if (down_read_trylock(&sbi->pin_sem)) {
-                       put_pin_sem = true;
-               } else {
-                       type = CURSEG_WARM_DATA;
-                       curseg = CURSEG_I(sbi, type);
-               }
-       } else if (type == CURSEG_COLD_DATA_PINNED) {
-               type = CURSEG_COLD_DATA;
-       }
+       unsigned long long old_mtime;
+       bool from_gc = (type == CURSEG_ALL_DATA_ATGC);
+       struct seg_entry *se = NULL;
 
        down_read(&SM_I(sbi)->curseg_lock);
 
        mutex_lock(&curseg->curseg_mutex);
        down_write(&sit_i->sentry_lock);
 
+       if (from_gc) {
+               f2fs_bug_on(sbi, GET_SEGNO(sbi, old_blkaddr) == NULL_SEGNO);
+               se = get_seg_entry(sbi, GET_SEGNO(sbi, old_blkaddr));
+               sanity_check_seg_type(sbi, se->type);
+               f2fs_bug_on(sbi, IS_NODESEG(se->type));
+       }
        *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
 
+       f2fs_bug_on(sbi, curseg->next_blkoff >= sbi->blocks_per_seg);
+
        f2fs_wait_discard_bio(sbi, *new_blkaddr);
 
        /*
@@ -3160,6 +3343,14 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
 
        stat_inc_block_count(sbi, curseg);
 
+       if (from_gc) {
+               old_mtime = get_segment_mtime(sbi, old_blkaddr);
+       } else {
+               update_segment_mtime(sbi, old_blkaddr, 0);
+               old_mtime = 0;
+       }
+       update_segment_mtime(sbi, *new_blkaddr, old_mtime);
+
        /*
         * SIT information should be updated before segment allocation,
         * since SSR needs latest valid block information.
@@ -3168,9 +3359,13 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
        if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO)
                update_sit_entry(sbi, old_blkaddr, -1);
 
-       if (!__has_curseg_space(sbi, type))
-               sit_i->s_ops->allocate_segment(sbi, type, false);
-
+       if (!__has_curseg_space(sbi, curseg)) {
+               if (from_gc)
+                       get_atssr_segment(sbi, type, se->type,
+                                               AT_SSR, se->mtime);
+               else
+                       sit_i->s_ops->allocate_segment(sbi, type, false);
+       }
        /*
         * segment dirty status should be updated after segment allocation,
         * so we just need to update status only one time after previous
@@ -3204,9 +3399,6 @@ void f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,
        mutex_unlock(&curseg->curseg_mutex);
 
        up_read(&SM_I(sbi)->curseg_lock);
-
-       if (put_pin_sem)
-               up_read(&sbi->pin_sem);
 }
 
 static void update_device_state(struct f2fs_io_info *fio)
@@ -3355,7 +3547,8 @@ static inline int __f2fs_get_curseg(struct f2fs_sb_info *sbi,
 
 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)
+                               bool recover_curseg, bool recover_newaddr,
+                               bool from_gc)
 {
        struct sit_info *sit_i = SIT_I(sbi);
        struct curseg_info *curseg;
@@ -3400,17 +3593,22 @@ void f2fs_do_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);
+               change_curseg(sbi, type, true);
        }
 
        curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr);
        __add_sum_entry(sbi, type, sum);
 
-       if (!recover_curseg || recover_newaddr)
+       if (!recover_curseg || recover_newaddr) {
+               if (!from_gc)
+                       update_segment_mtime(sbi, new_blkaddr, 0);
                update_sit_entry(sbi, new_blkaddr, 1);
+       }
        if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) {
                invalidate_mapping_pages(META_MAPPING(sbi),
                                        old_blkaddr, old_blkaddr);
+               if (!from_gc)
+                       update_segment_mtime(sbi, old_blkaddr, 0);
                update_sit_entry(sbi, old_blkaddr, -1);
        }
 
@@ -3422,7 +3620,7 @@ void f2fs_do_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);
+                       change_curseg(sbi, type, true);
                }
                curseg->next_blkoff = old_blkoff;
        }
@@ -3442,7 +3640,7 @@ 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_do_replace_block(sbi, &sum, old_addr, new_addr,
-                                       recover_curseg, recover_newaddr);
+                                       recover_curseg, recover_newaddr, false);
 
        f2fs_update_data_blkaddr(dn, new_addr);
 }
@@ -3574,7 +3772,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
                blk_off = le16_to_cpu(ckpt->cur_data_blkoff[type -
                                                        CURSEG_HOT_DATA]);
                if (__exist_node_summaries(sbi))
-                       blk_addr = sum_blk_addr(sbi, NR_CURSEG_TYPE, type);
+                       blk_addr = sum_blk_addr(sbi, NR_CURSEG_PERSIST_TYPE, type);
                else
                        blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type);
        } else {
@@ -3652,8 +3850,9 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
        }
 
        if (__exist_node_summaries(sbi))
-               f2fs_ra_meta_pages(sbi, sum_blk_addr(sbi, NR_CURSEG_TYPE, type),
-                                       NR_CURSEG_TYPE - type, META_CP, true);
+               f2fs_ra_meta_pages(sbi,
+                               sum_blk_addr(sbi, NR_CURSEG_PERSIST_TYPE, type),
+                               NR_CURSEG_PERSIST_TYPE - type, META_CP, true);
 
        for (; type <= CURSEG_COLD_NODE; type++) {
                err = read_normal_summaries(sbi, type);
@@ -3781,7 +3980,7 @@ int f2fs_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 f2fs_get_meta_page_nofail(sbi, current_sit_addr(sbi, segno));
+       return f2fs_get_meta_page(sbi, current_sit_addr(sbi, segno));
 }
 
 static struct page *get_next_sit_page(struct f2fs_sb_info *sbi,
@@ -4155,14 +4354,14 @@ static int build_curseg(struct f2fs_sb_info *sbi)
        struct curseg_info *array;
        int i;
 
-       array = f2fs_kzalloc(sbi, array_size(NR_CURSEG_TYPE, sizeof(*array)),
-                            GFP_KERNEL);
+       array = f2fs_kzalloc(sbi, array_size(NR_CURSEG_TYPE,
+                                       sizeof(*array)), GFP_KERNEL);
        if (!array)
                return -ENOMEM;
 
        SM_I(sbi)->curseg_array = array;
 
-       for (i = 0; i < NR_CURSEG_TYPE; i++) {
+       for (i = 0; i < NO_CHECK_TYPE; i++) {
                mutex_init(&array[i].curseg_mutex);
                array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL);
                if (!array[i].sum_blk)
@@ -4172,8 +4371,15 @@ static int build_curseg(struct f2fs_sb_info *sbi)
                                sizeof(struct f2fs_journal), GFP_KERNEL);
                if (!array[i].journal)
                        return -ENOMEM;
+               if (i < NR_PERSISTENT_LOG)
+                       array[i].seg_type = CURSEG_HOT_DATA + i;
+               else if (i == CURSEG_COLD_DATA_PINNED)
+                       array[i].seg_type = CURSEG_COLD_DATA;
+               else if (i == CURSEG_ALL_DATA_ATGC)
+                       array[i].seg_type = CURSEG_COLD_DATA;
                array[i].segno = NULL_SEGNO;
                array[i].next_blkoff = 0;
+               array[i].inited = false;
        }
        return restore_curseg_summaries(sbi);
 }
@@ -4294,9 +4500,12 @@ static void init_free_segmap(struct f2fs_sb_info *sbi)
 {
        unsigned int start;
        int type;
+       struct seg_entry *sentry;
 
        for (start = 0; start < MAIN_SEGS(sbi); start++) {
-               struct seg_entry *sentry = get_seg_entry(sbi, start);
+               if (f2fs_usable_blks_in_seg(sbi, start) == 0)
+                       continue;
+               sentry = get_seg_entry(sbi, start);
                if (!sentry->valid_blocks)
                        __set_free(sbi, start);
                else
@@ -4316,7 +4525,7 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
        struct free_segmap_info *free_i = FREE_I(sbi);
        unsigned int segno = 0, offset = 0, secno;
-       block_t valid_blocks;
+       block_t valid_blocks, usable_blks_in_seg;
        block_t blks_per_sec = BLKS_PER_SEC(sbi);
 
        while (1) {
@@ -4326,9 +4535,10 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
                        break;
                offset = segno + 1;
                valid_blocks = get_valid_blocks(sbi, segno, false);
-               if (valid_blocks == sbi->blocks_per_seg || !valid_blocks)
+               usable_blks_in_seg = f2fs_usable_blks_in_seg(sbi, segno);
+               if (valid_blocks == usable_blks_in_seg || !valid_blocks)
                        continue;
-               if (valid_blocks > sbi->blocks_per_seg) {
+               if (valid_blocks > usable_blks_in_seg) {
                        f2fs_bug_on(sbi, 1);
                        continue;
                }
@@ -4341,7 +4551,7 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
                return;
 
        mutex_lock(&dirty_i->seglist_lock);
-       for (segno = 0; segno < MAIN_SECS(sbi); segno += blks_per_sec) {
+       for (segno = 0; segno < MAIN_SEGS(sbi); segno += sbi->segs_per_sec) {
                valid_blocks = get_valid_blocks(sbi, segno, true);
                secno = GET_SEC_FROM_SEG(sbi, segno);
 
@@ -4408,11 +4618,13 @@ static int sanity_check_curseg(struct f2fs_sb_info *sbi)
         * 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++) {
+       for (i = 0; i < NR_PERSISTENT_LOG; 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;
 
+               sanity_check_seg_type(sbi, curseg->seg_type);
+
                if (f2fs_test_bit(blkofs, se->cur_valid_map))
                        goto out;
 
@@ -4637,7 +4849,7 @@ int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi)
 {
        int i, ret;
 
-       for (i = 0; i < NO_CHECK_TYPE; i++) {
+       for (i = 0; i < NR_PERSISTENT_LOG; i++) {
                ret = fix_curseg_write_pointer(sbi, i);
                if (ret)
                        return ret;
@@ -4678,6 +4890,101 @@ int f2fs_check_write_pointer(struct f2fs_sb_info *sbi)
 
        return 0;
 }
+
+static bool is_conv_zone(struct f2fs_sb_info *sbi, unsigned int zone_idx,
+                                               unsigned int dev_idx)
+{
+       if (!bdev_is_zoned(FDEV(dev_idx).bdev))
+               return true;
+       return !test_bit(zone_idx, FDEV(dev_idx).blkz_seq);
+}
+
+/* Return the zone index in the given device */
+static unsigned int get_zone_idx(struct f2fs_sb_info *sbi, unsigned int secno,
+                                       int dev_idx)
+{
+       block_t sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno));
+
+       return (sec_start_blkaddr - FDEV(dev_idx).start_blk) >>
+                                               sbi->log_blocks_per_blkz;
+}
+
+/*
+ * Return the usable segments in a section based on the zone's
+ * corresponding zone capacity. Zone is equal to a section.
+ */
+static inline unsigned int f2fs_usable_zone_segs_in_sec(
+               struct f2fs_sb_info *sbi, unsigned int segno)
+{
+       unsigned int dev_idx, zone_idx, unusable_segs_in_sec;
+
+       dev_idx = f2fs_target_device_index(sbi, START_BLOCK(sbi, segno));
+       zone_idx = get_zone_idx(sbi, GET_SEC_FROM_SEG(sbi, segno), dev_idx);
+
+       /* Conventional zone's capacity is always equal to zone size */
+       if (is_conv_zone(sbi, zone_idx, dev_idx))
+               return sbi->segs_per_sec;
+
+       /*
+        * If the zone_capacity_blocks array is NULL, then zone capacity
+        * is equal to the zone size for all zones
+        */
+       if (!FDEV(dev_idx).zone_capacity_blocks)
+               return sbi->segs_per_sec;
+
+       /* Get the segment count beyond zone capacity block */
+       unusable_segs_in_sec = (sbi->blocks_per_blkz -
+                               FDEV(dev_idx).zone_capacity_blocks[zone_idx]) >>
+                               sbi->log_blocks_per_seg;
+       return sbi->segs_per_sec - unusable_segs_in_sec;
+}
+
+/*
+ * Return the number of usable blocks in a segment. The number of blocks
+ * returned is always equal to the number of blocks in a segment for
+ * segments fully contained within a sequential zone capacity or a
+ * conventional zone. For segments partially contained in a sequential
+ * zone capacity, the number of usable blocks up to the zone capacity
+ * is returned. 0 is returned in all other cases.
+ */
+static inline unsigned int f2fs_usable_zone_blks_in_seg(
+                       struct f2fs_sb_info *sbi, unsigned int segno)
+{
+       block_t seg_start, sec_start_blkaddr, sec_cap_blkaddr;
+       unsigned int zone_idx, dev_idx, secno;
+
+       secno = GET_SEC_FROM_SEG(sbi, segno);
+       seg_start = START_BLOCK(sbi, segno);
+       dev_idx = f2fs_target_device_index(sbi, seg_start);
+       zone_idx = get_zone_idx(sbi, secno, dev_idx);
+
+       /*
+        * Conventional zone's capacity is always equal to zone size,
+        * so, blocks per segment is unchanged.
+        */
+       if (is_conv_zone(sbi, zone_idx, dev_idx))
+               return sbi->blocks_per_seg;
+
+       if (!FDEV(dev_idx).zone_capacity_blocks)
+               return sbi->blocks_per_seg;
+
+       sec_start_blkaddr = START_BLOCK(sbi, GET_SEG_FROM_SEC(sbi, secno));
+       sec_cap_blkaddr = sec_start_blkaddr +
+                               FDEV(dev_idx).zone_capacity_blocks[zone_idx];
+
+       /*
+        * If segment starts before zone capacity and spans beyond
+        * zone capacity, then usable blocks are from seg start to
+        * zone capacity. If the segment starts after the zone capacity,
+        * then there are no usable blocks.
+        */
+       if (seg_start >= sec_cap_blkaddr)
+               return 0;
+       if (seg_start + sbi->blocks_per_seg > sec_cap_blkaddr)
+               return sec_cap_blkaddr - seg_start;
+
+       return sbi->blocks_per_seg;
+}
 #else
 int f2fs_fix_curseg_write_pointer(struct f2fs_sb_info *sbi)
 {
@@ -4688,7 +4995,36 @@ int f2fs_check_write_pointer(struct f2fs_sb_info *sbi)
 {
        return 0;
 }
+
+static inline unsigned int f2fs_usable_zone_blks_in_seg(struct f2fs_sb_info *sbi,
+                                                       unsigned int segno)
+{
+       return 0;
+}
+
+static inline unsigned int f2fs_usable_zone_segs_in_sec(struct f2fs_sb_info *sbi,
+                                                       unsigned int segno)
+{
+       return 0;
+}
 #endif
+unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi,
+                                       unsigned int segno)
+{
+       if (f2fs_sb_has_blkzoned(sbi))
+               return f2fs_usable_zone_blks_in_seg(sbi, segno);
+
+       return sbi->blocks_per_seg;
+}
+
+unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi,
+                                       unsigned int segno)
+{
+       if (f2fs_sb_has_blkzoned(sbi))
+               return f2fs_usable_zone_segs_in_sec(sbi, segno);
+
+       return sbi->segs_per_sec;
+}
 
 /*
  * Update min, max modified time for cost-benefit GC algorithm
@@ -4715,6 +5051,7 @@ static void init_min_max_mtime(struct f2fs_sb_info *sbi)
                        sit_i->min_mtime = mtime;
        }
        sit_i->max_mtime = get_mtime(sbi, false);
+       sit_i->dirty_max_mtime = 0;
        up_write(&sit_i->sentry_lock);
 }
 
@@ -4830,7 +5167,7 @@ static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
 
        destroy_victim_secmap(sbi);
        SM_I(sbi)->dirty_info = NULL;
-       kvfree(dirty_i);
+       kfree(dirty_i);
 }
 
 static void destroy_curseg(struct f2fs_sb_info *sbi)
@@ -4842,10 +5179,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++) {
-               kvfree(array[i].sum_blk);
-               kvfree(array[i].journal);
+               kfree(array[i].sum_blk);
+               kfree(array[i].journal);
        }
-       kvfree(array);
+       kfree(array);
 }
 
 static void destroy_free_segmap(struct f2fs_sb_info *sbi)
@@ -4856,7 +5193,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);
-       kvfree(free_i);
+       kfree(free_i);
 }
 
 static void destroy_sit_info(struct f2fs_sb_info *sbi)
@@ -4868,7 +5205,7 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi)
 
        if (sit_i->sentries)
                kvfree(sit_i->bitmap);
-       kvfree(sit_i->tmp_map);
+       kfree(sit_i->tmp_map);
 
        kvfree(sit_i->sentries);
        kvfree(sit_i->sec_entries);
@@ -4880,7 +5217,7 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi)
        kvfree(sit_i->sit_bitmap_mir);
        kvfree(sit_i->invalid_segmap);
 #endif
-       kvfree(sit_i);
+       kfree(sit_i);
 }
 
 void f2fs_destroy_segment_manager(struct f2fs_sb_info *sbi)
@@ -4896,7 +5233,7 @@ void f2fs_destroy_segment_manager(struct f2fs_sb_info *sbi)
        destroy_free_segmap(sbi);
        destroy_sit_info(sbi);
        sbi->sm_info = NULL;
-       kvfree(sm_info);
+       kfree(sm_info);
 }
 
 int __init f2fs_create_segment_manager_caches(void)