RFC-0005:Blobfs 快照 | |
---|---|
狀態 | 已遭拒 |
區域 |
|
說明 | 支援在升級期間使用 Blobfs 快照。 |
Gerrit 變更 | |
作者 | |
審查人員 | |
提交日期 (年-月-日) | 2020-09-06 |
審查日期 (年-月-日) | 2020-09-21 |
摘要
這份 RFC 說明瞭簡單的快照機制,可提高升級程序中的錯誤復原能力。變更 Fuchsia 音量管理員 (FVM),可讓 Blobfs 區隔的快照,在升級期間的任何階段皆可還原。
提振精神
在撰寫本文時,升級失敗導致 Blobfs 分割區損毀,可能會讓裝置處於難以復原的狀態。目前的復原分區無法還原處於此狀態的裝置,因此在這種情況下,唯一支援的方式是透過引導程式,使用不利於使用者的程序。
快照機制可降低我們處於此狀態的風險。
設計
基本概念是在 FVM 中支援原始快照機制,讓升級期間出現兩個分區,但允許在分區之間共用資料。
目前,FVM 是簡單的音量管理器,可將切片從任意切片對齊邏輯偏移,對應至基礎裝置上的特定偏移,並將不同分區的對應項目分開。
Blobfs 包含下列獨立區域:
地區 |
---|
超級區塊 |
分配點陣圖 |
索引節點 |
日誌 |
資料 |
為了支援這項提案,我們可以在 FVM 中允許不同的切片類型1。這些類型會套用至切片的範圍:
類型 | 說明 |
---|---|
A/B 區塊 | 這是具有備用副本的區塊範圍。 |
A/B 位圖2 | 這是切片的範圍,其中包含代表共用資料範圍中分配情形的點陣圖備用副本。 |
分享的資料 | 這是切片的範圍,其分配方式由 A/B 位圖範圍管理。 |
共用 | 這會是兩個分區之間共用的區域,但一次只能由一個分區寫入該區域。 |
有了這些切片類型,FVM 就能呈現兩個顯示範圍的 A/B 變化版本。因此,回到 Blobfs 區域,我們會看到:
地區 | 類型 |
---|---|
超級區塊 | A/B 區塊 |
分配點陣圖 | A/B 點陣圖 |
索引節點 | A/B 區塊 |
日誌 | 共用3 |
資料 | 分享的資料 |
在大多數情況下,系統只會啟用其中一個分割區,因此系統會顯示目前的樣貌。
在升級期間,系統可以啟用第二個分割區,此時第一個分割區會變成「鎖定」狀態,且無法再寫入,但仍可繼續讀取。您可以準備第二個分區,方法可能與目前相同,但在整個升級期間,您隨時可以選擇回復第一個分區,且保證不會受到影響。
對於 A/B 範圍,您可以輕鬆查看第一個分區的資料如何保留;第二個分區不會看到第一個分區的資料。對於日誌,共用區域 — 只有可寫入的區段才能寫入,也就是第二個區段。對於共用資料區域,位元遮罩會指出可寫入哪些區塊。任何標示為由第一個分區使用的區塊,在兩個分區中都會顯示為唯讀。
為了方便實施這項配置,第二個區段也必須能夠讀取替代位元資料,以便瞭解可分配哪些區塊。因此,可在邏輯位址空間中,以目前未使用的偏移量呈現。假設提案是所有替代 A/B 範圍都會顯示在相同的偏移量,但會設定頂層位元 (唯讀)。
下圖說明各個區隔的顯示方式:
圖 1:分區配置。
注意:
- 這可為我們帶來部分彈性,但並非全部,這與簡單的 A/B 區隔方法相同。
- 我們可以保留目前的增量更新方式 (即只更新已變更的 Blob),但代價是無法獲得可預測的版面配置。在使用者版本中,我們可以選擇完全重寫所有 blob,但仍會受到分割的影響。
- 這會使 FVM 變得更複雜。
新版升級流程
您必須修改升級流程,以利快照互動。圖 2 顯示目前的流程,圖 3 則顯示建議的替代方案。新的 API 和互動會以顏色標示。
圖 2:目前的升級實作方式 (高階)
圖 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:拍攝快照前
在這個狀態下,失敗處理作業不需要任何變更;行為與目前的系統行為相同。
狀態 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 的中繼資料結構如下:
地區 | 說明 |
---|---|
超級區塊 | 預期結果。 |
分區資料表 | 一個項目陣列,每個分區一個,包含分區名稱、類型等。 |
配量分配 | 陣列項目,每個可分配的切片各有一項,用於指出該切片已分配至哪個分區 (如果有的話),以及該分區內的邏輯偏移量。 |
為了方便提案,您需要額外提供中繼資料,以便記錄範圍的切片類型,因此需要儲存類似下列的內容:
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 格式演進,因為每次更新時,可完全刪除及重新建立替代分區,而這項作業的成本不高。
不過,根據此提案改進 blobfs 格式時,仍有兩項挑戰需要處理。
- 區塊配置對應表無法變更,因為這是兩個活動/非活動分區之間共用的結構。(考量到分配圖表的簡單程度,這似乎是可接受的做法)。
- 活動分區無法覆寫由非活動分區分配的任何區塊。不過,這很容易處理:如果某個區域內的部分資料需要變更內部格式,系統可以在 TakeSnapshot 呼叫期間,直接分配新的區域並移動資料。
實作
實作這項功能需要進行下列變更,這些變更大致取決於先前的變更:
- 變更 FVM 和分割區設定。
- Blobfs 配置的變更。
- 對前期引導程式碼進行變更。
- 升級程序的變更,以便使用新的 API。
大部分的變更都必須符合 #1 和 #4 的要求。#1 會涉及磁碟上的格式變更,且在全新安裝的情況下,系統會支援遷移作業。您也必須重新安裝才能還原。這是涉及最多風險的重要步驟,但請注意,只有格式變更需要就位;任何使用新 FVM 中繼資料的程式碼都可以在後續階段保持休眠狀態。
其他步驟則可在不需要重新安裝的情況下完成,且可同樣還原。
成效
這對效能應該不會造成太大影響。升級期間可能會因為快照作業產生成本而受到輕微影響,但相較於其他升級活動,這可能不具影響力。在其他時間,則不應有任何變化。
空間需求
您需要為 Blobfs 區域的額外副本保留空間,包括超級區塊、Inode 表格和 Bitmap。具體值取決於裝置的設定,但與 Blobfs 可用的總空間相比,應相對較小。
安全性考量
無。
隱私權注意事項
無。
測試
我們會採用標準 Fuchsia 測試方法。現有的系統測試應已測試升級功能。這些測試將擴大範圍,納入故意毀損新 Blobfs 區隔的測試,以及故意毀損快照區隔的測試。
說明文件
我們會在 Fuchsia > 概念 > 檔案系統架構 一節中說明 FVM 的新架構和功能。
缺點、替代方案和未知事項
完整的 A/B 提案
我們考慮了完整的 A/B 提案。雖然這個提案在概念上很簡單,但有幾個重大缺點:
- 每個分區只能使用可用磁碟空間的 50%。
- 目前這項限制對系統更新而言是軟性限制,因為系統更新的預算只使用可用的 Blobfs 空間的 50%,但 A/B 版本提案會將這項限制設為硬性限制。
- 工程建構作業已超過 50% 的預算,因此不支援會修改許多檔案的升級作業。工程師非常依賴這項功能,可進行小幅的漸進式更新;因此,中斷這項工作流程是不可行的。
- 沒有在分區之間共用檔案的機制,因此每次更新都會重寫每個檔案。
- 這表示會增加閃存裝置的磨損,並導致升級速度變慢。每項更新基本上都是最大更新。
完整的 FVM 快照功能
開發完整的 FVM 快照功能會遇到一些挑戰。傳統的快照機制通常具有動態性質,也就是說,需要在寫入作業完成時更新中繼資料。此外,FVM 的切片大小 (目前為 1 MiB,即將變更為 32 KiB) 與 Blobfs 的區塊大小 (8 KiB) 不符。解決這個問題會大幅增加 FVM 的複雜度,而且也可能會出現空間不足的極端情況。或許可以開發具有靜態對應項目的配置,但不久之後,您就會提出與這裡所述相去不遠的提案。總而言之,這項做法實作起來可能需要更長的時間,且可能會帶來一些嚴重的負面影響 (寫入放大、複雜度),而且不提供我們認為近期需要的明確效益。完整快照功能可能會在長期上有所助益,因此 FVM 中精確的結構定義應可提供擴充空間,以便在未來支援這類用途。
既有技術與參考資料
可靠且具備復原能力的升級作業是常見問題,通常可透過以下方式解決:
- A/B 版本:保留功能相當的版本,並視需要在兩者之間切換。簡單,但會佔用空間。
- A/R 副本:保留復原副本,這是僅支援還原軟體的簡化版本。較為複雜,空間需求較低,使用者體驗略為下降。
- A/B/R:#1 和 #2 的組合。
- A+ 快照:大多數情況下,只有一個可用的副本。在升級時,請擷取 A 的快照,並將更新套用至快照的差異。隨時提供回復快照的選項。通常複雜,但具彈性。
作者認為 Android 使用 #3,iOS 和 macOS 則使用 #2 和 #4。
本 RFC 是 #4 的簡化版本。
撤銷的理由
我們在這個 RFC 的開發工作進行了數個月後,決定停止這項 RFC 的工作。這項決定受到多項因素影響,主要包括:
FVM 程式碼集的技術債導致進度緩慢,且變更風險高。缺乏測試涵蓋率、長期潛在的錯誤,以及對 FVM 格式版面配置的廣泛假設 (由於 FVM 格式缺乏封裝) 是主要的阻礙。
團隊對 FVM 的文件不足,也無法充分理解。關於 FVM 的組織知識隨著時間流逝而逐漸減少,而我們最初假設 FVM 是建構這項功能的相對簡單且合適的場所,這項假設並不正確。
推出這項功能的影響比原先預期的更大,因為這項功能需要 FVM 主要格式修訂版本,而這項修訂版本被判定會對工程作業造成極大干擾 (因為這需要重新映像裝置,且也需要推出 Zedboot 版本,而這本身就是極具干擾性的作業)。
由於開發這項功能的風險極高,且很可能會影響不斷成長的 Fuchsia 開發人員社群,因此我們不再繼續開發這項功能。相反地,儲存空間團隊將專注於改善測試涵蓋率和自動化,以減輕此 RFC 動機中所述的風險,並繼續改寫 FVM 主機工具 (造成意外複雜性的重大來源),以及評估減少對特定 FVM/Zedboot 版本依賴的可能性,以便在需要變更其中任何一個版本時,降低對開發人員的影響。
-
請注意,這些額外的切片類型不一定需要加入 FVM 格式;表達此中繼資料的方式有很多種,具體格式則留給實作細節。 ↩
-
為了簡化程序,我們可以省略 A/B 位圖和共用資料類型,並信任 Blobfs 的運作方式正確無誤。不過,在 FVM 中加入這項功能,可為 Blobfs 實作提供額外的錯誤防護機制。您也可以選擇保留空間,並在稍後再新增。 ↩
-
日誌的區域可供共用。在啟用第二個分割區時,系統可以將日誌刷出,並且不再需要用於鎖定的唯讀分割區;這項作業只需要用於避免可寫分割區出現不一致的情況。 ↩
-
這個可啟動的狀態可以儲存在其他位置,並在繫結時傳遞至 FVM,但最好還是直接將這個狀態儲存在 FVM 中。 ↩