Contact:       "Jaegeuk Kim" <jaegeuk@kernel.org>
 Description:   Do background GC agressively when set. When gc_urgent = 1,
                background thread starts to do GC by given gc_urgent_sleep_time
-               interval. It is set to 0 by default.
+               interval. When gc_urgent = 2, F2FS will lower the bar of
+               checking idle in order to process outstanding discard commands
+               and GC a little bit aggressively. It is set to 0 by default.
 
 What:          /sys/fs/f2fs/<disk>/gc_urgent_sleep_time
 Date:          August 2017
 
        GC_NORMAL,
        GC_IDLE_CB,
        GC_IDLE_GREEDY,
-       GC_URGENT,
+       GC_URGENT_HIGH,
+       GC_URGENT_LOW,
 };
 
 enum {
        unsigned int cur_victim_sec;            /* current victim section num */
        unsigned int gc_mode;                   /* current GC state */
        unsigned int next_victim_seg[2];        /* next segment in victim section */
+
        /* for skip statistic */
        unsigned int atomic_files;              /* # of opened atomic file */
        unsigned long long skipped_atomic_files[2];     /* FG_GC and BG_GC */
 
 static inline bool is_idle(struct f2fs_sb_info *sbi, int type)
 {
-       if (sbi->gc_mode == GC_URGENT)
+       if (sbi->gc_mode == GC_URGENT_HIGH)
                return true;
 
        if (get_pages(sbi, F2FS_RD_DATA) || get_pages(sbi, F2FS_RD_NODE) ||
                        atomic_read(&SM_I(sbi)->fcc_info->queued_flush))
                return false;
 
+       if (sbi->gc_mode == GC_URGENT_LOW &&
+                       (type == DISCARD_TIME || type == GC_TIME))
+               return true;
+
        return f2fs_time_over(sbi, type);
 }
 
 
                 * invalidated soon after by user update or deletion.
                 * So, I'd like to wait some time to collect dirty segments.
                 */
-               if (sbi->gc_mode == GC_URGENT) {
+               if (sbi->gc_mode == GC_URGENT_HIGH) {
                        wait_ms = gc_th->urgent_sleep_time;
                        down_write(&sbi->gc_lock);
                        goto do_gc;
                gc_mode = GC_CB;
                break;
        case GC_IDLE_GREEDY:
-       case GC_URGENT:
+       case GC_URGENT_HIGH:
                gc_mode = GC_GREEDY;
                break;
        }
         * foreground GC and urgent GC cases.
         */
        if (gc_type != FG_GC &&
-                       (sbi->gc_mode != GC_URGENT) &&
+                       (sbi->gc_mode != GC_URGENT_HIGH) &&
                        p->max_search > sbi->max_victim_search)
                p->max_search = sbi->max_victim_search;
 
 
 
        if (f2fs_lfs_mode(sbi))
                return false;
-       if (sbi->gc_mode == GC_URGENT)
+       if (sbi->gc_mode == GC_URGENT_HIGH)
                return true;
        if (unlikely(is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
                return true;
                        continue;
                }
 
-               if (sbi->gc_mode == GC_URGENT)
+               if (sbi->gc_mode == GC_URGENT_HIGH)
                        __init_discard_policy(sbi, &dpolicy, DPOLICY_FORCE, 1);
 
                sb_start_intwrite(sbi->sb);
 
                return -EINVAL;
 
        if (!strcmp(a->attr.name, "gc_urgent")) {
-               if (t >= 1) {
-                       sbi->gc_mode = GC_URGENT;
+               if (t == 0) {
+                       sbi->gc_mode = GC_NORMAL;
+               } else if (t == 1) {
+                       sbi->gc_mode = GC_URGENT_HIGH;
                        if (sbi->gc_thread) {
                                sbi->gc_thread->gc_wake = 1;
                                wake_up_interruptible_all(
                                        &sbi->gc_thread->gc_wait_queue_head);
                                wake_up_discard_thread(sbi, true);
                        }
+               } else if (t == 2) {
+                       sbi->gc_mode = GC_URGENT_LOW;
                } else {
-                       sbi->gc_mode = GC_NORMAL;
+                       return -EINVAL;
                }
                return count;
        }