Linux v3.8-rc1
super.c
f2fs_alloc_inode // 从缓存中申请内存后,初始化具体的inode信息并设定标志,返回vfs_inode
1 | /* struct inode 和 struct super_block 在/include/linux/fs.h中*/ |
f2fs_i_callback // 进程结束后把进程信息存放到缓存中
调用container_of
(macro在include/linux/kernel.h中)使指针head指向inode结构体的成员变量i_rcu, 把整个inode结构体的指针都存放在struct inode *inode
,再调用F2FS_I
函数通过inode指针指向f2fs_inode_info中的成员vfs_inode,通过inode指针返回结构体f2fs_inode_info的起始地址。然后调用kmem_cache_free
函数把前面返回的结构体地址保存到f2fs_inode_cachep。
f2fs_destroy_inode // 销毁旧的inode指针
f2fs_put_super // 释放内存
1 | static void f2fs_put_super(struct super_block *sb) |
f2fs_statfs // 获取f2fs使用情况
如block大小和数目。可使用的有效block数目,有效的inode数目,有效的node数目
f2fs_show_options // 属性显示
如有操作,如“后台清理”、“关闭前滚”、“无堆分配”等情况出现,向seq流中写入相应的字符串如”,background_gc_on”
parse_options // 解析属性,与f2fs_show_options关联,认为两者与项目无关
max_file_size // 通过索引节点中的地址指针,直接块中的地址指针,间接块中的节点ID计算最大文件大小
sanity_check_raw_super // 原始super的健全性检查
1 | static int sanity_check_raw_super(struct f2fs_super_block *raw_super) |
sanity_check_ckpt // checkpoint的健全性检查
如果ckpt, sit, nat, ssa各自的segment数之和加上gc的保留段数 小于等于 segment总数,则通过健全性检查
init_sb_info // 对f2fs_sb_info进行初始化
f2fs_fill_super // 读取磁盘的前端区域的数据,对F2FS元区域数据进行初始化
f2fs_fill_super
是加载F2FS文件系统的第一步,主要作用是读取磁盘的前端区域的数据,对F2FS元区域数据进行初始化。
1 首先调用
kzalloc
为特定f2fs的超级块信息分配内存
2 设定一个临时的块大小
3 调用sb_bread
读取原始超级块的信息
4 调用set_opt
初始化FS参数
5 调用parse_options
解析安装属性
6 对原始super进行健全性检查
7 初始化特定的超级块信息,如用于gc, cp, write_inode, writepages的互斥量
8 调用f2fs_iget
获取元空间的索引节点
9 对cp做健全性检查
10 调用init_orphan_info
初始化超级块orphan信息
11 依次调用build_segment_manager
,build_node_manager
,build_gc_manager
设置f2fs内部模块
12 调用recover_orphan_inodes
恢复孤立节点,有则释放它们
13 调用f2fs_iget
获取根索引节点和目录项
14 调用recover_fsync_data
恢复(与数据的同步写入磁盘相关)
15 运行后台GC线程
这个初始化函数中与后滚恢复相关的函数有get_valid_checkpoint
和build_segment_manager
,分别对应f2fs_checkpoint
相关的数据和curseg相关的数据
get_valid_checkpoint // 恢复f2fs_checkpoint
定义在fs/f2fs/checkpoint.c中
1 | // struct f2fs_sb_info在/fs/f2fs/f2fs.h中 |
build_segment_manager // 恢复curseg
恢复curseg的功能主要在build_segment_manager
函数的build_curseg
函数中完成
build_segment_manager和build_curseg定义在fs/f2fs/segment.c
1 | static int build_curseg(struct f2fs_sb_info *sbi) |
restore_curseg_summaries // 读取curseg
function in /fs/f2fs/segment.c
1 | static int restore_curseg_summaries(struct f2fs_sb_info *sbi) |
read_normal_summaries //读取summaries
这个函数对于F2FS的正常关闭,重新启动时读取的summary的方式都是类似的,都是根据HOT/WARM/COLD的顺序,读取对应的block,然后将数据保存到curseg对应的类型当中。这里重点考虑出现了宕机的情况的恢复。
checkpoint.c
grab_meta_page 和 get_meta_page// 获取元页面
不同之处在于,grab_meta_page
在返回给定缓存中给定索引处的锁定页面后,调用wait_on_page_writeback
等待页面完成回写再SetPageUptodate
更新页面,然后返回page
而get_meta_page
,在返回给定缓存中给定索引处的锁定页面后,调用mark_page_accessed
将页面标记为可访问,然后返回page
f2fs_write_meta_page f2fs_write_meta_pages sync_meta_pages与写页面相关
f2fs_set_meta_page_dirty // 设置元页面为脏
1 | static int f2fs_set_meta_page_dirty(struct page *page) |
check_orphan_space // 检查孤立空间大小
1 | int check_orphan_space(struct f2fs_sb_info *sbi) |
add_orphan_inode
remove_orphan_inode
recover_orphan_inode // 恢复孤立索引节点
1 | static void recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) |
recover_orphan_inodes
1 | int recover_orphan_inodes(struct f2fs_sb_info *sbi) |
get_valid_checkpoint // 获取有效的checkpoint
在f2fs_fill_super
中已介绍
do_checkpoint
首先刷新所有的NAT/SIT页面,然后根据curseg修改checkpoint的信息和summary的信息。
修改checkpoint:对f2fs_checkpoint
的修改主要是把curseg的当前segno,blkoff等写入到f2fs_checkpoint
中,以便下次重启时可以根据这些信息,重建curseg。
summary的回写:根据需要回写的summary的数目,返回需要写回的block的数目data_sum_blocks
,如果data_sum_blocks = 1 或者 2,则表示回写1个或者2个block,则设置CP_COMPACT_SUM_FLAG标志。
1 | data_sum_blocks = npages_for_summary_flush(sbi); |
然后调用write_data_summaries将summary写入磁盘。write_data_summaries(sbi, start_blk);//将data summary以及里面的journal写入磁盘
write_data_summaries
函数会判断一下是否设置了CP_COMPACT_SUM_FLAG标志,采取不同的方法写入磁盘。
write_checkpoint // 写checkpoint
1 | void write_checkpoint(struct f2fs_sb_info *sbi, bool blocked, bool is_umount) |
recovery.c
space_for_roll_forward // 是否可以前滚回复
1 | bool space_for_roll_forward(struct f2fs_sb_info *sbi) |
get_fsync_inode // 获取fsync索引节点
1 | // 返回一个entry(获取fsync索引节点) |
recover_dentry // 恢复目录项
1 | static int recover_dentry(struct page *ipage, struct inode *inode) |
recover_inode // 恢复inode
1 | static int recover_inode(struct inode *inode, struct page *node_page) |
find_fsync_dnodes // 找到所有的可以恢复的dnode对应的inode
1 | static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) |
destroy_fsync_dnodes // 销毁fsync dnodes
check_index_in_prev_nodes // 检查前向节点的索引
do_recover_data // 恢复data page和node page
首先调用
start_bidx_of_node
函数,把当前node page的起始块索引赋给startset_new_dnode
建立一个vfs dnode,get_dnode_of_data
初始化刚刚建立的dnode相关信息。wait_on_page_writeback
等待页面回写get_node_info
从node page获取节点信息datablock_addr
获取文件名和索引check_index_in_prev_nodes
检查具有以上获取的索引的前向节点
通过调用recover_data_page
和update_extent_cache
写入虚拟数据页
调用recover_node_page
恢复node page
recover_data // 恢复数据
1 | static void recover_data(struct f2fs_sb_info *sbi, |
recover_fsync_data // 恢复fsync数据
①首先通过find_fsync_dnodes
函数找到所有的可以恢复的dnode对应的inode(有可能dnode就是inode本身),放入到一个list中。
find_fsync_dnodes
的执行流程:通过调用CURSEG_I
和START_BLOCK
函数获取当前段(segment)的node pages,再调用alloc_page读取node pages,把可恢复的dnode对应的inode添加到list中,再检查下个段(segment)。
②恢复数据:恢复inode list里面的所有的node page