Segment Info Table 占据大小由Main Area中Segment数量决定。主要用于记录Main Area中Segment的分配信息,例如哪些Segment可用,Segment最后修改时间等信息。

综上所述,SIT的作用是维护每一个segment的block的使用状态以及有效无效状态,目的是便于分配block以及垃圾回收。

SIT的盘上结构

image.png

每个f2fs_sit_block包含55个f2fs_sit_entry。每一个entry对应一个segment的管理状态.

struct f2fs_sit_block {
	struct f2fs_sit_entry entries[SIT_ENTRY_PER_BLOCK]; //55
} __packed;

由于一个block的尺寸是4KB,因此根据sizeof(struct f2fs_sit_entry entries)的值,得到SIT_ENTRY_PER_BLOCK的值为55。struct f2fs_sit_entry entries用来表示每一个segment的状态信息,它的结构如下:

struct f2fs_sit_entry {
	__le16 vblocks;				/* reference above */
	__u8 valid_map[SIT_VBLOCK_MAP_SIZE];	/* bitmap for valid blocks */
	__le64 mtime;				/* segment age for cleaning */
} __packed;

#define SIT_VBLOCKS_SHIFT	10
#define SIT_VBLOCKS_MASK	((1 << SIT_VBLOCKS_SHIFT) - 1)
#define GET_SIT_VBLOCKS(raw_sit)				\\
	(le16_to_cpu((raw_sit)->vblocks) & SIT_VBLOCKS_MASK)
#define GET_SIT_TYPE(raw_sit)					\\
	((le16_to_cpu((raw_sit)->vblocks) & ~SIT_VBLOCKS_MASK)	\\
	 >> SIT_VBLOCKS_SHIFT)

其中vblocks前10位表示这个seg里面已使用的block数目,后6位表示segment type。

valid_map是有效块的位图。

mtime表示修改时间,用于计算这个segment的年龄,GC的时候用。

内存管理结构

SIT表项在内存中的结构是struct seg_entry。与盘上表项相比,多了表示segment类型的type和表示cp内容的ckpt_valid_blocks和ckpt_valid_map。但是盘上表项也存储了type,其中vblocks的前10位表示数目,后6位表示segment type。

// 内存entry
struct seg_entry {
	unsigned short valid_blocks;	/* # of valid blocks */
	unsigned char *cur_valid_map;	/* validity bitmap of blocks */
	unsigned short ckpt_valid_blocks;
	unsigned char *ckpt_valid_map;
	unsigned char type;		/* segment type like CURSEG_XXX_TYPE */
	unsigned long long mtime;	/* modification time of the segment */
};

SIT在内存中对应的管理结构是struct f2fs_sm_info,它在 build_segment_manager函数进行初始化:

int build_segment_manager(struct f2fs_sb_info *sbi)
{
	struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
	struct f2fs_sm_info *sm_info;
	int err;

    /* 分配空间 */
	sm_info = kzalloc(sizeof(struct f2fs_sm_info), GFP_KERNEL);

	/* 初始化一些地址信息,基础信息 */
	sbi->sm_info = sm_info;
	INIT_LIST_HEAD(&sm_info->wblist_head);
	spin_lock_init(&sm_info->wblist_lock);
	sm_info->seg0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr);
	sm_info->main_blkaddr = le32_to_cpu(raw_super->main_blkaddr);
	sm_info->segment_count = le32_to_cpu(raw_super->segment_count);
	sm_info->reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count);
	sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count);
	sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main);
	sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr);

    /* 初始化内存中的entry数据结构 */
	err = build_sit_info(sbi);
    
    /* 初始化可用segment的数据结构 */
	err = build_free_segmap(sbi);

    /* 恢复checkpoint active segment区域的信息,参考checkpoint结构那一节 */
	err = build_curseg(sbi);

	/* 从磁盘中将SIT物理区域记录的 物理区域sit_entry与只存在于内存的sit_entry建立联系 */
	build_sit_entries(sbi);

    /* 根据checkpoint记录的恢复信息,恢复可用segment的映射关系 */
	init_free_segmap(sbi);
    
    /* 恢复脏segment的映射关系 */
	err = build_dirty_segmap(sbi);

    /* 初始化最大最小的修改时间 */
	init_min_max_mtime(sbi);
	return 0;
}

未列出的参考资料