Segment Info Table 占据大小由Main Area中Segment数量决定。主要用于记录Main Area中Segment的分配信息,例如哪些Segment可用,Segment最后修改时间等信息。
综上所述,SIT的作用是维护每一个segment的block的使用状态以及有效无效状态,目的是便于分配block以及垃圾回收。
每个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;
}