Linux f2fs_check_valid_map segment位图与SSR分配f2fs将整个闪存空间划分为固定大小的segment默认2MB每个segment又细分为多个block4KB。segment的分配和回收状态通过struct f2fs_sit_entry和struct seg_entry管理。其中seg_entry包含一个关键的成员valid_map这是一个位图每个bit对应segment内的一个block标记该block是否包含有效数据。struct seg_entry {unsigned short valid_blocks;unsigned char *valid_map;unsigned char *discard_map;unsigned int type;unsigned long long mtime;unsigned long long atime;...};valid_map的检查和操作通过f2fs_check_valid_map()函数实现int f2fs_check_valid_map(struct f2fs_sb_info *sbi,unsigned int segno,unsigned int offset){struct seg_entry *sentry;struct f2fs_sit_entry *sit;unsigned int seg_blocks sbi-blocks_per_seg;sentry get_seg_entry(sbi, segno);if (offset seg_blocks)return -EFSCORRUPTED;if (!test_bit(offset, sentry-valid_map))return 0;return -EINVAL;}该函数首先通过get_seg_entry()从SITSegment Information Table缓存中获取指定segment的seg_entry结构。SIT在内存中以数组形式组织索引为segment号。然后检查offset是否超出segment的block范围。最后通过test_bit()测试valid_map中对应bit是否为1。返回0表示该block无效可分配返回-EINVAL表示block已被占用。SIT的持久化存储在磁盘的SIT area中。每个segment对应一个struct f2fs_sit_entry包含32位valid_map实际位图数据和VBLOCKS字段记录有效block计数struct f2fs_sit_entry {__le16 vblocks;__u8 reserved[6];__le64 valid_map;} __packed;SIT数据在mount时被加载到内存并在运行时持续更新。在checkpoint过程中脏SIT条目被写回磁盘的SIT area。f2fs使用SIT journal进一步优化在checkpoint block中嵌入最近修改的SIT条目减少对SIT area的大块写入。f2fs_check_valid_map()在实际分配block时被调用。f2fs的分配器有两种模式顺序分配LFS, Log-structured File System和原地分配SSR, Slice Space Reclamation。SSR模式下分配器会优先选择valid_blocks较少的segment进行覆盖写入此时必须通过f2fs_check_valid_map()确保待分配的block确实为空static int f2fs_allocate_block_ssr(struct f2fs_sb_info *sbi,struct victim_sel_policy *p){struct seg_entry *se;unsigned int segno;int offset;int ret;segno f2fs_victim_segment_from_policy(sbi, p);se get_seg_entry(sbi, segno);offset f2fs_find_next_zero_bit(se-valid_map,sbi-blocks_per_seg, 0);while (offset sbi-blocks_per_seg) {ret f2fs_check_valid_map(sbi, segno, offset);if (ret 0) {f2fs_set_bit(offset, se-valid_map);se-valid_blocks;return segno * sbi-blocks_per_seg offset;}offset f2fs_find_next_zero_bit(se-valid_map,sbi-blocks_per_seg,offset 1);}return -ENOSPC;}在SSR分配中f2fs_find_next_zero_bit()用于扫描valid_map找到第一个为0的bit。每次找到候选位置后通过f2fs_check_valid_map()双重验证。验证通过后调用f2fs_set_bit()将对应位置1并递增valid_blocks计数。valid_map的更新还涉及垃圾回收操作。当GC决定迁移一个segment的部分block时迁移完成后通过f2fs_i_blocks_write()更新inode的i_blocks计数并调用f2fs_clear_bit()清除original segment中对应block的valid_map位void f2fs_clear_bit(unsigned int offset, unsigned char *addr){unsigned int mask BIT(offset (BITS_PER_BYTE - 1));addr[offset BITS_PER_BYTE_SHIFT] ~mask;}f2fs还利用valid_map和valid_blocks进行segment预判。在CPcheckpoint过程中f2fs会扫描所有segment的valid_blocks将valid_blocks为0的segment加入空闲链表static void f2fs_update_sit_entry(struct f2fs_sb_info *sbi,block_t blkaddr, int del){unsigned int segno GET_SEGNO(sbi, blkaddr);unsigned int offset GET_BLKOFF_FROM_SEG0(sbi, blkaddr);struct seg_entry *se get_seg_entry(sbi, segno);if (del 0) {if (!test_bit(offset, se-valid_map)) {set_bit(offset, se-valid_map);se-valid_blocks;}} else {if (test_bit(offset, se-valid_map)) {clear_bit(offset, se-valid_map);se-valid_blocks--;if (se-valid_blocks 0)f2fs_free_segment(sbi, segno);}}}当valid_blocks递减到0时整个segment被标记为free。在CP时free segment对应的SIT条目被更新为全0SSASegment Summary Area中对应的summary条目被清除。valid_map的边界检查和一致性验证在f2fs的fsck流程中也有关键作用。当扫描目录或文件数据块时fsck遍历valid_map与inode中记录的数据块地址进行交叉校验识别悬挂块或重复块分配错误。
Linux f2fs_check_valid_map segment位图与SSR分配
Linux f2fs_check_valid_map segment位图与SSR分配f2fs将整个闪存空间划分为固定大小的segment默认2MB每个segment又细分为多个block4KB。segment的分配和回收状态通过struct f2fs_sit_entry和struct seg_entry管理。其中seg_entry包含一个关键的成员valid_map这是一个位图每个bit对应segment内的一个block标记该block是否包含有效数据。struct seg_entry {unsigned short valid_blocks;unsigned char *valid_map;unsigned char *discard_map;unsigned int type;unsigned long long mtime;unsigned long long atime;...};valid_map的检查和操作通过f2fs_check_valid_map()函数实现int f2fs_check_valid_map(struct f2fs_sb_info *sbi,unsigned int segno,unsigned int offset){struct seg_entry *sentry;struct f2fs_sit_entry *sit;unsigned int seg_blocks sbi-blocks_per_seg;sentry get_seg_entry(sbi, segno);if (offset seg_blocks)return -EFSCORRUPTED;if (!test_bit(offset, sentry-valid_map))return 0;return -EINVAL;}该函数首先通过get_seg_entry()从SITSegment Information Table缓存中获取指定segment的seg_entry结构。SIT在内存中以数组形式组织索引为segment号。然后检查offset是否超出segment的block范围。最后通过test_bit()测试valid_map中对应bit是否为1。返回0表示该block无效可分配返回-EINVAL表示block已被占用。SIT的持久化存储在磁盘的SIT area中。每个segment对应一个struct f2fs_sit_entry包含32位valid_map实际位图数据和VBLOCKS字段记录有效block计数struct f2fs_sit_entry {__le16 vblocks;__u8 reserved[6];__le64 valid_map;} __packed;SIT数据在mount时被加载到内存并在运行时持续更新。在checkpoint过程中脏SIT条目被写回磁盘的SIT area。f2fs使用SIT journal进一步优化在checkpoint block中嵌入最近修改的SIT条目减少对SIT area的大块写入。f2fs_check_valid_map()在实际分配block时被调用。f2fs的分配器有两种模式顺序分配LFS, Log-structured File System和原地分配SSR, Slice Space Reclamation。SSR模式下分配器会优先选择valid_blocks较少的segment进行覆盖写入此时必须通过f2fs_check_valid_map()确保待分配的block确实为空static int f2fs_allocate_block_ssr(struct f2fs_sb_info *sbi,struct victim_sel_policy *p){struct seg_entry *se;unsigned int segno;int offset;int ret;segno f2fs_victim_segment_from_policy(sbi, p);se get_seg_entry(sbi, segno);offset f2fs_find_next_zero_bit(se-valid_map,sbi-blocks_per_seg, 0);while (offset sbi-blocks_per_seg) {ret f2fs_check_valid_map(sbi, segno, offset);if (ret 0) {f2fs_set_bit(offset, se-valid_map);se-valid_blocks;return segno * sbi-blocks_per_seg offset;}offset f2fs_find_next_zero_bit(se-valid_map,sbi-blocks_per_seg,offset 1);}return -ENOSPC;}在SSR分配中f2fs_find_next_zero_bit()用于扫描valid_map找到第一个为0的bit。每次找到候选位置后通过f2fs_check_valid_map()双重验证。验证通过后调用f2fs_set_bit()将对应位置1并递增valid_blocks计数。valid_map的更新还涉及垃圾回收操作。当GC决定迁移一个segment的部分block时迁移完成后通过f2fs_i_blocks_write()更新inode的i_blocks计数并调用f2fs_clear_bit()清除original segment中对应block的valid_map位void f2fs_clear_bit(unsigned int offset, unsigned char *addr){unsigned int mask BIT(offset (BITS_PER_BYTE - 1));addr[offset BITS_PER_BYTE_SHIFT] ~mask;}f2fs还利用valid_map和valid_blocks进行segment预判。在CPcheckpoint过程中f2fs会扫描所有segment的valid_blocks将valid_blocks为0的segment加入空闲链表static void f2fs_update_sit_entry(struct f2fs_sb_info *sbi,block_t blkaddr, int del){unsigned int segno GET_SEGNO(sbi, blkaddr);unsigned int offset GET_BLKOFF_FROM_SEG0(sbi, blkaddr);struct seg_entry *se get_seg_entry(sbi, segno);if (del 0) {if (!test_bit(offset, se-valid_map)) {set_bit(offset, se-valid_map);se-valid_blocks;}} else {if (test_bit(offset, se-valid_map)) {clear_bit(offset, se-valid_map);se-valid_blocks--;if (se-valid_blocks 0)f2fs_free_segment(sbi, segno);}}}当valid_blocks递减到0时整个segment被标记为free。在CP时free segment对应的SIT条目被更新为全0SSASegment Summary Area中对应的summary条目被清除。valid_map的边界检查和一致性验证在f2fs的fsck流程中也有关键作用。当扫描目录或文件数据块时fsck遍历valid_map与inode中记录的数据块地址进行交叉校验识别悬挂块或重复块分配错误。