F2FS源码分析-1.3 [F2FS 元数据布局部分] Checkpoint结构
F2FS源码分析系列文章
主目录
一、文件系统布局以及元数据结构
- 总体结构
- Superblock区域
- Checkpoint区域
- Segment Infomation Table区域(SIT)
- Node Address Table区域(NAT)
- Segment Summary Area区域(SSA)
二、文件数据的存储以及读写
三、文件与目录的创建以及删除(未完成)
四、垃圾回收机制
五、数据恢复机制
六、重要数据结构或者函数的分析
Checkpoint区域
Checkpoint是维护F2FS的数据一致性的结构,它维护了系统当前的状态,例如segment的分配情况,node的分配情况,以及当前的active segment的状态等。F2FS在满足一定的条件的情况下,会将当前系统的分配状态写入到Checkpoint中,万一系统出现突然宕机,这个是F2FS可以从Checkpoint中恢复到上次回写时的状态,以保证数据的可恢复性。F2FS维护了两个Checkpoint结构,互为备份,其中一个是当前正在使用的Checkpoint,另外一个上次回写的稳定的Chcekpoint。如果系统出现了宕机,那么当前的Checkpoint就会变得不可信任,进而使用备份Checkpoint进行恢复。
Checkpoint在元数据区域的物理结构
根据上述的结构图,Checkpoint区域由几个部分构成,分别是checkpoint元数据区域(f2fs_checkpoint)、orphan node区域、active segments区域。同时active segments区域在不同的情况下,会有不同的形式,目的是减少IO的写入。接下来分别讨论Checkpoint不同的部分。
Checkpoint元数据区域
F2FS使用数据结构f2fs_checkpoint
表示Checkpoint结构,它保存在磁盘中f2fs_super_block
之后区域中,数据结构如下。需要特别注意的是cur_node_segno
、cur_node_blkoff
、cur_data_segno
、cur_data_blkoff
这几个变量。第一节提到,F2FS分为了6个log区域,分别对应hot node/data、warm node/data、cold node/data。F2FS必须定时执行Checkpoint去记录当前系统的log分配到哪个位置,否则在系统宕机的时候,会出现数据丢失等一致性问题,因此cur_xxx_segno
以及cur_xxx_blkoff
记录了上次Checkpoint时,系统正在使用的log的segment number,以及分配到这个segment的哪个位置。
struct f2fs_checkpoint {__le64 checkpoint_ver; /* CP版本,用于比较新旧版本进行恢复 */__le64 user_block_count; /* # of user blocks */__le64 valid_block_count; /* # of valid blocks in main area */__le32 rsvd_segment_count; /* # of reserved segments for gc */__le32 overprov_segment_count; /* # of overprovision segments */__le32 free_segment_count; /* # of free segments in main area *//* information of current node segments */__le32 cur_node_segno[MAX_ACTIVE_NODE_LOGS];__le16 cur_node_blkoff[MAX_ACTIVE_NODE_LOGS];/* information of current data segments */__le32 cur_data_segno[MAX_ACTIVE_DATA_LOGS];__le16 cur_data_blkoff[MAX_ACTIVE_DATA_LOGS];__le32 ckpt_flags; /* Flags : umount and journal_present */__le32 cp_pack_total_block_count; /* total # of one cp pack */__le32 cp_pack_start_sum; /* start block number of data summary */__le32 valid_node_count; /* Total number of valid nodes */__le32 valid_inode_count; /* Total number of valid inodes */__le32 next_free_nid; /* Next free node number */__le32 sit_ver_bitmap_bytesize; /* Default value 64 */__le32 nat_ver_bitmap_bytesize; /* Default value 256 */__le32 checksum_offset; /* checksum offset inside cp block */__le64 elapsed_time; /* mounted time *//* allocation type of current segment */unsigned char alloc_type[MAX_ACTIVE_LOGS];/* SIT and NAT version bitmap */unsigned char sit_nat_version_bitmap[1];
} __packed;
Orphan node区域
这是一个动态的区域,如果没有orphan node list则不会占用空间。
Active Segments区域
Active Segments的定义
Active Segments,又称current segment(CURSEG),即当前正在用于进行数据分配的log对应的segment,如用户需要写入8KB数据,那么就会从active segments分配两个block提供给用户写入到磁盘中。F2FS为了提高数据分配的效率,根据数据的特性,一共定义了6个active segment。如第一章的总体结构这一节提到的multi-head logging特性所描述,这6个active segments对应了(how, warm, cold) X (node, data)的数据。
Active Segment与恢复相关的数据结构
CP的主要任务是维护数据一致性,因此CP的active segment区域的主要任务是维护Active Segment的分配状态,使系统宕机时候可以恢复正常。维护active segment需要维护三种信息,分别是f2fs_checkpoint
的信息,以及该segment对应的journal和summary的信息。
f2fs_checkpoint中Active Segment信息:从上面给出的
f2fs_checkpoint
定义,cur_node_segno[MAX_ACTIVE_NODE_LOGS]
和cur_data_segno[MAX_ACTIVE_DATA_LOGS]
表示node和data当前的Active Segment的编号(segment number, segno),系统可以通过这个编号找到对应的segment。MAX_ACTIVE_NODE_LOGS
以及MAX_ACTIVE_NODE_LOGS
分别表示data和node有多少种类型,F2FS默认情况下都等于3,表示、、即HOT、WARM、COLD类型数据。cur_node_blkoff[MAX_ACTIVE_NODE_LOGS]
以及cur_data_blkoff[MAX_ACTIVE_DATA_LOGS]
则分别表示当前active segment分配到哪一个block(一个segment包含了512个block)。Segment对应的Journal信息:Journal在两处地方都有出现,分别是CP区域以及SSA区域。CP区域的journal主要用来保存active segment的修改信息,而SSA区域的则是持久化保存的所有的segment的journal信息。如系统分配出一个block给用户,那么就要将这个block所在的segment的bitmap中标记为已分配,防止其他写请求使用。分两个区域存放journal是为了减轻频繁更新导致的系统性能下降。例如,当系统写压力很大的时候,bitmap就会频繁被更新,如果这个时候频繁将bitmap写入SSA,就会加重写压力。因此CP区域的Journal的作用就是维护这些经常修改的数据,等待CP被触发的时候才回写到闪存设备,从而减少写压力,提高闪存寿命。(journal的实现参考第六章的journal这一节)
Segment对应的Summary信息:summary同样在CP区域和SSA区域有出现,它表示的是逻辑地址和物理地址的映射关系,这个映射关系会使用到GC流程中。summary与segment是一对一的关系,一个summary保存了一个segment所有的block的物理地址和逻辑地址的映射关系。summary保存在CP区域中同样是出于减少IO的写入。
Checkpoint内存管理结构
Checkpoint的内存管理结构是struct f2fs_checkpoint
本身,因为Checkpoint一般只在F2FS启动的时候被读取数据,用于数据恢复,而在运行过程中大部分情况都是被写,用于记录恢复信息。因此,Checkpoint不需要过于复杂的内存管理结构,因此使用struct f2fs_checkpoint
本身即可以满足需求。
另一方面,active segments,即F2FS的log,主要用于系统free block的分配,因此需要特定的管理结构struct curseg_info
进行管理,它的定义如下:
struct curseg_info {struct mutex curseg_mutex;struct f2fs_summary_block *sum_blk; /* 每一个segment对应一个summary block */struct rw_semaphore journal_rwsem;struct f2fs_journal *journal; /*每一个segment对应一个 info */unsigned char alloc_type;unsigned int segno; /* 当前segno */unsigned short next_blkoff; /* 记录当前segment用于分配的下一个给block号 */unsigned int zone; /* current zone number */unsigned int next_segno; /* 当前segno用完以后,下个即将用来分配的segno */
};
从结构分析可以直到,curseg_info
记录当前的segment的分配信息,当系统出现宕机的时候,可以从CP记录的curseg_info
恢复当上一次CP点的状态。
每一种类型的active segment就对应一个struct curseg_info
结构。在F2FS中,使用一个数组来表示:
struct f2fs_sm_info {...struct curseg_info *curseg_array; // 默认是分配6个curseg_info,分别对应不同类型...
}
struct f2fs_sm_info
是SIT的管理结构,它也管理了CP最终的active segment的信息,是一个跨区域的管理结构。
struct f2fs_checkpoint
通过get_checkpoint_version
函数从磁盘读取出来:
static int get_checkpoint_version(struct f2fs_sb_info *sbi, block_t cp_addr,struct f2fs_checkpoint **cp_block, struct page **cp_page,unsigned long long *version)
{unsigned long blk_size = sbi->blocksize;size_t crc_offset = 0;__u32 crc = 0;*cp_page = f2fs_get_meta_page(sbi, cp_addr); // 根据CP所在的地址cp_addr从磁盘读取一个block*cp_block = (struct f2fs_checkpoint *)page_address(*cp_page); // 直接转换为数据结构crc_offset = le32_to_cpu((*cp_block)->checksum_offset);if (crc_offset > (blk_size - sizeof(__le32))) {f2fs_msg(sbi->sb, KERN_WARNING,"invalid crc_offset: %zu", crc_offset);return -EINVAL;}crc = cur_cp_crc(*cp_block);if (!f2fs_crc_valid(sbi, crc, *cp_block, crc_offset)) { // 比较CRC的值,进而知道是否成功读取出来f2fs_msg(sbi->sb, KERN_WARNING, "invalid crc value");return -EINVAL;}*version = cur_cp_version(*cp_block);return 0;
}
struct curseg_info
则是通过build_curseg
函数进行初始化:
static int build_curseg(struct f2fs_sb_info *sbi)
{struct curseg_info *array;int i;array = f2fs_kzalloc(sbi, array_size(NR_CURSEG_TYPE, sizeof(*array)),GFP_KERNEL); // 根据active segment类型的数目分配空间if (!array)return -ENOMEM;SM_I(sbi)->curseg_array = array; // 赋值到f2fs_sm_info->curseg_arrayfor (i = 0; i < NR_CURSEG_TYPE; i++) { // 为curseg的其他信息分配空间mutex_init(&array[i].curseg_mutex);array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL);if (!array[i].sum_blk)return -ENOMEM;init_rwsem(&array[i].journal_rwsem);array[i].journal = f2fs_kzalloc(sbi,sizeof(struct f2fs_journal), GFP_KERNEL);if (!array[i].journal)return -ENOMEM;array[i].segno = NULL_SEGNO;array[i].next_blkoff = 0;}return restore_curseg_summaries(sbi); // 从f2fs_checkpoint恢复上一个CP点CURSEG的状态
}static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
{struct f2fs_journal *sit_j = CURSEG_I(sbi, CURSEG_COLD_DATA)->journal;struct f2fs_journal *nat_j = CURSEG_I(sbi, CURSEG_HOT_DATA)->journal;int type = CURSEG_HOT_DATA;int err;...for (; type <= CURSEG_COLD_NODE; type++) { // 按类型逐个恢复active segment的信息err = read_normal_summaries(sbi, type);if (err)return err;}...return 0;
}static int read_normal_summaries(struct f2fs_sb_info *sbi, int type)
{struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);struct f2fs_summary_block *sum;struct curseg_info *curseg;struct page *new;unsigned short blk_off;unsigned int segno = 0;block_t blk_addr = 0;...segno = le32_to_cpu(ckpt->cur_data_segno[type]); // 从CP读取segnoblk_off = le16_to_cpu(ckpt->cur_data_blkoff[type - CURSEG_HOT_DATA]); // 从CP读取blk_offblk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); // 获取summary block地址 // 读取&转换结构new = f2fs_get_meta_page(sbi, blk_addr);sum = (struct f2fs_summary_block *)page_address(new);curseg = CURSEG_I(sbi, type); // 根据type找到对应的cursegmutex_lock(&curseg->curseg_mutex);/* 复制&恢复数据 */down_write(&curseg->journal_rwsem);memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE);up_write(&curseg->journal_rwsem);memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE);memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE);curseg->next_segno = segno;reset_curseg(sbi, type, 0);curseg->alloc_type = ckpt->alloc_type[type];curseg->next_blkoff = blk_off; // 恢复上次的分配状态mutex_unlock(&curseg->curseg_mutex);f2fs_put_page(new, 1);return 0;
}
F2FS源码分析-1.3 [F2FS 元数据布局部分] Checkpoint结构相关推荐
- F2FS源码分析-1.4 [F2FS 元数据布局部分] Segment Infomation Table-SIT结构
F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node ...
- F2FS源码分析-1.6 [F2FS 元数据布局部分] Segment Summary Area-SSA结构
F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node ...
- F2FS源码分析-1.2 [F2FS 元数据布局部分] Superblock结构
F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node ...
- F2FS源码分析-2.2 [F2FS 读写部分] F2FS的一般文件写流程分析
F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 F2FS文件数据组织方式 一般文件写流程 一般文件读流程 目录文件读流程(未完成) 目录文件写流程(未完成 ...
- F2FS源码分析-2.3 [F2FS 读写部分] F2FS的一般文件读流程分析
F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 F2FS文件数据组织方式 一般文件写流程 一般文件读流程 目录文件读流程(未完成) 目录文件写流程(未完成 ...
- F2FS源码分析-2.1 [F2FS 读写部分] F2FS文件数据组织方式以及物理地址的映射
F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 F2FS文件数据组织方式 一般文件写流程 一般文件读流程 目录文件读流程(未完成) 目录文件写流程(未完成 ...
- F2FS源码分析-6.6 [其他重要数据结构以及函数] F2FS的重命名过程-f2fs_rename函数
F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 三.文件与目录的创建以及删除(未完成) 四.垃圾回收机制 五.数据恢复机制 六.重要数据结构或者函数的分析 ...
- F2FS源码分析-5.2 [数据恢复流程] 后滚恢复和Checkpoint的作用与实现
F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 三.文件与目录的创建以及删除(未完成) 四.垃圾回收机制 五.数据恢复机制 数据恢复的原理以及方式 后滚恢 ...
- F2FS源码分析系列文章目录
一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node Address Table区域( ...
最新文章
- Docker数据卷管理
- Java 集合系列02之 Collection架构
- 树莓派安装 MySQL 时出现错误的解决方法
- 20165208 课下作业
- liblfds 测试
- UiAutomator喷射事件的源代码分析
- java clone 深拷贝_Java clone() 浅拷贝 深拷贝
- 能编写PHP语言吗_语言能力差口才不好,靠看书就能提高吗
- 摄像头网络信号测试软件,工程宝如何测试摄像机
- 常见词根-词缀-需要记忆
- uniapp 动态插槽 slot 兼容微信小程序 h5 APP
- 基于vue3.0的遮罩
- 增加表空间数据文件语句
- 带你走进神经网络的“前世今生”
- C# extention extension
- Excel如何快速合并相同单元格
- 利用python如何抓取微博评论?
- CAS Server
- 不让粘贴的数字在excel中以科学计数法形式显示
- 9.leetcode题目189: Rotate Array