f2fs: get the right gc victim section when section has several segments
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>
This commit is contained in:
parent
db5ae36329
commit
da52f8ade4
37
fs/f2fs/gc.c
37
fs/f2fs/gc.c
@ -21,6 +21,9 @@
|
|||||||
#include "gc.h"
|
#include "gc.h"
|
||||||
#include <trace/events/f2fs.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)
|
static int gc_thread_func(void *data)
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = 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) {
|
if (p->alloc_mode == SSR) {
|
||||||
p->gc_mode = GC_GREEDY;
|
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->max_search = dirty_i->nr_dirty[type];
|
||||||
p->ofs_unit = 1;
|
p->ofs_unit = 1;
|
||||||
} else {
|
} else {
|
||||||
p->gc_mode = select_gc_type(sbi, gc_type);
|
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;
|
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) {
|
while (1) {
|
||||||
unsigned long cost;
|
unsigned long cost, *dirty_bitmap;
|
||||||
unsigned int segno;
|
unsigned int unit_no, segno;
|
||||||
|
|
||||||
segno = find_next_bit(p.dirty_segmap, last_segment, p.offset);
|
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 (segno >= last_segment) {
|
||||||
if (sm->last_victim[p.gc_mode]) {
|
if (sm->last_victim[p.gc_mode]) {
|
||||||
last_segment =
|
last_segment =
|
||||||
@ -381,14 +394,7 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||||||
}
|
}
|
||||||
|
|
||||||
p.offset = segno + p.ofs_unit;
|
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
|
#ifdef CONFIG_F2FS_CHECK_FS
|
||||||
/*
|
/*
|
||||||
@ -421,9 +427,10 @@ static int get_victim_by_default(struct f2fs_sb_info *sbi,
|
|||||||
next:
|
next:
|
||||||
if (nsearched >= p.max_search) {
|
if (nsearched >= p.max_search) {
|
||||||
if (!sm->last_victim[p.gc_mode] && segno <= last_victim)
|
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
|
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] %=
|
sm->last_victim[p.gc_mode] %=
|
||||||
(MAIN_SECS(sbi) * sbi->segs_per_sec);
|
(MAIN_SECS(sbi) * sbi->segs_per_sec);
|
||||||
break;
|
break;
|
||||||
|
@ -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]))
|
if (!test_and_set_bit(segno, dirty_i->dirty_segmap[t]))
|
||||||
dirty_i->nr_dirty[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)
|
enum dirty_type dirty_type)
|
||||||
{
|
{
|
||||||
struct dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
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]))
|
if (test_and_clear_bit(segno, dirty_i->dirty_segmap[dirty_type]))
|
||||||
dirty_i->nr_dirty[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]))
|
if (test_and_clear_bit(segno, dirty_i->dirty_segmap[t]))
|
||||||
dirty_i->nr_dirty[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),
|
clear_bit(GET_SEC_FROM_SEG(sbi, segno),
|
||||||
dirty_i->victim_secmap);
|
dirty_i->victim_secmap);
|
||||||
#ifdef CONFIG_F2FS_CHECK_FS
|
#ifdef CONFIG_F2FS_CHECK_FS
|
||||||
clear_bit(segno, SIT_I(sbi)->invalid_segmap);
|
clear_bit(segno, SIT_I(sbi)->invalid_segmap);
|
||||||
#endif
|
#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 dirty_seglist_info *dirty_i = DIRTY_I(sbi);
|
||||||
struct free_segmap_info *free_i = FREE_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 valid_blocks;
|
||||||
|
unsigned short blks_per_sec = BLKS_PER_SEC(sbi);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
/* find dirty segment based on free segmap */
|
/* 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);
|
__locate_dirty_segment(sbi, segno, DIRTY);
|
||||||
mutex_unlock(&dirty_i->seglist_lock);
|
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)
|
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;
|
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);
|
init_dirty_segmap(sbi);
|
||||||
return init_victim_secmap(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++)
|
for (i = 0; i < NR_DIRTY_TYPE; i++)
|
||||||
discard_dirty_segmap(sbi, 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);
|
destroy_victim_secmap(sbi);
|
||||||
SM_I(sbi)->dirty_info = NULL;
|
SM_I(sbi)->dirty_info = NULL;
|
||||||
kvfree(dirty_i);
|
kvfree(dirty_i);
|
||||||
|
@ -166,8 +166,11 @@ enum {
|
|||||||
struct victim_sel_policy {
|
struct victim_sel_policy {
|
||||||
int alloc_mode; /* LFS or SSR */
|
int alloc_mode; /* LFS or SSR */
|
||||||
int gc_mode; /* GC_CB or GC_GREEDY */
|
int gc_mode; /* GC_CB or GC_GREEDY */
|
||||||
unsigned long *dirty_segmap; /* dirty segment bitmap */
|
unsigned long *dirty_bitmap; /* dirty segment/section bitmap */
|
||||||
unsigned int max_search; /* maximum # of segments to search */
|
unsigned int max_search; /*
|
||||||
|
* maximum # of segments/sections
|
||||||
|
* to search
|
||||||
|
*/
|
||||||
unsigned int offset; /* last scanned bitmap offset */
|
unsigned int offset; /* last scanned bitmap offset */
|
||||||
unsigned int ofs_unit; /* bitmap search unit */
|
unsigned int ofs_unit; /* bitmap search unit */
|
||||||
unsigned int min_cost; /* minimum cost */
|
unsigned int min_cost; /* minimum cost */
|
||||||
@ -266,6 +269,7 @@ enum dirty_type {
|
|||||||
struct dirty_seglist_info {
|
struct dirty_seglist_info {
|
||||||
const struct victim_selection *v_ops; /* victim selction operation */
|
const struct victim_selection *v_ops; /* victim selction operation */
|
||||||
unsigned long *dirty_segmap[NR_DIRTY_TYPE];
|
unsigned long *dirty_segmap[NR_DIRTY_TYPE];
|
||||||
|
unsigned long *dirty_secmap;
|
||||||
struct mutex seglist_lock; /* lock for segment bitmaps */
|
struct mutex seglist_lock; /* lock for segment bitmaps */
|
||||||
int nr_dirty[NR_DIRTY_TYPE]; /* # of dirty segments */
|
int nr_dirty[NR_DIRTY_TYPE]; /* # of dirty segments */
|
||||||
unsigned long *victim_secmap; /* background GC victims */
|
unsigned long *victim_secmap; /* background GC victims */
|
||||||
|
Loading…
Reference in New Issue
Block a user