RFC-0005:Blobfs 快照

RFC-0005:Blobfs 快照
状态已拒绝
领域
  • 存储
说明

升级期间支持 Blobfs 快照。

Gerrit 更改
作者
审核人
提交日期(年-月-日)2020-09-06
审核日期(年-月-日)2020-09-21

摘要

此 RFC 介绍了一种可提高弹性的简单快照机制 在升级过程中发现错误对 Fuchsia Volume Manager (FVM) 的更改、 允许截取 Blobfs 分区的快照,该快照可在 升级过程中的任何阶段

设计初衷

在写入时,导致 Blobfs 损坏的升级失败 分区可能会使设备处于难以恢复的状态。通过 recovery 分区目前无法恢复处于此状态的设备, 在这些情况下,唯一支持的恢复方法是使用 对最终用户来说不友好的流程

快照机制将降低我们最终处于此状态的风险。

设计

基本概念是在 FVM 中支持原始快照机制, 允许在升级期间显示两个分区, 允许在分区之间共享数据。

目前,FVM 是一个简单的卷管理器,能够映射切片 从任意切片对齐的逻辑偏移量到 底层设备的映射,并使来自不同分区的映射保持分离。

Blobfs 由以下不同区域组成:

区域
超级区块
分配位图
Inode
日志
数据

为了支持此处的提案,我们允许 FVM1。这些类型适用于 Slice 的范围

类型 说明
A/B 切片 这将是具有备用副本的 Slice 范围。
A/B 位图2 这可能是具有位图的备用副本(表示共享数据范围中的分配)的 Slice。
分享的数据 这将是切片的范围,其分配由 A/B 位图范围管理。
已共享 这将在两个分区之间共享,但一次只能有一个分区向该区域写入数据。

利用这些切片类型,FVM 有可能向 FVM 显示两个 分区的 A/B 变化。返回 Blobfs 区域,则结果为:

区域 类型
超级区块 A/B 切片
分配位图 A/B 位图
Inode A/B 切片
日志 已分享3
数据 分享的数据

大多数情况下,只有一个分区处于活跃状态,系统会 呈现的样子。

升级期间,可以激活第二个分区,此时第一个分区 分区变为锁定状态,且不允许进一步写入,但读取 将会继续被投放第二个分区可以准备,有可能 其优化方式与现在相同,但在整个升级期内, 始终提供返回第一个分区的选项 不受影响

对于 A/B 范围,很容易看出第一个分区的数据 preserved;第二个分区就无法看到第一个分区的数据。对于 Journal,共享区域 - 只有可写分区才能写入 即第二个分区。对于共享数据区域,位图将 指示可以写入哪些块任何标记为“已使用”的块 第一个分区将对这两个分区都是只读的。

为了便于实施此方案,第二个分区还需要能够 读取备用位图,以便它知道允许哪些块 分配,因此为了实现这一目的,可以在逻辑地址中 添加一些额外的空间“草人求婚”是指 备选 A/B 范围将以相同的偏移量显示,但设置了顶部位 (只读)。

下图说明了每个分区 显示:

分区排列

图 1:分区排列。

注意:

  • 它能让我们获得通过简单的 A/B 更新所能获得的弹性 但并非全部。
  • 我们可以保留当前的增量更新方法(即仅更新 但其代价是不能生成可预测的、 布局。在 user build 中,我们可以选择完全重写所有 但碎片化仍然任重而道远。
  • 这会增加 FVM 的复杂性。

新的升级流程

必须修改升级流程,以促进快照互动。 当前流程如图 2所示,建议的替代方案 图 3.新的 API 和交互会带有颜色。

当前 OTA

图 2:当前升级实现(概要)

建议的 OTA

图 3:建议的升级实现(高级)

新的 FVM 操作

必须在软件中实现并集成若干新的 FVM 操作 投放 (SWD) 堆栈。这些 API 用于驱动状态机(图 4),这最终会在分区之间切换系统。

快照状态机

图 4:用于截取快照的状态机。

TakeSnapshot

将活跃分区的元数据截取到备用分区, (请参阅“DeleteSnapshot”)。活跃分区会变为 只读分区,所有后续写入操作现在必须转到非活动分区。

  • FVM 将活跃分区设为只读。
    • 必须清除待处理日志条目。
  • FVM 会创建非活跃分区。
  • FVM 会从活跃 - > 非活跃分区复制元数据。

在此多步骤过程中写入新的 blob 并且必须放弃一半写入的 blob, 限制,因为负责写入 blob 的组件应该是 即负责请求快照的组件

CancelSnapshot

取消对 TakeSnapshot 创建的快照的填充,清除 非活跃分区并允许创建另一个快照。

  • 此时,必须关闭针对非活跃分区的所有读取连接。
  • FVM 将删除非活跃分区。活跃分区将 从而再次变为可写入状态

SetWritablePartition

用于切换可写入的分区。

  • 此时必须刷新日志(所有待处理的操作必须 完成)。上图中的 fsync 调用有助于实现这一目的, 理想情况下,日志刷新操作应以事务方式完成 这样任何新写入操作都不会“偷入”。

您可能很少使用该选项,因为 TakeSnapshot 会自动切换 但如果需要返回并使 可写分区(例如,为了对未使用的 blob 进行垃圾回收),则 此 API 均可使用。

SetBootPartition

更改了哪个分区可启动。

正常情况下,可启动的分区会发生变化,具体取决于 但也可以单独切换 可启动。此设置可能很少使用。

DeleteSnapshot

将备用分区标记为已清除。FVM 可能会选择 元数据。

ListSnapshotPartitions

针对配置为截取快照的分区查询 FVM。

QuerySnapshotPartition

查询 FVM 以获取有关支持哪些分区的 快照。

  • 标识 A/B 分区的状态,例如处于活动状态。

故障模式

系统可能会在状态中描述的任何状态 虚拟机。本部分介绍了在发生以下情况时应采取的相应操作: 遇到故障。

请注意,失败可能是自愿的(在这种情况下,系统会主动决定取消 非自愿的(如果系统因外部 等因素)。必须同时考虑这两种情况。

请注意,blobfs 具有防止元数据的日记机制 在修改过程中出现非自愿失败时会导致损坏。没有其他选项 使 blobfs 能够对非自愿故障保持稳健性, 修改。

FVM 中的任何新元数据操作都应设为事务性 以防 FVM 因非自愿故障而损坏 修改过程中会发生的情况

状态 1:拍摄快照之前

在此状态下,无需进行故障处理;行为是 与当前系统的行为相同。

状态 2:TakeSnapshot 之后、重新启动之前

  • 对于自愿失败,可以调用 CancelSnapshot API 来删除 非活动分区,使系统返回到状态 1。
  • 对于非自愿故障,系统可以决定直接中止 在其恢复在线状态(通过调用 CancelSnapshot)进行更新,或者系统 可以选择尝试恢复更新。

状态 3:重新启动后、TakeSnapshot 之前

等同于状态 1。

支持临时软件包

临时软件包是指未包含在基础软件包中的软件包 。

此方案对临时软件包施加了一些额外的限制:该 路由新创建的文件部分 说明了在任意状态下如何继续支持临时软件包 但有一点需要注意:如果存在以下情况,则必须删除临时软件包: 在准备新的基本分区时取消快照。

临时包可能会在更新后保持不变, 更新开始到活跃分区,将被复制到非活跃分区 并在调用 TakeSnapshot 时创建,之后,所有临时的 软件包将写入新分区,该分区可被 系统(更新后将成为新的活跃分区) 完成)。

路由新创建的文件

决定新文件的位置时,需要考虑三种情况 已安装。为简化本讨论,我们假设分区 A 处于活跃状态, 分区 B 处于非活跃状态。

案例 1:TakeSnapshot 之前

  • 基础软件包:未写入。
  • 临时软件包:写入分区 A。

案例 2:TakeSnapshot 之后,重新启动之前

  • 基础软件包:写入分区 B。
  • 临时软件包:写入分区 B。请注意,这些软件包 如果取消快照,则会在尝试下一个快照之前删除。

案例 3:重新装载后(注意:相当于“Before TakeSnapshot”)

  • 基础软件包:未写入。
  • 临时软件包:写入分区 B。

对 FVM 元数据的更改

FVM 的元数据具有以下结构:

区域 说明
超级区块 这是正常的。
分区表 条目数组(每个分区对应一个条目),包含分区名称、类型等信息。
切片分配 条目数组,每个可分配切片对应一个条目,指示其分配给哪个分区(如果有)以及该分区中的逻辑偏移量。

为简化提案,需要额外的元数据来录制 Slice 因此需要将以下内容存储在某个位置:

enum class uint32_t SliceType {
  kNormal,
  kAB,
  kABBitmap,
  kSharedData,
  kShared,
};

struct {
  uint32_t slice_offset;  // Offset within the partition
  SliceType slice_type;   // The slice type
} extents[8];

此元数据可添加到每个分区条目中。更好的方法可能是 添加包含此元数据的单独分区(即快照元数据 分区)。未讨论此元数据的确切位置和结构 分别介绍实现细节

采用这种结构时,Blobfs 的范围为:

[
  /* super block: */       { 0,                    SliceType::kAB },
  /* allocation bitmap: */ { 0x10000 / kSliceSize, SliceType::kABBitmap },
  /* inodes: */            { 0x20000 / kSliceSize, SliceType::kAB },
  /* journal: */           { 0x30000 / kSliceSize, SliceType::kShared },
  /* data: */              { 0x40000 / kSliceSize, SliceType::kSharedData }
]

需要某种状态,以指示两个分区中哪一个当前处于 可写、两个分区都处于活动状态(还是只有一个分区)以及哪个分区 应被视为可启动4

不需要对切片分配进行任何更改,除了位于 则需要分配备选的偏移值

可能还需要对 super 代码块进行其他细微更改(例如, 版本)。

支持 blobfs 格式演变

此方案大大简化了 blobfs 格式的演变, 可以完全删除并重新创建备用分区,且几乎没有费用 。

不过,在发展 blobf 时,仍然有两个挑战需要处理 格式。

  • 块分配映射无法更改,因为它是一个共享的结构 两个活跃/非活跃分区之间分配的值(鉴于 这似乎是完全可以接受的。)
  • 活跃分区无法覆盖已分配到的任何范围 被非活动分区所影响不过,处理过程相当简单: 在一定范围内,某些数据的内部格式需要改变, 分配新的范围并移动数据 TakeSnapshot 调用。

实现

实现过程中需要做出以下更改,更改的 具体取决于之前所做的更改:

  1. 对 FVM 和分区设置进行了更改。
  2. 对 Blobfs 分配进行更改。
  3. 对前期引导代码的更改。
  4. 更改了升级流程以使用新的 API。

第 1 条和第 4 条要求涉及大部分更改。第 1 步将涉及到磁盘 全新安装即可支持格式更改和迁移。正在还原 还需要进行全新安装这是关键一步, 但请注意,您只需更改格式即可;任何代码 新的 FVM 元数据可以保持休眠状态,直到后续阶段。

其他步骤都可以直接执行,无需彻底安装,并且可以 还原为原始大小

性能

这对性能的影响微乎其微。在升级过程中 因为快照制作成本高,影响很小,但是很有可能 却没那么重要其他时候 不会有任何变化。

空间要求

需要为 Blobfs 区域的额外副本预留空间:Superblock、 Inode 表和位图。确切程度取决于配置 但与总使用量相比应该相对较小 为 Blobfs 的可用空间。

安全注意事项

无。

隐私注意事项

无。

测试

将采用标准 Fuchsia 测试做法。现有系统测试应 已经在测试升级。我们将扩展这些测试,以包含 故意破坏新的 Blobfs 分区,以及试图 会破坏快照分区。

文档

FVM 的新架构和功能将在 紫红 >概念 >文件系统架构

缺点、替代方案和未知问题

完整的 A/B 提案

考虑了完整的 A/B 提案。虽然该方案从概念上来讲比较简单 但也存在一些严重的缺点:

  • 每个分区只能使用 50% 的可用磁盘空间。
    • 目前,这是对系统更新的限制, 预算设置为仅使用 50% 的可用 Blobfs 空间,但 A/B 提案会成为硬性约束。
    • 工程 build 已经超出 50% 的预算,因此未超出预算 支持修改多个文件的升级。工程师非常依赖于 能够进行增量小规模更新;打破此工作流程 非启动方式。
  • 不存在可在分区之间共享文件的机制, 都会重写每个文件
    • 这意味着闪光灯磨损更严重,且升级速度较慢。每次更新 基本上就是一次最大更新。

完整的 FVM 快照功能

开发完整的 FVM 快照功能存在一些挑战。传统 快照机制本质上通常是动态的,这意味着元数据 需要在写入写入到达时更新。此外, FVM 的切片大小(目前为 1 MiB,很快将达到 32 KiB)和 Blobfs 的块大小 (8 KiB)。解决此问题将大幅增加 FVM 的复杂性 此外,还有可能出现空间不足的极端情况。可能是 可以制定一个包含静态映射的方案,但不久之后, 最终得到的提案与这里介绍的提案并无太大区别。 总之,可能需要更长时间才能实现,可能 存在一些严重的缺点(写入放大、复杂性),并且没有 一些显而易见的优势。有可能出现 从长远来看,完整的快照功能会有所帮助, FVM 的元数据应提供扩展空间,以支持 。

先验技术和参考资料

可靠性和弹性的升级是一个常见问题, 方法:

  1. A/B 副本:保留功能相同的副本,并按如下方式在它们之间切换: 必填字段。简单,但需要空间。
  2. A/R 副本:保留恢复副本,即只有 支持恢复软件更复杂,空间要求更低 用户体验略有下降。
  3. A/B/R:第一部分和第二部分的组合2.
  4. + 快照:大多数情况下,只有一个副本可用。升级时 截取 A 的快照,然后以增量的形式对快照应用更新。 随时提供回滚到快照的选项。经常 复杂,但灵活。

作者认为 Android 使用 #3,iOS 和 macOS 使用 #2,4.

此 RFC 是 #4 的简化版本。

撤销的理由

此 RFC 的开发工作已持续数月,之后我们就决定 此 RFC。我们在做出此决定时考虑了多种因素,主要包括:

  • FVM 代码库中的技术债务导致进度缓慢和有风险的变更。缺乏 测试覆盖率、长期潜在 bug 以及关于 FVM 格式布局的广泛假设 (由于缺少 FVM 格式的封装)是主要障碍。

  • FVM 记录不足,团队也不太理解。关于 FVM 的组织知识 并且最初假设 FVM 是相对简单的 构建此地图项的适当位置不正确。

  • 推出此功能带来的影响比最初的理解更大,因为 功能将需要一个 FVM 主要格式修订,该修订被确定为高度 会对工程工作造成干扰(因为这需要对设备进行重新成像,而且 还需要滚动 Zedboot 版本,这本身就是一种破坏性很高的操作)。

鉴于开发此功能的风险较高,并且对增长的 Fuchsia 开发者社区的成员,没有必要再开发这项功能。相反, 团队将专注于扩大测试覆盖范围和提高自动化程度,以降低风险 中描述的 RFC 的动机,继续重写 FVM 主机工具( 意外复杂性的巨大来源),以及评估降低依赖性的可能性, 以降低对开发者的影响,从而在需要执行上述任一操作时 已更改。


  1. 请注意,这些额外的切片类型不一定需要 需要添加到 FVM 格式中您可以采用多种表达方式 而确切格式则留作实现 。 

  2. 为了简化操作,我们可以不用 A/B 测试 位图和共享数据类型,以及信任 Blobfs 行为 正确。然而,在 FVM 中包含此特征可为我们提供 防止 Blobfs 实现中出现 bug。此外,还有 可以选择腾出空间,稍后再添加 阶段。 

  3. 可以分享该期刊的地区。在 第二个分区被激活,此时可以刷新日志 锁定的只读分区不再需要;只需要 以防止可写 。 

  4. 这种可启动状态可能会被存储在其他位置 并在绑定时传递给 FVM,但直接存储 状态。