RFC-0005:Blobfs 快照

RFC-0005:Blobfs 快照
狀態已遭拒
區域
  • 儲存空間
說明

升級期間支援 Blobfs 快照。

Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2020-09-06
審查日期 (年-月-日)2020-09-21

摘要

本 RFC 說明簡單的快照機制,可提高升級程序中錯誤的復原能力。Fuchsia Volume Manager (FVM) 的變更可讓您擷取 Blobfs 分割區的快照,並在升級期間的任何階段還原。

提振精神

撰寫本文時,如果升級失敗導致 Blobfs 分區損毀,裝置可能會進入難以復原的狀態。目前復原磁碟分割區無法還原處於這種狀態的裝置,因此在這些情況下,唯一支援的還原方式是透過開機載入程式,但這個程序對一般使用者來說並不友善。

快照機制可降低我們陷入這種狀態的風險。

設計

基本概念是在 FVM 中支援原始快照機制,允許在升級期間出現兩個分割區,但允許在分割區之間共用資料。

目前 FVM 只是簡單的磁碟區管理工具,可將任意與磁碟區對齊的邏輯偏移量對應至基礎裝置上的特定偏移量,並將不同磁碟分割區的對應項目分開。

Blobfs 包含下列不同區域:

地區
Superblock
分配點陣圖
索引節點
日誌
資料

為支援此提案,我們可以在 FVM1 中允許不同的切片類型。這些類型會套用至切片的範圍

類型 說明
A/B 區塊 這是指具有替代副本的切片範圍。
A/B 位元對應2 這會是切片的範圍,其中包含點陣圖的替代副本,代表共用資料範圍中的分配情形。
分享的資料 這會是切片範圍,其分配作業由 A/B 位元對應範圍管理。
共用 這會是兩個分區共用的範圍,但一次只能有一個分區寫入該區域。

有了這些切片類型,FVM 就能呈現兩個分割區,顯示範圍的 A/B 變體。因此,回到 Blobfs 區域,我們會看到:

地區 類型
Superblock A/B 區塊
分配點陣圖 A/B 點陣圖
索引節點 A/B 區塊
日誌 已分享3
資料 分享的資料

在大多數情況下,只有一個分割區會處於啟用狀態,系統看起來與現在相同。

升級期間,系統會啟用第二個分割區,此時第一個分割區會鎖定,不允許進一步寫入,但仍會繼續提供讀取服務。第二個磁碟分割區的準備方式可能與現在相同,但在升級期間,您隨時可以選擇返回第一個磁碟分割區,且保證第一個磁碟分割區不會受到影響。

對於 A/B 範圍,您可以輕鬆查看第一個分區的資料保留方式;第二個分區不會看到第一個分區的資料。如果是日誌,共用區域只能由可寫入的分割區寫入,也就是第二個分割區。如果是共用資料區域,點陣圖會指出可寫入的區塊。標示為由第一個分區使用的任何區塊,對兩個分區而言都會顯示為唯讀。

為方便執行這項配置,第二個分割區也必須能夠讀取替代點陣圖,才能知道允許分配哪些區塊。為此,替代點陣圖可以出現在目前未使用的偏移量,位於邏輯位址空間中。草案提案是所有替代 A/B 範圍都會出現在相同偏移,但會設定頂端位元 (唯讀)。

下圖應可說明每個分割區的顯示方式:

分區排列方式

圖 1:磁碟分割區配置。

注意:

  • 這能為我們帶來一些簡單 A/B 分區方法提供的彈性,但並非全部。
  • 我們可以保留目前的漸進式更新方法 (即只更新已變更的 Blob),但這樣一來,版面配置就無法預測。在使用者建構版本中,我們可以選擇完全重寫所有 Blob,但仍會受到片段化影響。
  • 這會增加 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 會自動切換可寫入的分區,因此這個 API 可能很少會用到,但如有需要返回並讓作用中分區可寫入 (例如為了垃圾收集未使用的 Blob),則可使用這個 API。

SetBootPartition

變更可開機的分區。

通常可開機的磁碟分割會根據哪個 ZBI 插槽處於啟用狀態而變更,但也可以分別切換可開機的磁碟分割。這個選項可能很少用到。

DeleteSnapshot

將替代分割區標示為已清除。FVM 可能會選擇刪除其中的中繼資料。

ListSnapshotPartitions

向 FVM 查詢已設定快照的分割區。

QuerySnapshotPartition

查詢 FVM,取得支援快照的分區相關資訊。

  • 識別 A/B 分割區的狀態,例如哪個分割區處於啟用狀態。

故障模式

系統可能會在狀態機描述的任何狀態中發生故障。本節說明系統發生故障時應採取的適當動作。

請注意,失敗可能是自願性 (系統主動決定取消進行中的更新),也可能是非自願性 (系統因外部因素而失敗,例如斷電)。這兩種情況都必須考量。

請注意,blobfs 具有記錄機制,可防止修改期間發生非自願性故障時,中繼資料損毀。您不需要額外作業,即可確保 blobfs 在修改期間發生非自願性故障時仍能正常運作。

如有必要,FVM 中的任何新中繼資料作業都應設為交易式,以免在修改期間發生非自願性故障,導致 FVM 損毀。

狀態 1:TakeSnapshot 之前

在此狀態下,您不需要變更失敗處理方式,行為與目前的系統行為相同。

狀態 2:TakeSnapshot 後,重新啟動前

  • 如果是自願性失敗,可以呼叫 CancelSnapshot API 刪除非使用中的分割區,讓系統返回狀態 1。
  • 如果發生非自願性故障,系統可以選擇在恢復連線後直接中止更新 (透過叫用 CancelSnapshot),也可以選擇嘗試繼續更新。

狀態 3:重新啟動後,在 TakeSnapshot 之前

等同於狀態 1。

支援暫時性套件

如果套件未納入特定系統版本的基本套件組,即為暫時性套件。

這項提案對暫時性套件的限制不多;下方的「新建立檔案的路由」一節說明如何在 OTA 期間的任何狀態下繼續支援暫時性套件,但有一項附帶條件:如果準備新基礎分割區時中止快照,就必須刪除暫時性套件。

由於更新開始前寫入有效分割區的暫時性套件會在呼叫 TakeSnapshot 時複製到無效分割區,因此暫時性套件可能會在更新後保留。之後,所有暫時性套件都會寫入新分割區,系統可讀取及寫入該分割區 (更新完成後,該分割區會成為新的有效分割區)。

新建立檔案的路由

決定新檔案的安裝位置時,請考量以下三種情況。為簡化討論,假設分割區 A 處於啟用狀態,而分割區 B 處於停用狀態。

案例 1:TakeSnapshot 之前

  • 基本套件:未撰寫。
  • 暫時性套件:寫入分區 A。

情況 2:在 TakeSnapshot 之後,重新啟動之前

  • 基本套件:寫入 B 分區。
  • 暫時性套件:寫入分區 B。請注意,如果中止快照,系統會刪除這些套件,然後再嘗試下一個快照。

案例 3:重新掛接後 (注意:相當於「Before TakeSnapshot」)

  • 基本套件:未撰寫。
  • 暫時性套件:寫入分區 B。

FVM 中繼資料異動

FVM 的中繼資料結構如下:

地區 說明
Superblock 符合預期。
分區資料表 項目陣列,每個分割區各有一個項目,包含分割區名稱、類型等項目。
配量分配 項目陣列,每個可分配的切片各有一個項目,指出切片分配到的分割區 (如有) 和該分割區內的邏輯偏移。

為方便提案,需要額外的中繼資料來記錄範圍的切片類型,因此需要將類似下列內容儲存在某處:

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

除了需要分配替代位移的切片外,切片分配不需要任何變更。

超級區塊可能還需要進行其他微小變更 (例如版本升級)。

支援 blobfs 格式演進

這項提案大幅簡化了 blobfs 格式演進,因為替代分割區可以完全刪除並重新建立,每次更新的成本很低。

不過,根據這項提案演進 blob 格式時,仍有兩項挑戰需要處理。

  • 區塊配置對應表無法變更,因為這是有效/無效分割區共用的結構。(由於分配對應表非常簡單,這似乎完全可以接受)。
  • 作用中分區無法覆寫也由非作用中分區分配的任何範圍。不過,這項問題很容易解決:如果範圍內某些資料的內部格式需要變更,系統只要在 TakeSnapshot 呼叫期間分配新範圍並移動資料即可。

實作

導入時需要進行下列變更,這些變更大致上取決於先前的變更:

  1. 變更 FVM 和磁碟分割設定。
  2. Blobfs 分配異動。
  3. 早期啟動程式碼的變更。
  4. 升級程序異動,以便使用新版 API。

其中大部分變更都是因應第 1 和第 4 項規定。#1 會涉及磁碟格式變更,並支援透過全新安裝進行遷移。如要還原,也必須重新安裝。這是最關鍵的步驟,涉及的風險也最高,但請注意,您只需要變更格式,使用新 FVM 中繼資料的任何程式碼都可以維持休眠狀態,直到後續階段再啟用。

其他步驟都不需要清除重裝,而且同樣可以還原。

效能

這對效能的影響應該微乎其微。升級期間,由於快照作業會耗用資源,因此可能會造成些微影響,但相較於其他升級活動,這應該微不足道。其他時間則不應有任何變化。

空間需求

您必須預留空間給 Blobfs 區域的額外副本,包括 Superblock、Inode 表格和 Bitmap。具體大小取決於裝置的設定,但相較於 Blobfs 可用的總空間,應該相對較小。

安全性考量

無。

隱私權注意事項

無。

測試

系統會採用標準 Fuchsia 測試做法。現有的系統測試應已在測試升級。這些測試會擴大範圍,納入刻意損毀新 Blobfs 分區的測試,以及嘗試刻意損毀快照分區的測試。

說明文件

FVM 的新架構和功能將在「Fuchsia」>「概念」>「檔案系統架構」中說明。

缺點、替代方案和未知事項

完整的 A/B 提案

我們已考慮完整的 A/B 提案。雖然這項提案的概念很簡單,但有幾個重大缺點:

  • 每個分割區只能使用 50% 的可用磁碟空間。
    • 目前系統更新的這項限制是軟性限制,預算只允許使用 50% 的可用 Blobfs 空間,但 A/B 提案會將這項限制設為硬性限制。
    • 工程建構作業已超出預算 50%,因此不支援修改許多檔案的升級作業。工程師非常依賴執行增量小更新的能力;如果這個工作流程中斷,就無法開始作業。
  • 分割區之間沒有檔案共用機制,因此每次更新都會重新編寫每個檔案。
    • 這表示快閃記憶體耗損會增加,升級速度也會變慢。每次更新基本上都是最大更新。

完整 FVM 快照功能

開發完整的 FVM 快照功能時,會遇到一些挑戰。傳統快照機制通常是動態性質,也就是說,中繼資料必須隨著寫入作業更新。此外,FVM 的切片大小 (目前為 1 MiB,不久後將為 32 KiB) 與 Blobfs 的區塊大小 (8 KiB) 不符。解決這個問題會大幅增加 FVM 的複雜度,而且在某些極端情況下,空間可能會用盡。或許可以開發具有靜態對應的架構,但不久後,您就會發現提案與這裡呈現的架構大同小異。總而言之,這項做法可能需要很長時間才能實作,而且可能會有嚴重的缺點 (寫入放大、複雜度),且我們認為近期內不會有明顯的優點。從長遠來看,完整快照功能可能會有幫助,因此 FVM 中繼資料的精確設計應提供擴充空間,以便日後支援這類用途。

既有技術和參考資料

可靠且具復原能力的升級是常見問題,通常可透過下列方式解決:

  1. A/B 副本:保留功能相同的副本,並視需要切換。簡單,但會耗用空間。
  2. A/R 副本:保留復原副本,這是精簡版,僅支援還原軟體。較複雜、空間需求較低、使用者體驗略有下降。
  3. A/B/R:#1 和 #2 的組合。
  4. A+ 快照:大部分時間只會提供一個副本。升級時,請擷取 A 的快照,並將更新套用為快照的差異。 隨時提供還原快照的選項。通常很複雜,但很靈活。

作者認為 Android 使用 #3,iOS 和 macOS 則使用 #2 和 #4。

本 RFC 是 #4 的簡化版本。

撤銷原因

我們在決定停止這項 RFC 的開發工作前,已進行了數個月。我們做出這項決定的原因有許多,主要包括:

  • FVM 程式碼集中的技術債導致進展緩慢,且變更風險高。測試涵蓋範圍不足、長期潛在的錯誤,以及對 FVM 格式版面配置的廣泛假設 (由於 FVM 格式缺乏封裝),都是主要障礙。

  • FVM 的文件不足,團隊對此瞭解不深。隨著時間推移,組織對 FVM 的瞭解程度逐漸衰退,而最初認為 FVM 是相對簡單且適合建構這項功能的假設並不正確。

  • 推出這項功能造成的影響比原先預期的更大,因為這項功能需要大幅修訂 FVM 格式,而這會對工程工作造成嚴重干擾 (因為需要重新映像裝置,且需要推出 Zedboot 版本,這本身就是一項極具干擾性的作業)。

由於開發這項功能風險高,且很可能對日漸壯大的 Fuchsia 開發人員社群造成影響,因此我們不再適合繼續開發這項功能。儲存空間團隊將專注於改善測試涵蓋範圍和自動化作業,以降低本 RFC 動機中所述的風險,並繼續重寫 FVM 主機工具 (這是意外複雜性的主要來源),以及評估減少對特定 FVM/Zedboot 版本的依賴,以減少其中任一版本需要變更時對開發人員的影響。


  1. 請注意,這些額外的切片類型不一定需要新增至 FVM 格式;表達這項中繼資料的方法有很多種,確切格式則留待實作細節。 

  2. 為簡化作業,我們可以省略 A/B 位元映像和共用資料型別,並信任 Blobfs 的行為正確無誤。不過,在 FVM 中加入這項功能,可提供額外的保護層級,防範 Blobfs 實作中的錯誤。您也可以先保留空間,之後再新增。 

  3. 可以分享日記的區域。在啟動第二個分割區時,可以清除日誌,此時鎖定的唯讀分割區不再需要日誌,日誌只用於避免可寫入分割區發生不一致的情況。 

  4. 可啟動的狀態可能會儲存在其他位置,並在繫結時傳遞至 FVM,但將此狀態儲存在 FVM 中可能比較簡單。