关注了就能看到更多这么棒的文章哦~
Atomic extent swapping for XFS
By Jonathan Corbet
May 1, 2020
原文来自:https://lwn.net/Articles/818977/
主译:DeepL
通常来说人们使用文件系统中的一个个文件来把各种数据分隔开。如果我们看到一个文件里面有了本该属于另一个文件的数据,那通常意味着文件系统损坏了。然而,在有些情况下,我们希望能够在两个文件之间按我们的计划来进行数据交换。Darrick Wong最近发布了一个patch set,在XFS文件系统中实现了这一功能,同时这种实现方式也很通用。
其实,XFS的数据交换功能已经存在一段时间了。一直没有详细记入文档的XFS_IOC_SWAPEXT ioctl()命令就会交换两个文件中的extend(范围)数据。这个功能只在一个场景下有用:文件系统碎片化。xfs_fsr utility程序会扫描文件系统来找出那些最碎片化的文件(most fragmented files,也就是被分割成最多extent的文件)。然后,它会分配一个足够大的extent来创建一个新文件,然后把一个过于碎片化的文件中的数据复制过来。最后调用XFS_IOC_SWAPEXT,将旧文件的数据块以原子操作的方式替换成新的无碎片的版本。
不过,似乎还有其他用户也在关注这个接口。应用程序开发者们希望有一种方法,以atomic并且safe的方式来替换文件中的部分或全部内容,这样哪怕系统中途宕机,也可以不让文件损坏。目前,可以通过这种方式来实现:先创建一个临时文件,写好内容,最后把它改名成我们原定的文件名。这种方法很有效,但牵涉到多个步骤,很难实现得完全正确。
如果能有一个完美实现好的 "swap range "操作,那就可以通过下面步骤来实现用原子操作替换文件的一部分内容:
在要修改的文件系统中创建一个新文件,要使用O_TMPFILE参数。
将新数据填入临时文件中。如果只需要改变文件的一部分内容,应用程序可以先用FICLONE ioctl()将旧的数据通过 "copy"进去,然后只需要写入要修改的数据。
执行swap操作,将新数据移入旧文件中。
关闭临时文件,然后内核将删除该文件。
同样会使用这个文件的其他进程,将会只看到旧数据或新数据,永远不会看到半新半旧的数据文件。
Wong的patch set在virtual filesystem层中增加了一个新的FISWAPRANGE ioctl()命令,这就意味着任何文件系统都可以实现这个API(虽然目前只有XFS做了)。应用程序需要填写一个数据结构来描述后面要执行的操作:
struct file_swap_range {
__s64 file1_fd;
__s64 file1_offset;
__s64 file2_offset;
__s64 length;
__u64 flags;
__s64 file2_ino;
__s64 file2_mtime;
__s64 file2_ctime;
__s32 file2_mtime_nsec;
__s32 file2_ctime_nsec;
};
file1_fd来是一个文件描述符,指定了一个文件;另一个文件直接通过ioctl()调用的file-descriptor参数来传递进来。这个调用的通常行为是将第一个文件中从file1_offset开始的一段数据与第二个文件中从file2_offset开始的长度相同的一段数据进行交换。要移动的数据范围不需要与文件中已有的各段extent对应,但偏移量必须与文件系统的块大小对齐,大多数情况下,长度必须是块大小的倍数。有趣的是,其实两个文件可以是指向同一个文件。在这种情况下,两个范围不能重叠,然后可以互相交换位置。
这个操作是个原子操作,这意味着在crash的情况下,文件也不会损坏。
多种flags参数可以改变这个操作的具体行为。patch set中定义的标志有:
FILE_SWAP_RANGE_NONATOMIC:应用程序并不要求这个操作是原子操作,这样就可以较快完成。
FILE_SWAP_RANGE_FILE2_FRESH:在执行swap操作之前,内核会验证第二个文件的inode号是否与file2_ino匹配,以及它的修改时间是否与file2_mtime、file2_mtime_nsec、file2_ctime和file_ctime_nsec匹配。在没有完全匹配的情况下,调用将返回EBUSY错误。这是一种确保在准备swap过程中没有人修改过文件的方法。
FILE_SWAP_RANGE_FULL_FILES:确保swap操作会对两个文件中的所有数据进行。换句话说,要求两个偏移量都为零,并且两个文件的长度相同(必须与length字段相匹配)。
FILE_SWAP_RANGE_TO_EOF:数据从指定的偏移量到两个文件的两端进行swap。在这种情况下,长度字段会被忽略,两个要swap的范围的大小可能不同。
XFS在实际执行操作之前,会在transaction log事务日志中记录这个操作。这在碰到crash时可以确保健壮性。但这样一来也会使得带有此功能的文件系统与早期版本的XFS代码不兼容。
在内部实现时,该patch set为不断增长的file_operations结构又增加了另一个成员:
int (*swap_file_range)(struct file *file_in, struct file *file_out,
struct file_swap_range *fsr);
其他文件系统只要实现这个函数指针,就可以支持FISWAPRANGE ioctl()。
这个patch set刚出来不久,review过程还没有开始。因此,很有可能在今后合入mainline的时候它应该会变化很大。不过,这似乎确实是一个有用的功能,所以很可能在今后什么时候能正式合并。
全文完
LWN文章遵循CC BY-SA 4.0许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注LWN深度文章以及开源社区的各种新近言论~