]> git.baikalelectronics.ru Git - kernel.git/commitdiff
f2fs: get the right gc victim section when section has several segments
authorJack Qiu <jack.qiu@huawei.com>
Thu, 18 Jun 2020 04:37:10 +0000 (12:37 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Thu, 18 Jun 2020 19:35:38 +0000 (12:35 -0700)
Assume each section has 4 segment:
     .___________________________.
     |_Segment0_|_..._|_Segment3_|
     .                          .
     .                  .
     .__________.
     |_section0_|

Segment 0~2 has 0 valid block, segment 3 has 512 valid blocks.
It will fail if we want to gc section0 in this scenes,
because all 4 segments in section0 is not dirty.
So we should use dirty section bitmap instead of dirty segment bitmap
to get right victim section.

Signed-off-by: Jack Qiu <jack.qiu@huawei.com>
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/gc.c
fs/f2fs/segment.c
fs/f2fs/segment.h

index 5b95d5a146eb6cde844c7b3b61c44a3b97382b79..9b6fc61ce6493bd537bc9950092017edfce3315b 100644 (file)
@@ -21,6 +21,9 @@
 #include "gc.h"
 #include <trace/events/f2fs.h>
 
+static unsigned int count_bits(const unsigned long *addr,
+                               unsigned int offset, unsigned int len);
+
 static int gc_thread_func(void *data)
 {
        struct f2fs_sb_info *sbi = data;
@@ -187,14 +190,20 @@ static void select_policy(struct f2fs_sb_info *sbi, int gc_type,
 
        if (p->alloc_mode == SSR) {
                p->gc_mode = GC_GREEDY;
-               p->dirty_segmap = dirty_i->dirty_segmap[type];
+               p->dirty_bitmap = dirty_i->dirty_segmap[type];
                p->max_search = dirty_i->nr_dirty[type];
                p->ofs_unit = 1;
        } else {
                p->gc_mode = select_gc_type(sbi, gc_type);
-               p->dirty_segmap = dirty_i->dirty_segmap[DIRTY];
-               p->max_search = dirty_i->nr_dirty[DIRTY];
                p->ofs_unit = sbi->segs_per_sec;
+               if (__is_large_section(sbi)) {
+                       p->dirty_bitmap = dirty_i->dirty_secmap;
+                       p->max_search = count_bits(p->dirty_bitmap,
+                                               0, MAIN_SECS(sbi));
+               } else {
+                       p->dirty_bitmap = dirty_i->dirty_segmap[DIRTY];
+                       p->max_search = dirty_i->nr_dirty[DIRTY];
+               }
        }
 
        /*
@@ -365,10 +374,14 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
        }
 
        while (1) {
-               unsigned long cost;
-               unsigned int segno;
-
-               segno = find_next_bit(p.dirty_segmap, last_segment, p.offset);
+               unsigned long cost, *dirty_bitmap;
+               unsigned int unit_no, segno;
+
+               dirty_bitmap = p.dirty_bitmap;
+               unit_no = find_next_bit(dirty_bitmap,
+                               last_segment / p.ofs_unit,
+                               p.offset / p.ofs_unit);
+               segno = unit_no * p.ofs_unit;
                if (segno >= last_segment) {
                        if (sm->last_victim[p.gc_mode]) {
                                last_segment =
@@ -381,14 +394,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
                }
 
                p.offset = segno + p.ofs_unit;
-               if (p.ofs_unit > 1) {
-                       p.offset -= segno % p.ofs_unit;
-                       nsearched += count_bits(p.dirty_segmap,
-                                               p.offset - p.ofs_unit,
-                                               p.ofs_unit);
-               } else {
-                       nsearched++;
-               }
+               nsearched++;
 
 #ifdef CONFIG_F2FS_CHECK_FS
                /*
@@ -421,9 +427,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
 next:
                if (nsearched >= p.max_search) {
                        if (!sm->last_victim[p.gc_mode] && segno <= last_victim)
-                               sm->last_victim[p.gc_mode] = last_victim + 1;
+                               sm->last_victim[p.gc_mode] =
+                                       last_victim + p.ofs_unit;
                        else
-                               sm->last_victim[p.gc_mode] = segno + 1;
+                               sm->last_victim[p.gc_mode] = segno + p.ofs_unit;
                        sm->last_victim[p.gc_mode] %=
                                (MAIN_SECS(sbi) * sbi->segs_per_sec);
                        break;
index 196f315035118eb2b767c3eded3c728078f7267a..66eeaba30e91868a5e581674f7b570a2138d71b2 100644 (file)
@@ -796,6 +796,18 @@ static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
                }
                if (!test_and_set_bit(segno, dirty_i->dirty_segmap[t]))
                        dirty_i->nr_dirty[t]++;
+
+               if (__is_large_section(sbi)) {
+                       unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
+                       unsigned short valid_blocks =
+                               get_valid_blocks(sbi, segno, true);
+
+                       f2fs_bug_on(sbi, unlikely(!valid_blocks ||
+                                       valid_blocks == BLKS_PER_SEC(sbi)));
+
+                       if (!IS_CURSEC(sbi, secno))
+                               set_bit(secno, dirty_i->dirty_secmap);
+               }
        }
 }
 
@@ -803,6 +815,7 @@ static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno,
                enum dirty_type dirty_type)
 {
        struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
+       unsigned short valid_blocks;
 
        if (test_and_clear_bit(segno, dirty_i->dirty_segmap[dirty_type]))
                dirty_i->nr_dirty[dirty_type]--;
@@ -814,13 +827,26 @@ 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, true) == 0) {
+               valid_blocks = get_valid_blocks(sbi, segno, true);
+               if (valid_blocks == 0) {
                        clear_bit(GET_SEC_FROM_SEG(sbi, segno),
                                                dirty_i->victim_secmap);
 #ifdef CONFIG_F2FS_CHECK_FS
                        clear_bit(segno, SIT_I(sbi)->invalid_segmap);
 #endif
                }
+               if (__is_large_section(sbi)) {
+                       unsigned int secno = GET_SEC_FROM_SEG(sbi, segno);
+
+                       if (!valid_blocks ||
+                                       valid_blocks == BLKS_PER_SEC(sbi)) {
+                               clear_bit(secno, dirty_i->dirty_secmap);
+                               return;
+                       }
+
+                       if (!IS_CURSEC(sbi, secno))
+                               set_bit(secno, dirty_i->dirty_secmap);
+               }
        }
 }
 
@@ -4293,8 +4319,9 @@ 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;
+       unsigned int segno = 0, offset = 0, secno;
        unsigned short valid_blocks;
+       unsigned short blks_per_sec = BLKS_PER_SEC(sbi);
 
        while (1) {
                /* find dirty segment based on free segmap */
@@ -4313,6 +4340,22 @@ static void init_dirty_segmap(struct f2fs_sb_info *sbi)
                __locate_dirty_segment(sbi, segno, DIRTY);
                mutex_unlock(&dirty_i->seglist_lock);
        }
+
+       if (!__is_large_section(sbi))
+               return;
+
+       mutex_lock(&dirty_i->seglist_lock);
+       for (segno = 0; segno < MAIN_SECS(sbi); segno += blks_per_sec) {
+               valid_blocks = get_valid_blocks(sbi, segno, true);
+               secno = GET_SEC_FROM_SEG(sbi, segno);
+
+               if (!valid_blocks || valid_blocks == blks_per_sec)
+                       continue;
+               if (IS_CURSEC(sbi, secno))
+                       continue;
+               set_bit(secno, dirty_i->dirty_secmap);
+       }
+       mutex_unlock(&dirty_i->seglist_lock);
 }
 
 static int init_victim_secmap(struct f2fs_sb_info *sbi)
@@ -4349,6 +4392,14 @@ static int build_dirty_segmap(struct f2fs_sb_info *sbi)
                        return -ENOMEM;
        }
 
+       if (__is_large_section(sbi)) {
+               bitmap_size = f2fs_bitmap_size(MAIN_SECS(sbi));
+               dirty_i->dirty_secmap = f2fs_kvzalloc(sbi,
+                                               bitmap_size, GFP_KERNEL);
+               if (!dirty_i->dirty_secmap)
+                       return -ENOMEM;
+       }
+
        init_dirty_segmap(sbi);
        return init_victim_secmap(sbi);
 }
@@ -4775,6 +4826,12 @@ static void destroy_dirty_segmap(struct f2fs_sb_info *sbi)
        for (i = 0; i < NR_DIRTY_TYPE; i++)
                discard_dirty_segmap(sbi, i);
 
+       if (__is_large_section(sbi)) {
+               mutex_lock(&dirty_i->seglist_lock);
+               kvfree(dirty_i->dirty_secmap);
+               mutex_unlock(&dirty_i->seglist_lock);
+       }
+
        destroy_victim_secmap(sbi);
        SM_I(sbi)->dirty_info = NULL;
        kvfree(dirty_i);
index cba16cca518958d987eb8e414fbcb181f942c6a2..f261e3e6a69bc999937c01af7ad0e3f8a6ed3f1b 100644 (file)
@@ -166,8 +166,11 @@ enum {
 struct victim_sel_policy {
        int alloc_mode;                 /* LFS or SSR */
        int gc_mode;                    /* GC_CB or GC_GREEDY */
-       unsigned long *dirty_segmap;    /* dirty segment bitmap */
-       unsigned int max_search;        /* maximum # of segments to search */
+       unsigned long *dirty_bitmap;    /* dirty segment/section bitmap */
+       unsigned int max_search;        /*
+                                        * maximum # of segments/sections
+                                        * to search
+                                        */
        unsigned int offset;            /* last scanned bitmap offset */
        unsigned int ofs_unit;          /* bitmap search unit */
        unsigned int min_cost;          /* minimum cost */
@@ -266,6 +269,7 @@ enum dirty_type {
 struct dirty_seglist_info {
        const struct victim_selection *v_ops;   /* victim selction operation */
        unsigned long *dirty_segmap[NR_DIRTY_TYPE];
+       unsigned long *dirty_secmap;
        struct mutex seglist_lock;              /* lock for segment bitmaps */
        int nr_dirty[NR_DIRTY_TYPE];            /* # of dirty segments */
        unsigned long *victim_secmap;           /* background GC victims */