3. 移植你的文件系统¶
3.1. 为什么要转换?¶
将文件系统转换为 iomap 有几个原因。
传统的 Linux I/O 路径效率不是很高。Pagecache 操作一次锁定一个基本页,然后调用文件系统以仅返回该页的映射。直接 I/O 操作一次构建一个文件块的 I/O 请求。这对于诸如 ext2 之类的直接/间接映射的文件系统来说效果还不错,但是对于诸如 XFS 之类的基于区段的文件系统来说效率非常低。
只有通过 iomap 才支持大 folio;没有计划将旧的 buffer_head 路径转换为使用它们。
只有通过 iomap 才支持对内存类设备(fsdax)上的存储进行直接访问。
降低了各个文件系统维护者的维护开销。iomap 本身会处理常见的 pagecache 相关操作,例如分配、实例化、锁定和解锁 folio。使用 iomap 的文件系统不需要实现 ->write_begin(), ->write_end() 或 direct_IO address_space_operations。
3.2. 如何转换文件系统?¶
首先,从你的源代码中添加 #include <linux/iomap.h>
,并将 select FS_IOMAP
添加到你的文件系统的 Kconfig 选项中。构建内核,使用 -g all
选项在你的文件系统支持的各种配置上运行 fstests,以建立哪些测试通过和哪些测试失败的基线。
推荐的方法是首先实现 ->iomap_begin
(如果需要,还可以实现 ->iomap_end
),以允许 iomap 获取文件范围的只读映射。在大多数情况下,这是对现有 get_block()
函数进行只读映射的相对简单的转换。FS_IOC_FIEMAP
是一个很好的首要目标,因为实现对它的支持很简单,然后可以从用户空间确定区段映射迭代是否正确。如果 FIEMAP 返回了正确的信息,则这是一个好兆头,表明其他只读映射操作将执行正确的操作。
接下来,修改文件系统的 get_block(create = false)
实现,以使用新的 ->iomap_begin
实现来映射所选读取操作的文件空间。隐藏在调试旋钮后面,可以为所选的调用路径打开 iomap 映射函数。有必要编写一些代码以从 iomap
结构中填充基于 bufferhead 的映射信息,但是可以在不需要实现任何 iomap API 的情况下测试新函数。
一旦只读功能像这样工作,就将每个高级文件操作逐个转换为使用 iomap 原生 API,而不是通过 get_block()
。一次完成一个,回归应该是显而易见的。你确实 有 fstests 的回归测试基线,对吗?建议在处理 I/O 路径之前转换交换文件激活,SEEK_DATA
和 SEEK_HOLE
。此时可能出现的复杂性将是由于 bufferhead 而转换缓冲读取 I/O 路径。缓冲读取 I/O 路径尚不需要转换,但在此阶段应转换直接 I/O 读取路径。
此时,你应该查看你的 ->iomap_begin
函数。如果它根据 flags
参数的调度在大量代码之间切换,则应考虑将其分解为每个操作的 iomap ops,这些 ops 具有更小,更具凝聚力的函数。XFS 是一个很好的例子。
接下来要做的就是在 ->iomap_begin
/->iomap_end
方法中实现 get_blocks(create == true)
功能。强烈建议为写入操作创建单独的映射函数和 iomap ops。然后将直接 I/O 写入路径转换为 iomap,并开始认真地在文件系统上启用 DIO 的情况下运行 fsx。这将清除新的写入映射实现引入的许多数据完整性边缘情况错误。
现在,转换所有剩余的文件操作以调用 iomap 函数。这将使整个文件系统使用新的映射函数,并且在此步骤之后,它们应该在很大程度上被调试并正常工作。
此时,很可能仍需要转换缓冲的读取和写入路径。映射函数应该都能正常工作,因此需要做的就是重写所有与 bufferhead 接口的代码,使其与 iomap 和 folio 接口。首先将常规文件 I/O(没有任何花哨的功能,如 fscrypt、fsverity、压缩或 data=journaling)转换为使用 iomap 会更容易。iomap 中尚未实现某些花哨的功能(fscrypt 和压缩)。对于将 pagecache 用于符号链接和目录的未日志文件系统,你也可以尝试将其处理转换为 iomap。
其余的留给读者练习,因为它对于每个文件系统都将不同。如果遇到问题,请通过 get_maintainers.pl
中的电子邮件联系人员和列表以寻求帮助。