NAT区中两个segment为一组,互为备份(第一个记作segment A,第二个记作segment B),nid对应的f2fs_nat_entry既可能在A中,也可能在B中,如果nm_i->nat_bitmap中的bit置位,那么B中的entry是有效的,否则 A中的entry有效。
f2fs挂载时,f2fs_fill_super --> f2fs_build_node_manager --> init_node_manager将存储设备的checkpoint区中的nat位图复制到nm_i->nat_bitmap。
version_bitmap = __bitmap_ptr(sbi, NAT_BITMAP);
nm_i->nat_bitmap = kmemdup(version_bitmap, nm_i->bitmap_size,
GFP_KERNEL);
nat bitmap中的bit=1,表示nat entry位于segment B中;bit=0,表示nat entry位于segment A中。
struct f2fs_nm_info->nat_blkaddr是NAT区域起始block号。
一个block中含有NAT_ENTRY_PER_BLOCK个entry(455个),根据nid可以算出nat entry在第N个block中(见NAT_BLOCK_OFFSET宏)。为了说明方便,N称为nat block offset(不考虑备份的情况下,从nat area起始block开始算,nat entry在第几个block上)。
/* node block offset on the NAT area dedicated to the given start node id */
#define NAT_BLOCK_OFFSET(start_nid) ((start_nid) / NAT_ENTRY_PER_BLOCK)
current_nat_addr函数用于获取nid对应的nat entry所在的block的块地址。
static inline 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;
/* 这段注释会误导人理解下面的代码,请忽略 */
/*
* block_off = segment_off * 512 + off_in_segment
* OLD = (segment_off * 512) * 2 + off_in_segment
* NEW = 2 * (segment_off * 512 + off_in_segment) - off_in_segment
*/
/*
* block_off即上文中红字部分提到的nat block offset(这是没有考虑segment备份的偏移值).
*/
block_off = NAT_BLOCK_OFFSET(start);
/*
* NAT区中2个segment互为备份,第一个记作segment A,第二个记作segment B.
*
* 由于segment是双备份的,所以需要将block_off << 1, 得到nid=start这个nat entry所在的
* block的真实偏移(考虑备份,从nat的起始block算).
* nm_i->nat_blkaddr + (block_off << 1) 计算出nid=start的nat entr所属的磁盘块的块地址.
*
* block_off & (sbi->blocks_per_seg - 1)表示block_off块号在所属segment中的偏移.
*
* block_addr表示nat entry位于segment A时,nat entry所属磁盘块的块地址.
*/
block_addr = (pgoff_t)(nm_i->nat_blkaddr +
(block_off << 1) -
(block_off & (sbi->blocks_per_seg - 1)));
/*
* nm_i->nat_bitmap置位,表示nat entry位于segment B,
* 将block_addr再加上一个segment中的block数,计算出nat entry所属磁盘块的块地址.*/
if (f2fs_test_bit(block_off, nm_i->nat_bitmap))
block_addr += sbi->blocks_per_seg;
return block_addr;
}