Merge branch 'release' of git://git.kernel.org/pub/scm/linux/kernel/git/lenb/linux
[firefly-linux-kernel-4.4.55.git] / fs / f2fs / segment.c
index 179a13e86f69ab51721b142fa28d0665a7564801..d8e84e49a5c301cb139acfa6d0e06cbd1544b82a 100644 (file)
@@ -18,6 +18,7 @@
 #include "f2fs.h"
 #include "segment.h"
 #include "node.h"
+#include <trace/events/f2fs.h>
 
 /*
  * This function balances dirty node and dentry pages.
@@ -49,9 +50,20 @@ static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
 
        if (dirty_type == DIRTY) {
                struct seg_entry *sentry = get_seg_entry(sbi, segno);
+               enum dirty_type t = DIRTY_HOT_DATA;
+
                dirty_type = sentry->type;
+
                if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type]))
                        dirty_i->nr_dirty[dirty_type]++;
+
+               /* Only one bitmap should be set */
+               for (; t <= DIRTY_COLD_NODE; t++) {
+                       if (t == dirty_type)
+                               continue;
+                       if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t]))
+                               dirty_i->nr_dirty[t]--;
+               }
        }
 }
 
@@ -64,13 +76,16 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
                dirty_i->nr_dirty[dirty_type]--;
 
        if (dirty_type == DIRTY) {
-               struct seg_entry *sentry = get_seg_entry(sbi, segno);
-               dirty_type = sentry->type;
-               if (test_and_clear_bit(segno,
-                                       dirty_i->dirty_segmap[dirty_type]))
-                       dirty_i->nr_dirty[dirty_type]--;
-               clear_bit(segno, dirty_i->victim_segmap[FG_GC]);
-               clear_bit(segno, dirty_i->victim_segmap[BG_GC]);
+               enum dirty_type t = DIRTY_HOT_DATA;
+
+               /* clear all the bitmaps */
+               for (; t <= DIRTY_COLD_NODE; t++)
+                       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),
+                                               dirty_i->victim_secmap);
        }
 }
 
@@ -296,13 +311,12 @@ static void write_sum_page(struct f2fs_sb_info *sbi,
        f2fs_put_page(page, 1);
 }
 
-static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi,
-                                       int ofs_unit, int type)
+static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi, int type)
 {
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
        unsigned long *prefree_segmap = dirty_i->dirty_segmap[PRE];
-       unsigned int segno, next_segno, i;
-       int ofs = 0;
+       unsigned int segno;
+       unsigned int ofs = 0;
 
        /*
         * If there is not enough reserved sections,
@@ -318,28 +332,46 @@ static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi,
        if (IS_NODESEG(type))
                return NULL_SEGNO;
 next:
-       segno = find_next_bit(prefree_segmap, TOTAL_SEGS(sbi), ofs++);
-       ofs = ((segno / ofs_unit) * ofs_unit) + ofs_unit;
+       segno = find_next_bit(prefree_segmap, TOTAL_SEGS(sbi), ofs);
+       ofs += sbi->segs_per_sec;
+
        if (segno < TOTAL_SEGS(sbi)) {
+               int i;
+
                /* skip intermediate segments in a section */
-               if (segno % ofs_unit)
+               if (segno % sbi->segs_per_sec)
                        goto next;
 
-               /* skip if whole section is not prefree */
-               next_segno = find_next_zero_bit(prefree_segmap,
-                                               TOTAL_SEGS(sbi), segno + 1);
-               if (next_segno - segno < ofs_unit)
+               /* skip if the section is currently used */
+               if (sec_usage_check(sbi, GET_SECNO(sbi, segno)))
                        goto next;
 
+               /* skip if whole section is not prefree */
+               for (i = 1; i < sbi->segs_per_sec; i++)
+                       if (!test_bit(segno + i, prefree_segmap))
+                               goto next;
+
                /* skip if whole section was not free at the last checkpoint */
-               for (i = 0; i < ofs_unit; i++)
-                       if (get_seg_entry(sbi, segno)->ckpt_valid_blocks)
+               for (i = 0; i < sbi->segs_per_sec; i++)
+                       if (get_seg_entry(sbi, segno + i)->ckpt_valid_blocks)
                                goto next;
+
                return segno;
        }
        return NULL_SEGNO;
 }
 
+static int is_next_segment_free(struct f2fs_sb_info *sbi, int type)
+{
+       struct curseg_info *curseg = CURSEG_I(sbi, type);
+       unsigned int segno = curseg->segno;
+       struct free_segmap_info *free_i = FREE_I(sbi);
+
+       if (segno + 1 < TOTAL_SEGS(sbi) && (segno + 1) % sbi->segs_per_sec)
+               return !test_bit(segno + 1, free_i->free_segmap);
+       return 0;
+}
+
 /*
  * Find a new segment from the free segments bitmap to right order
  * This function should be returned with success, otherwise BUG
@@ -362,7 +394,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,
                                        TOTAL_SEGS(sbi), *newseg + 1);
-               if (segno < TOTAL_SEGS(sbi))
+               if (segno - *newseg < sbi->segs_per_sec -
+                                       (*newseg % sbi->segs_per_sec))
                        goto got_it;
        }
 find_other_zone:
@@ -560,20 +593,20 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,
                                                int type, bool force)
 {
        struct curseg_info *curseg = CURSEG_I(sbi, type);
-       unsigned int ofs_unit;
 
        if (force) {
                new_curseg(sbi, type, true);
                goto out;
        }
 
-       ofs_unit = need_SSR(sbi) ? 1 : sbi->segs_per_sec;
-       curseg->next_segno = check_prefree_segments(sbi, ofs_unit, type);
+       curseg->next_segno = check_prefree_segments(sbi, type);
 
        if (curseg->next_segno != NULL_SEGNO)
                change_curseg(sbi, type, false);
        else if (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);
        else
@@ -655,10 +688,16 @@ static void do_submit_bio(struct f2fs_sb_info *sbi,
        if (type >= META_FLUSH)
                rw = WRITE_FLUSH_FUA;
 
+       if (btype == META)
+               rw |= REQ_META;
+
        if (sbi->bio[btype]) {
                struct bio_private *p = sbi->bio[btype]->bi_private;
                p->sbi = sbi;
                sbi->bio[btype]->bi_end_io = f2fs_end_io_write;
+
+               trace_f2fs_do_submit_bio(sbi->sb, btype, sync, sbi->bio[btype]);
+
                if (type == META_FLUSH) {
                        DECLARE_COMPLETION_ONSTACK(wait);
                        p->is_sync = true;
@@ -695,7 +734,7 @@ static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page,
                do_submit_bio(sbi, type, false);
 alloc_new:
        if (sbi->bio[type] == NULL) {
-               sbi->bio[type] = f2fs_bio_alloc(bdev, bio_get_nr_vecs(bdev));
+               sbi->bio[type] = f2fs_bio_alloc(bdev, max_hw_blocks(sbi));
                sbi->bio[type]->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr);
                /*
                 * The end_io will be assigned at the sumbission phase.
@@ -713,6 +752,7 @@ alloc_new:
        sbi->last_block_in_bio[type] = blk_addr;
 
        up_write(&sbi->bio_sem);
+       trace_f2fs_submit_write_page(page, blk_addr, type);
 }
 
 static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type)
@@ -1557,14 +1597,13 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
        }
 }
 
-static int init_victim_segmap(struct f2fs_sb_info *sbi)
+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(TOTAL_SEGS(sbi));
+       unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SECS(sbi));
 
-       dirty_i->victim_segmap[FG_GC] = kzalloc(bitmap_size, GFP_KERNEL);
-       dirty_i->victim_segmap[BG_GC] = kzalloc(bitmap_size, GFP_KERNEL);
-       if (!dirty_i->victim_segmap[FG_GC] || !dirty_i->victim_segmap[BG_GC])
+       dirty_i->victim_secmap = kzalloc(bitmap_size, GFP_KERNEL);
+       if (!dirty_i->victim_secmap)
                return -ENOMEM;
        return 0;
 }
@@ -1591,7 +1630,7 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi)
        }
 
        init_dirty_segmap(sbi);
-       return init_victim_segmap(sbi);
+       return init_victim_secmap(sbi);
 }
 
 /*
@@ -1678,18 +1717,10 @@ static void discard_dirty_segmap(struct f2fs_sb_info *sbi,
        mutex_unlock(&dirty_i->seglist_lock);
 }
 
-void reset_victim_segmap(struct f2fs_sb_info *sbi)
-{
-       unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi));
-       memset(DIRTY_I(sbi)->victim_segmap[FG_GC], 0, bitmap_size);
-}
-
-static void destroy_victim_segmap(struct f2fs_sb_info *sbi)
+static void destroy_victim_secmap(struct f2fs_sb_info *sbi)
 {
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
-
-       kfree(dirty_i->victim_segmap[FG_GC]);
-       kfree(dirty_i->victim_segmap[BG_GC]);
+       kfree(dirty_i->victim_secmap);
 }
 
 static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
@@ -1704,7 +1735,7 @@ static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
        for (i = 0; i < NR_DIRTY_TYPE; i++)
                discard_dirty_segmap(sbi, i);
 
-       destroy_victim_segmap(sbi);
+       destroy_victim_secmap(sbi);
        SM_I(sbi)->dirty_info = NULL;
        kfree(dirty_i);
 }