这篇文章将讲述f2fs的gc,其主要的步骤应该是分为两步,首先select一个合适的section,然后将section中的数据全部迁移。
f2fs_gc:这个函数主要有两个函数调用gc线程和f2fs_balance_fs。首先检查super_block是否设置MS_ACTIVE,也就是super_block处于活动状态(目前不知道什么个状态),如果设置了就不做gc了。然后再检查是否设置了CP_ERROR_FLAG(这个表示文件系统的没有稳定的cp pack),如果设置了,也是不做gc了。如果此时是BG_GC并且已经没有足够的section了,那么将gc_type设置成FG_GC。如果这种情况下,没有废弃的prefree segment并且调用get_victim函数也没有获得section,但是还有足够的section,那么此时不需做write_checkpoint,其他情况下都要进行write_checkpoint并且选择的segno被设置成NULL_SEGNO。接着检查调用get_victim_by_default来选择victim section来进行垃圾回收。然后就调用do_garbage_collect对选择的section进行数据的迁移。如果是BG_GC进入的f2fs_gc,这个时候还需要检查有没有足够的section,如果没有的话,继续回去进行新一轮的回收,另外如果此时的gc_type是FG_GC,那么进行write_checkpoint操作。
int f2fs_gc(struct f2fs_sb_info *sbi, bool sync)
{
unsigned int segno;
int gc_type = sync ? FG_GC : BG_GC;
int sec_freed = 0;
int ret = -EINVAL;
struct cp_control cpc;
struct gc_inode_list gc_list = {
.ilist = LIST_HEAD_INIT(gc_list.ilist),
.iroot = RADIX_TREE_INIT(GFP_NOFS),
};
cpc.reason = __get_cp_reason(sbi);
gc_more:
segno = NULL_SEGNO;
if (unlikely(!(sbi->sb->s_flags & MS_ACTIVE)))
goto stop;
if (unlikely(f2fs_cp_error(sbi))) {
ret = -EIO;
goto stop;
}
if (gc_type == BG_GC && has_not_enough_free_secs(sbi, sec_freed, 0)) {
gc_type = FG_GC;
if (__get_victim(sbi, &segno, gc_type) || prefree_segments(sbi)) {
ret = write_checkpoint(sbi, &cpc);
if (ret)
goto stop;
segno = NULL_SEGNO;
} else if (has_not_enough_free_secs(sbi, 0, 0)) {
ret = write_checkpoint(sbi, &cpc);
if (ret)
goto stop;
}
}
if (segno == NULL_SEGNO && !__get_victim(sbi, &segno, gc_type))
goto stop;
ret = 0;
if (do_garbage_collect(sbi, segno, &gc_list, gc_type) &&
gc_type == FG_GC)
sec_freed++;
if (gc_type == FG_GC)
sbi->cur_victim_sec = NULL_SEGNO;
if (!sync) {
if (has_not_enough_free_secs(sbi, sec_freed, 0))
goto gc_more;
if (gc_type == FG_GC)
ret = write_checkpoint(sbi, &cpc);
}
stop:
mutex_unlock(&sbi->gc_mutex);
put_gc_inode(&gc_list);
if (sync)
ret = sec_freed ? 0 : -EAGAIN;
return ret;
}
get_victim_by_default:首先初始化选择过程中使用到的数据结构victim_sel_policy,先说一下其字段的含义:alloc_mode,可以取值LFS和SSR,选择过程中这两种的处理模式是不同的。gc_mode这个是计算cost的算法,取值GC_CB和GC_GREEDY。dirty_segmap是记录dirty的segment的位图,选择过程中需要在dirty的segment的section中选择。max_search表示查找过程中的最多的segment的数量,实际就是上述位图的dirty的segment的数量。offset表示在便利过程中的当前的查找偏移。ofs_unit表示在查找过程中每次查找跨越的单元,SSR是以1个segment为单元,LFS是以1个section为单元。min_cost记录查找过程中的最小cost。min_segno记录的是查找过程中最小cost所对应的segno。
struct victim_sel_policy {
int alloc_mode;
int gc_mode;
unsigned long *dirty_segmap;
unsigned int max_search;
unsigned int offset;
unsigned int ofs_unit;
unsigned int min_cost;
unsigned int min_segno;
};
所以其初始化时通过函数select_policy完成的。然后检查是不是以LSF并且是FG_GC的方式进行select,如果是就直接使用之前BG_GC选择过的GC(这样选择的section其有效块数比较少,具体原因不清楚)。如果对应的segno不是NULL_SEGNO,那就找到了。否则需要跟其他的一样对所有的有脏的segment的section进行计算cost,然后选择出最小的cost作为最后的结果。可能是为了均匀的原因,这个遍历不是从头开始的,而是从上次的选择开始的,然后遍历整个循环。首先通过函数find_next