Zoned Storage-Linux Kernel Support之文件系统
文件系统
dm-zoned device-mapper 目标可以使用任何带有主机管理的分区块设备的文件系统。 它通过隐藏设备的顺序写入约束来做到这一点。 这个解决方案很简单,并且可以使用文件系统,但它在基于块的区域回收过程中潜在的高开销意味着这不是最有效的解决方案。
其实现直接支持分区块设备的文件系统具有更有效的zone-reclamation区域回收处理。 这是因为直接支持分区块设备的文件系统具有元数据和文件抽象,与采用基于 dm-zoned-block 的方法的文件系统相比,它们提供了有关存储块的使用和有效性的更多信息。
一些文件系统的设计方式使其能够很好地适应主机管理的分区块设备的顺序写入约束。 日志结构文件系统(例如 f2fs)和写时复制 (CoW) 文件系统(例如 Btrfs)就是这种情况。
zonefs
zonefs 是一个非常简单的文件系统,它将zoned块设备的每个zone公开为一个file。 zonefs 自 5.6.0 版以来已包含在Linux 内核中。
zonefs 不会向用户隐藏zoned块设备的顺序写入约束。 在这方面,它不同于具有本地zoned块设备支持(例如 f2fs)的常规 POSIX 兼容文件系统。 表示设备上顺序写入区域的文件必须从文件末尾开始顺序写入(这些是“append only”写入)。
因此 zonefs 更类似于原始块设备访问接口,而不是功能齐全的 POSIX 文件系统。 zonefs 的目标是简化应用程序中zoned块设备支持的实现,它旨在通过用更丰富的常规文件 API 替换原始块设备文件访问来做到这一点(避免依赖可能更晦涩和对开发人员不友好的 直接块设备文件 ioctls)。 这种方法的一个例子是在zoned块设备上实现 LSM(日志结构化合并)树结构(例如在 RocksDB 和 LevelDB 中使用):SSTables 以类似于常规的方式存储在一个zone文件中 , 文件系统不是作为整个磁盘的一些扇区工作的。 引入更高级别的构造“one file is one zone”可以减少应用程序所需的更改次数,并且还引入了对不同应用程序编程语言的支持。
代表区域的文件按区域类型分组,这些区域类型本身由子目录表示。 此文件结构完全使用设备提供的zone信息构建,因此不需要任何复杂的磁盘元数据结构。
磁盘元数据
zonefs 磁盘元数据仅由一个immutable不可变的超级块组成,该块永久存储一个magic数和可选的功能标志和值。 在挂载时,zonefs 使用块层 API 函数 blkdev_report_zones()
来获取设备区域配置,并使用仅基于此信息的静态文件树填充挂载点。 文件大小来自设备zone类型和写指针位置,两者都由设备本身管理。 zonefs 仅根据来自设备的信息运行。 zonefs 没有任何自己的元数据。
超级块总是写入磁盘的第 0 扇区。存储超级块的设备的第一个zone永远不会被 zonefs 公开为 zone file。 如果包含超级块的区域是顺序区域,则 mkzonefs
格式工具始终“FINISH”该区域(即,它将区域转换为完整状态以使其只读,防止任何数据写入)。
区域类型子目录
代表相同类型区域的文件被组合在同一个子目录下,该子目录在挂载时自动创建。
对于常规区域,使用子目录“cnv”。 仅当设备具有可用的常规区域时才会创建此目录。 如果设备在扇区 0 只有一个常规区域,则该区域不会作为文件公开(因为它将用于存储 zonefs 超级块)。 对于此类设备,不会创建“cnv”子目录。
对于顺序写入区域,使用子目录“seq”。
这两个目录是 zonefs 中唯一存在的目录。 用户不能创建其他目录,也不能重命名或删除“cnv”和“seq”子目录。
目录的大小表示目录下存在的文件数。 此大小由 struct stat
的 st_size
字段指示,该字段是通过 stat()
或 fstat()
系统调用获得的。
区域文件
区域文件使用它们在特定类型的区域集中表示的区域编号来命名。 “cnv”和“seq”目录都包含名为“0”、“1”、“2”、…的文件。文件编号也代表设备上增加的区域起始扇区。
不允许对区域文件进行超出文件最大大小(即超出区域大小)的读写操作。 任何超过区域大小的访问都会失败并出现 -EFBIG 错误。
不允许创建、删除、重命名和修改文件的任何属性。
stat()
和 fstat()
报告的文件块数表示文件zone的大小(即最大文件大小)。
-
常规区域文件
常规区域文件的大小固定为它们所代表的区域的大小。 不能截断常规区域文件。可以使用任何类型的 I/O 操作随机读取和写入这些文件:缓冲 I/O、直接 I/O、内存映射 I/O (mmap) 等。这些文件除了 上面提到的文件大小限制。
-
顺序区域文件
分组在“seq”子目录中的顺序区域文件的大小表示文件的区域写入指针相对于区域起始扇区的位置。顺序区域文件只能顺序写入,从文件末尾开始(即写入操作只能是“追加写入”)。 zonefs 不会尝试接受随机写入,并且将失败任何具有与文件末尾不对应的开始偏移量的写入请求,或者与发出的最后一次写入的末尾不对应并且仍在进行中(对于异步 I/O 操作)。
因为页面缓存的脏页写回不能保证顺序写入模式,所以 zonefs 会阻止缓冲写入和顺序文件上的可写共享映射。 这些文件只接受直接 I/O 写入。 zonefs 依赖于将写入 I/O 请求顺序传递到由块层电梯实现的设备(请参阅写入命令排序)。
对于顺序区域文件中用于读取操作的 I/O 类型没有限制。 缓冲 I/O、直接 I/O 和共享读取映射都被接受。
仅允许截断顺序区域文件到 0,在这种情况下,区域被重置以将文件区域写入指针位置倒回到区域的开头,或者直到区域大小,在这种情况下,文件的区域将转换为 FULL 状态(完成区域操作)。
格式选项
zonefs 的几个可选功能可以在格式化时启用。
- 常规区域聚合:连续的常规区域范围可以聚合到一个更大的文件中,而不是默认的“每个区域一个文件”。
- 文件所有者:默认情况下,区域文件的所有者 UID 和 GID 为 0(根),但可以更改为任何有效的 UID/GID。
- 文件访问权限:可以更改默认访问权限(640)。
IO错误处理
分区块设备可能会导致 I/O 请求失败,原因类似于常规块设备失败 I/O 请求的原因,例如 如果有坏扇区。 但管理分区块设备行为的标准还定义了可能导致 I/O 错误的其他条件(除了这些已知的 I/O 故障模式)。
- 一个区域可能会转变为只读状态:虽然已经写入该区域的数据仍然可读,但该区域不能再被写入。 对区域的任何用户操作(区域管理命令或读/写访问)都不能将区域条件更改回正常的读/写状态。 虽然标准没有定义设备将区域转换为只读状态的原因,但这种转换的典型原因是 HDD 上的写入磁头有缺陷(此磁头下的所有区域都更改为只读)
- 一个区域可能会转变为离线状态:一个离线区域既不能被读取也不能被写入。 任何用户操作都不能将脱机区域转换回可操作的“良好状态”。 与区域只读转换类似,驱动器将区域转换为离线状态的原因是不确定的。 一个典型的原因是(例如)HDD 上有缺陷的读写头,导致损坏的磁头下方的盘片上的所有区域都无法访问。
- 未对齐的写入错误:这些错误是由于设备接收到的写入请求的起始扇区与目标区域的写入指针位置不对应。 尽管 zonefs 对顺序区域强制执行顺序文件写入,但在拆分为多个 BIO/请求或异步 I/O 操作的非常大的直接 I/O 操作部分失败的情况下,仍然可能发生未对齐的写入错误。 如果向设备发出的一组顺序写入请求中的一个写入请求失败,则在它之后排队的所有写入请求将变得未对齐并失败。
- 延迟写入错误:与常规块设备一样,如果启用了设备端写入缓存,则在刷新设备写入缓存时,可能会在先前完成的写入范围内发生写入错误,例如 在 fsync() 上。 与立即未对齐写入错误的情况一样,延迟写入错误可以通过区域的缓存顺序数据流传播,这可能导致导致错误的扇区之后的所有数据被丢弃。
zonefs 检测到的所有 I/O 错误都会报告给用户,并为触发或检测到错误的系统调用返回一个错误代码。 zonefs 为响应 I/O 错误而采取的恢复操作取决于 I/O 类型(读取与写入)和错误原因(坏扇区、未对齐写入或区域条件更改)。
- 对于读取 I/O 错误,仅当文件区域仍处于良好状态并且文件 inode 大小与其区域写入指针位置之间没有不一致时,zonefs 才会采取恢复操作。 如果检测到问题,则执行 I/O 错误恢复(见下表)。
- 对于写 I/O 错误,总是执行 zonefs I/O 错误恢复。
- 区域条件更改为“只读”或“离线”也总是会触发 zonefs I/O 错误恢复。
zonefs 最小 I/O 错误恢复可以更改文件的大小及其文件访问权限。
- 文件大小更改:顺序区域文件中的即时或延迟写入错误会导致文件 inode 大小与成功写入文件区域的数据量不一致。 例如,multi-BIO 大写操作的部分失败将导致区域写指针部分前进,即使整个写操作向用户报告为失败。 在这种情况下,文件 inode 大小必须提前以反映区域写入指针的变化,并最终允许用户在文件末尾重新开始写入。 文件大小也可以减小以反映在 fsync() 上检测到的延迟写入错误:在这种情况下,有效写入区域的数据量可能小于文件 inode 大小最初指示的数据量。 在任何此类 I/O 错误之后,zonefs 总是修复文件 inode 大小以反映持久存储在文件区域中的数据量。
- 访问权限更改:文件访问权限的更改指示区域条件更改为只读,从而使文件变为只读。 这将禁用对文件属性的更改和数据修改。 对于离线区域,文件的所有权限(读取和写入)都被禁用。
用户可以使用“errors&