在使用fsync同步数据时,由于hmfs使用了oob恢复,导致很多情况下只需要刷写数据而不需要刷写node。
但是在某些情况下还是需要刷写inode。具体有两处判断,即f2fs_skip_inode_update和need_sync_node。
如果inode是脏的,则在这两处判断中都要刷写下盘。但是后一次会为inode打上fsync标记。那么为什么需要第一次?
flowchart
A[f2fs_do_file_sync]-->B[f2fs_skip_inode_update]-->C[hmfs_write_inode]
A-->D[need_sync_node]-->E[hmfs_fsync_node_pages]-->F[__fsync_inode_xattr_pages]
在现在的f2fs中,只要inode是脏的,就刷写inode。f2fs_skip_inode_update用于判断一些可以跳过刷写inode的特殊情况。
如果没有FI_AUTO_RECOVER标记则不跳过f2fs_inode_info标志位
猜测这个意思是即使块计数和文件大小有误,在恢复时仍可以通过盘上信息恢复出来,也就是没必要在fsync的时候因为这个写inode。
原版f2fs就有。
如果file_keep_isize(inode)为真则不跳过
https://patchwork.kernel.org/project/linux-fsdevel/patch/20161129003125.GA15941@jaegeuk/
通过fallocate设定了稀疏文件的大小,则这个文件的大小无法通过扫描盘得到。因此要将inode刷盘
如果文件大小没有对齐到页边界(i_size_read(inode) & ~PAGE_MASK)则不跳过
如果i_size不是4K的整数倍,说明文件的最后一个页没有写满。那么在恢复的时候,如果inode没有同步,则不能判断文件的准确大小,从而不知道最后一个页中的哪些部分属于文件,哪些部分属于填充。例如,如果页面大小为 4 KB,而文件大小为 9 KB,那么最后一页只有 1 KB 的数据。
那么可以对于append写的数据(或者其他修改最后一个块的时候)刷inode,其他的不刷。因为修改或在中间添加数据不影响最后一个块。
与修改时间、访问时间有关的判断
如果文件在内存中的大小和上一次落盘的大小相等则跳过,否则不跳过(没有追加写)
如果不满足跳过条件,则通过hmfs_write_inode刷写inode并跳到判断是否需要CP处。
也就是说无论是否做CP,都要刷写inode。这不是浪费吗?
小结:如果inode是脏的,则在hmfs_fsync_node_pages中调用__fsync_inode_xattr_pages为inode打上fsync标记,并刷盘。
问题:为什么有hmfs_fsync_node_pages→__fsync_inode_xattr_pages?如果xattr脏了就直接做CP了:need_do_checkpoint→CP_XATTR_DIRTY
__fsync_inode_xattr_pages除了刷写xattr以外,还有别的操作,见下面分析。
f2fs_do_sync_file中获取了两个判断条件:
fi->fsync_dirty_pages
在f2fs_do_sync_file中,首先通过fi->fsync_dirty_pages = get_dirty_pages(inode);获取该文件脏数据页的数量。(f2fs_inode_info:F2FS的inode )如果没有脏数据页,则代表是修改了inode中的属性或做了truncate。
注意:只要有脏数据,就会有脏dnode,但不一定有脏inode。因为dnode直接指向数据的地址,而inode中只要不动直接保存的923个数据块,则修改数据不一定会改动inode,因为node的地址保存在NAT表中。这也就是F2FS避免雪崩式元数据更新的特点。
也就是说,有脏数据的文件不一定inode脏。
dirty_inode
在更新extent cache的时候,如果没有文件数据的改变而只涉及到extent的修改,则将inode置脏并打上FI_ONLY_LARGEST_EXT_CHG标记,表示fsync的时候只刷写inode。
如果该inode是脏的,并且没有FI_ONLY_LARGEST_EXT_CHG标记,则dirty_inode为真。
注意,开启oob恢复后不会有这个标记,因此只要inode是脏的就为真。
这两个判断条件在开启oob恢复后基本上是没有用到。在need_sync_node中: