f2fs为了防止宕机对元数据造成不可恢复的损害,所以sit/nat这种元数据有着两个副本,但是这两个副本只有一个是表示最新的数据,f2fs通过保存在cp pack中的sit/nat version bitmap来指示哪个才是最新的。本文将讲述sit和nat两个副本的放置情况,以及sit/nat version bitmap在cp pack中的放置情况,最后将描述sit/nat更新时的version bitmap的变化情况。
下面是根据nid来获取该nid所对应的最新的f2fs_nat_entry所在的f2fs_nat_block所在的块地址。
static pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start)
{
struct f2fs_nm_info *nm_i = NM_I(sbi);
pgoff_t block_off;
pgoff_t block_addr;
int seg_off;
block_off = NAT_BLOCK_OFFSET(start);
seg_off = block_off >> sbi->log_blocks_per_seg;
block_addr = (pgoff_t)(nm_i->nat_blkaddr + (seg_off << sbi->log_blocks_per_seg << 1) +
(block_off & ((1 << sbi->log_blocks_per_seg) -1)));
if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
block_addr += sbi->blocks_per_seg;
return block_addr;
}
根据上面的源码可以看出f2fs_nat_block是以下图的形式放置的。也就是f2fs_nat_entry为最小的单元,但是以f2fs_nat_block的组织形式组织成磁盘上最小的单位块,然后相邻的这些f2fs_nat_block形成一个segment,而相邻的这些f2fs_nat_block的副本也形成一个segment与其相邻放置。然后所有的这些segment以这样的方式重复。
下面是根据segno来获取该segment所对应的最新的f2fs_sit_entry所在的f2fs_sit_block所在的块地址。
static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, unsigned int start)
{
struct sit_info *sit_i = SIT_I(sbi);
unsigned int offset = SIT_BLOCK_OFFSET(start);
block_t blk_addr = sit_i->sit_base_addr + offset;
check_seg_range(sbi, start);
if (f2fs_test_bit(offset, sit_i->sit_bitmap))
blk_addr += sit_i->sit_blocks;
return blk_addr;
}
根据上面的源码可以看出f2fs_sit_block是以下图的形式放置的。也就是f2fs_sit_entry为最小的单元,但是以f2fs_sit_block的组织形式组织成磁盘上最小的单位块,然后所有的f2fs_sit_block的第一个副本相邻放置,而这些f2fs_sit_block的第二个副本叶祥林放置并排列在第一个副本的最后一个f2fs_sit_block的后面。
接着是关于sit/nat version bitmap在cp pack中的放置情况,这个根据下面的函数可以看出。首先解释一下cp_payload这个字段,由于在f2fs的cp pack中的第一个块本来应该放置f2fs_checkpoint这个数据结构的,但是我们发现这个数据结构的大小不够一个block,也就是还有剩余的空间,所以当si/nat version bitmap比较大的时候,那么这两个bitmap是需要额外的空间来保存的,所以cp_payload记录的就是这个额外的空间的块的数量。
static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag)
{
struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);
int offset;
if (__cp_payload(sbi) > 0) {
if (flag == NAT_BITMAP)
return &ckpt->sit_nat_version_bitmap;
else
return (unsigned char *)ckpt + F2FS_BLKSIZE;
} else {
offset = (flag == NAT_BITMAP) ?
le32_to_cpu(ckpt->sit_ver_bitmap_bytesize) : 0;
return &ckpt->sit_nat_version_bitmap + offset;
}
}
根据源码分析一下:当cp_payload > 0时,也就是存在额外的空间来存放bitmap。如果是NAT_BITMAP,那么nat version bitmap是放置在以f2fs_checkpoint的最后一个字段开始的长度为nat_ver_bitmap_bytesize的一段空间中,而对于SIT_BITMAP,sit version bitmap就是放置以cp pack第一个块后面的第二个块开始的长度为sit_ver_bitmap_bytesize的一段空间中。当cp_paylo