主旨

在使用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_skip_inode_update判断

在现在的f2fs中,只要inode是脏的,就刷写inode。f2fs_skip_inode_update用于判断一些可以跳过刷写inode的特殊情况。

  1. 如果是fdatasync,则检查&F2FS_I(inode)->gdirty_list(f2fs_inode_info:F2FS的inode )。如果该inode不在全局脏链表中则跳过,否则不跳过。
  2. 如果不是fdatasync
    1. 如果没有FI_AUTO_RECOVER标记则不跳过f2fs_inode_info标志位

      猜测这个意思是即使块计数和文件大小有误,在恢复时仍可以通过盘上信息恢复出来,也就是没必要在fsync的时候因为这个写inode。

      原版f2fs就有。

    2. 如果file_keep_isize(inode)为真则不跳过

      https://patchwork.kernel.org/project/linux-fsdevel/patch/20161129003125.GA15941@jaegeuk/

      通过fallocate设定了稀疏文件的大小,则这个文件的大小无法通过扫描盘得到。因此要将inode刷盘

    3. 如果文件大小没有对齐到页边界(i_size_read(inode) & ~PAGE_MASK)则不跳过

      如果i_size不是4K的整数倍,说明文件的最后一个页没有写满。那么在恢复的时候,如果inode没有同步,则不能判断文件的准确大小,从而不知道最后一个页中的哪些部分属于文件,哪些部分属于填充。例如,如果页面大小为 4 KB,而文件大小为 9 KB,那么最后一页只有 1 KB 的数据。

      那么可以对于append写的数据(或者其他修改最后一个块的时候)刷inode,其他的不刷。因为修改或在中间添加数据不影响最后一个块。

    4. 与修改时间、访问时间有关的判断

    5. 如果文件在内存中的大小和上一次落盘的大小相等则跳过,否则不跳过(没有追加写)

如果不满足跳过条件,则通过hmfs_write_inode刷写inode并跳到判断是否需要CP处。

也就是说无论是否做CP,都要刷写inode。这不是浪费吗?

need_sync_node判断

小结:如果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中获取了两个判断条件:

  1. 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脏。

  2. dirty_inode

    在更新extent cache的时候,如果没有文件数据的改变而只涉及到extent的修改,则将inode置脏并打上FI_ONLY_LARGEST_EXT_CHG标记,表示fsync的时候只刷写inode。

    如果该inode是脏的,并且没有FI_ONLY_LARGEST_EXT_CHG标记,则dirty_inode为真。

    注意,开启oob恢复后不会有这个标记,因此只要inode是脏的就为真。

    f2fs_inode_info标志位

这两个判断条件在开启oob恢复后基本上是没有用到。在need_sync_node中: