F2FS源码分析系列文章

主目录
一、文件系统布局以及元数据结构
  1. 总体结构
  2. Superblock区域
  3. Checkpoint区域
  4. Segment Infomation Table区域(SIT)
  5. Node Address Table区域(NAT)
  6. 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_segnocur_node_blkoffcur_data_segnocur_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结构相关推荐

  1. F2FS源码分析-1.4 [F2FS 元数据布局部分] Segment Infomation Table-SIT结构

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node ...

  2. F2FS源码分析-1.6 [F2FS 元数据布局部分] Segment Summary Area-SSA结构

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node ...

  3. F2FS源码分析-1.2 [F2FS 元数据布局部分] Superblock结构

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node ...

  4. F2FS源码分析-2.2 [F2FS 读写部分] F2FS的一般文件写流程分析

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 F2FS文件数据组织方式 一般文件写流程 一般文件读流程 目录文件读流程(未完成) 目录文件写流程(未完成 ...

  5. F2FS源码分析-2.3 [F2FS 读写部分] F2FS的一般文件读流程分析

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 F2FS文件数据组织方式 一般文件写流程 一般文件读流程 目录文件读流程(未完成) 目录文件写流程(未完成 ...

  6. F2FS源码分析-2.1 [F2FS 读写部分] F2FS文件数据组织方式以及物理地址的映射

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 F2FS文件数据组织方式 一般文件写流程 一般文件读流程 目录文件读流程(未完成) 目录文件写流程(未完成 ...

  7. F2FS源码分析-6.6 [其他重要数据结构以及函数] F2FS的重命名过程-f2fs_rename函数

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 三.文件与目录的创建以及删除(未完成) 四.垃圾回收机制 五.数据恢复机制 六.重要数据结构或者函数的分析 ...

  8. F2FS源码分析-5.2 [数据恢复流程] 后滚恢复和Checkpoint的作用与实现

    F2FS源码分析系列文章 主目录 一.文件系统布局以及元数据结构 二.文件数据的存储以及读写 三.文件与目录的创建以及删除(未完成) 四.垃圾回收机制 五.数据恢复机制 数据恢复的原理以及方式 后滚恢 ...

  9. F2FS源码分析系列文章目录

    一.文件系统布局以及元数据结构 总体结构 Superblock区域 Checkpoint区域 Segment Infomation Table区域(SIT) Node Address Table区域( ...

最新文章

  1. Docker数据卷管理
  2. Java 集合系列02之 Collection架构
  3. 树莓派安装 MySQL 时出现错误的解决方法
  4. 20165208 课下作业
  5. liblfds 测试
  6. UiAutomator喷射事件的源代码分析
  7. java clone 深拷贝_Java clone() 浅拷贝 深拷贝
  8. 能编写PHP语言吗_语言能力差口才不好,靠看书就能提高吗
  9. 摄像头网络信号测试软件,工程宝如何测试摄像机
  10. 常见词根-词缀-需要记忆
  11. uniapp 动态插槽 slot 兼容微信小程序 h5 APP
  12. 基于vue3.0的遮罩
  13. 增加表空间数据文件语句
  14. 带你走进神经网络的“前世今生”
  15. C# extention extension
  16. Excel如何快速合并相同单元格
  17. 利用python如何抓取微博评论?
  18. CAS Server
  19. 不让粘贴的数字在excel中以科学计数法形式显示
  20. 9.leetcode题目189: Rotate Array

热门文章

  1. 交易时间与开盘价确定
  2. JAVA网站视频播放不了_java视频播放网站小结
  3. html中ol和li,HTML ol和li标签
  4. tcplayer 源码改造第三弹 - 防盗录
  5. MBA心路历程第一天 —— 开始行动
  6. 关于DOTS的个人总结
  7. 一篇文章学会日志logback的使用
  8. matlab和vc,Vc++和matlab双剑合璧
  9. Let's go home 【2-sat 经典作图】+【scc判定有无解】
  10. tkinter可视化天气查询