RFC-0204:VMO 參考資料子項

RFC-0204:VMO 參考子項
狀態已接受
區域
  • Kernel
說明

用於參考追蹤的 VMO 子類型。

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2023-01-04
審查日期 (年-月-日)2023-01-03

摘要

本文旨在介紹新的 VMO 子類型,用於明確追蹤 VMO 的參照。

提振精神

使用 VMO 代表記憶體中檔案的檔案系統,需要追蹤何時可以安全地垃圾收集這些 VMO。這可能包括將修改過的 VMO 內容排清至磁碟、從使用者分頁服務中分離 VMO、拆除 VMO 本身 (藉此釋放其頁面),以及釋放檔案系統可能用於追蹤開啟檔案的任何內部中繼資料等活動。

檔案系統可透過多種方式與用戶端共用檔案 VMO。這類控制代碼可提供給 VMO,讓用戶端直接從 VMO 的頁面讀取及寫入資料。用戶端也可以使用 VMO 控制代碼搭配 zx_vmar_map() 建立 VM 對應,捨棄 VMO 控制代碼,然後透過 VM 對應中的位址繼續存取 VMO 的頁面。檔案系統也可以使用 VMO 透過 zx_stream_create() 建立串流,並將串流控制代碼交給用戶端。您需要統一的參照計數機制,才能追蹤所有這類參照。

Blobfs (Fuchsia 上提供可執行檔的不變動檔案系統) 會嘗試建立檔案 VMO 的「寫入時複製」(ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE) 副本,解決這個參照計數問題。檔案 VMO 的唯一控制代碼由 blobfs 保留,每次用戶端要求檔案時,系統都會使用這個控制代碼建立 CoW 副本。這是 blobfs 傳遞的複製項控制代碼,而非原始 VMO。然後,參照計數會對應至追蹤未完成複製的數量,而 blobfs 則會在複製數量降至零時,等待原始 VMO 上的 ZX_VMO_ZERO_CHILDREN 信號變成有效狀態,藉此進行垃圾收集。這也適用於 VM 對應,因為即使 VMO 控制代碼遭到捨棄,對應也會在內部維持 VMO 的運作狀態。Blobfs 今天不會發放串流。

這種使用 CoW 複製的參照計數策略適用於不可變動的檔案系統,但套用至可變動的檔案系統 (例如 fxfsminfs) 時會失效。在可變動的檔案系統中,對用戶端檔案參照所做的任何變更,都應反映在原始檔案中。換句話說,在 CoW 複製作業中執行的任何寫入作業,都應返回父項 VMO。這違反了 CoW 語意,因為當頁面寫入 CoW 副本時,核心會在副本中建立上層頁面的副本;副本的變更永遠不會在上層中顯示。快照副本 (ZX_VMO_CHILD_SNAPSHOT) 也會發生類似問題。孩子個人檔案的更新不會顯示在家長個人檔案中,反之亦然。

這會留下 ZX_VMO_CHILD_SLICE VMO 子類型。從表面上看,切片似乎是我們需要的項目,因為切片可直接連往上層的頁面。不過,切片有以下限制:無法針對可調整大小的 VMO 建立切片,且切片本身無法調整大小。可變動的檔案系統會允許用戶端調整檔案 VMO 的大小。因此,目前可用的三種 VMO 子類型,在採用 VMO 子項的參照計數配置中,都無法達到要求。

利害關係人

協助人員:

  • cpu@google.com

審查者:

  • adanis@google.com、godtamit@google.com

已諮詢:

  • brettw@google.com、cdrllrd@google.com、csuter@google.com、travisg@google.com

社交:

這項設計已與 Zircon 團隊溝通,並在 Kernel Evolution 工作群組中討論。

設計

ZX_VMO_CHILD_REFERENCE 是新型 VMO 子項,用於追蹤 VMO 的待處理參照。目的是讓檔案系統每次需要用戶端檔案 VMO 的新參照時,都會建立參照子項,並將其視為原始 VMO。系統會根據參照子項建立串流,而非原始 VMO。同樣地,系統也會為參照子項建立 VM 對應。這樣一來,核心就能使用現有的 VMO 子項追蹤機制,追蹤 VMO 的參照數量。檔案系統可以使用 ZX_VMO_ZERO_CHILDREN 信號判斷何時沒有參照,以及何時可以安全地銷毀 VMO。

請注意,重複使用 ZX_VMO_ZERO_CHILDREN 信號表示只要存在任何類型的子項 (不只是參照子項),檔案系統就無法終止原始 VMO。不過,這沒問題,且允許檔案系統根據需求混合不同的子項類型。舉例來說,blobfs可以繼續使用 CoW 複製項,處理寫入作業必須限制在用戶端的資料區段,並使用參考子項 (以 ZX_VMO_CHILD_NO_WRITE 建立的唯讀子項) 處理文字區段。但仍可使用 ZX_VMO_ZERO_CHILDREN 信號,進行統一的參照計數配置。

建立參照子項

zx_vmo_create_child() 系統呼叫將支援新的選項標記 ZX_VMO_CHILD_REFERENCE。由於這在概念上是參照父項 VMO,因此 offsetlength 參數沒有意義,且都必須設為 0。參考子項一律會參照父項 VMO 的整體。另一種做法是讓呼叫端將 offset 指定為 0,並將 length 指定為 VMO 的目前大小,但這樣會對呼叫端造成不當負擔,因為呼叫端必須隨時瞭解父項 VMO 的大小,這可能需要進行 zx_vmo_get_size() 呼叫。如果檔案系統用戶端不斷變更 VMO 大小 (例如附加至檔案),檔案系統可能需要多次呼叫 zx_vmo_get_size(),直到看到穩定的大小為止,這相當麻煩。

管理 ZX_VMO_ZERO_CHILDREN 信號的相同規則,將適用於參照子項,如同現有的子項類型。建立參照子項會停用信號 (如果信號先前處於啟用狀態),而刪除最後一個子項則會啟用信號。

大小調整

ZX_VMO_CHILD_RESIZABLE 只有在以 ZX_VMO_RESIZABLE 建立上層 VMO 時,才允許使用。您可以建立可調整大小的 VMO 的不可調整大小參照,或可調整大小的參照。不過,您無法建立不可調整大小的 VMO 可調整大小的參照;參照子項可以視為參照父項 VMO,如果父項 VMO 不可調整大小,參照子項就無法調整大小。

參照會沿用父項的 ZX_VMO_RESIZABLE 選項。 參考控點本身的可調整大小功能,取決於控點右側的新 ZX_RIGHT_RESIZE,只有在參考項目是使用 ZX_VMO_CHILD_RESIZABLE 建立時,才會新增這個圖示。即使參考項目無法調整大小,父項仍可調整大小;這裡的無法調整大小僅限制透過參考項目直接調整大小的功能。將 ZX_RIGHT_RESIZE 右側與 ZX_VMO_RESIZABLE 選項旗標分開,即可擷取這項差異。

導入新版 ZX_RIGHT_RESIZE 時,必須視情況將其整合至所有現有的 VMO API。如要這麼做,請將權限新增至 VMO 控制代碼的預設權限集 (如果使用 ZX_VMO_RESIZABLE 標記建立),或是 VMO 子項的 ZX_VMO_CHILD_RESIZABLE

無論參照控制代碼本身是否具有 ZX_RIGHT_RESIZE,為可調整大小的 VMO 參照建立的所有 VM 對應都必須使用 ZX_VM_ALLOW_FAULTS 標記。

與 VMO 切片的差異

參考子項提供的許多功能與 ZX_VMO_CHILD_SLICE 重疊。兩者之間有幾項主要差異:

  • 切片可提供 VMO 中子範圍的視窗。參照一律會涵蓋整個 VMO。
  • 無法為可調整大小的 VMO 建立切片,切片本身也無法調整大小。可調整大小的 VMO 可以建立參照,且參照本身也可以調整大小 (如果父項可調整大小)。

支援 VMO 類型

您可以為使用 zx_vmo_create()zx_pager_create_vmo() 建立的所有 VMO,以及這類 VMO 的後代建立參照子項。這不包括以 zx_vmo_create_contiguous()zx_vmo_create_physical() 建立的 VMO,但並非完全排除這些用途。連續和實體 VMO 都無法調整大小,因此使用者可以改為在整個 VMO 上建立切片,以取得同等行為。

與 VMO 作業互動

系統只會將參考資料的所有 VMO 作業轉送給上層。例如:

  • VMO 的讀取和寫入作業會直接在父項 VMO 上執行。
  • 承諾和取消承諾也會轉送給父項。
  • 針對參照子項建立 VM 對應時,系統會對應上層中的相應頁面。

建立參照的子項

建立參照子項的權限,與在父項 VMO 上建立子項的權限相同,都受同一組規則控管。舉例來說,如果父項 VMO 是分頁支援的 VMO,因此不支援 ZX_VMO_CHILD_SNAPSHOT,其參照子項也不會支援 ZX_VMO_CHILD_SNAPSHOT

如果根據參照建立子項,即使子項在概念上可視為原始 VMO 的子項,子項的 parent_koid 仍會指向參照。目的是要準確呈現後代 VMO 的建立鏈結;這種行為與巢狀切片中子項建立的現有行為一致。

網頁出處

由於參照中的所有網頁都參照父項 VMO 中的網頁,因此系統會繼續將所有網頁歸因於父項,如 committed_bytes (zx_object_get_info()) 所示。參照不會直接保留任何網頁,且網頁歸因計數一律為零。這與現有的 VMO 頁面歸因模式一致,每個頁面都只會歸因於一個 VMO。

調整參照大小

調整參照大小時,系統會一併調整父項 VMO 的大小。同樣地,如果父項 VMO 調整大小,參照也會反映這項變更。參照也會遵循父項 VMO 的內容大小,並能以類似 VMO 大小的方式存取及操控。

核心異動

這些是 VMO 核心實作中必須進行的廣泛變更。

  • 參照子項會指向與父項相同的網頁容器 (VmCowPages)。這樣一來,對參照項目的作業就會轉送至父項,因為兩者都會對同一個內部物件執行作業,達到預期效果。
  • 系統會在父項的子項清單中追蹤參照,允許在父項上使用 ZX_VMO_ZERO_CHILDREN 信號。
  • 父項 VMO 也會維護所有參照子項的新清單。某些作業 (例如將更新傳播至 VM 對應項) 需要這個項目,這些作業是在頁面容器外部追蹤。
  • 為正確更新內容大小 (用於對 VMO 建立的串流執行讀取和寫入作業),參照也會與父項共用相同的 ContentSizeManager
  • 參考不會讓父項 VMO 維持運作。父項消失後,參照仍可繼續存取共用頁面容器,因為參照仍指向該容器,因此容器會保持運作。參考子項會重新歸入祖父項 (如有),這與現有子項類型的情況相同。系統會將父項的參照清單移至其中一個參照,確保 VM 對應更新作業能繼續正常運作。這一切可確保使用者觀察到的行為,與目前切片行為一致。

實作

您可以在幾個小型 CL 中實作參照,在 VMO 核心內部新增對新子類型的支援,然後透過 zx_vmo_create_child() 系統呼叫公開新類型標記。

建立可調整大小的 VMO 時,系統會將新的 ZX_RIGHT_RESIZE 新增至預設權利集。因此,在 zx_vmo_set_size()中強制執行這項權利時,不應中斷可調整大小的 VMO 現有使用者。我們可以先從簡單的開始,同時要求 ZX_RIGHT_WRITEZX_RIGHT_RESIZE。接著進行稽核,找出因移除 ZX_RIGHT_WRITE 而導致大小調整的現有案例,並讓這些案例移除 ZX_RIGHT_RESIZE (或同時移除 ZX_RIGHT_WRITEZX_RIGHT_RESIZE)。日後,zx_vmo_set_size() 只能檢查 ZX_RIGHT_RESIZE

效能

對參照執行的作業,會視為直接對父項 VMO 執行。這是透過在核心中分享相同的內部頁面容器來實現。因此,使用參照而非原始 VMO,應該不會對大多數作業造成任何可觀察到的效能影響。系統會編寫 VM 微基準,以驗證這項作業。

安全性考量

所有可對參照子項執行的作業,都可對父項 VMO 本身執行,而建立參照時需要父項 VMO 的控制代碼。參照只會取代原始 VMO 的特定用法,因此即使沒有參照,也能執行這些作業。

測試

系統會為參照子項新增核心單元測試和核心測試。

說明文件

zx_vmo_create_child() 系統呼叫說明文件將更新為 ZX_VMO_CHILD_REFERENCE。其他 VMO 系統呼叫的文件也需要更新,以納入 ZX_RIGHT_RESIZE (如適用)。

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

計算控點

另一種做法是導入一般控制代碼計數機制,當物件只剩一個控制代碼時,就會在物件上啟動信號。不過,VMO 控制代碼無法準確呈現未完成參照的數量。您可透過 VM 對應和串流保留 VMO 的參照,不必保留用於建立 VMO 的控制代碼。我們也需要考量 VM 對應、串流,以及日後可能保留 VMO 參照的任何新物件。這很快就會變得複雜,而且無法擴充。此外,控制代碼計數本質上具有競爭性,不建議用於偵錯以外的用途。

參考子項方法能更準確地擷取檔案系統及其用戶端之間的關係,而這實際上是階層式關係。檔案系統對 VMO 的參照可視為主要參照,而檔案系統提供給用戶端的其他參照都是次要參照。上下層關係可充分代表這種情況。另一方面,控制代碼是對稱的;在這種情況下,我們不僅要確保只保留一個參照,也希望保留的參照是檔案系統持有的參照。

參考權杖

我們可以鑄造新的參照權杖物件,供檔案系統發放,除了 VMO 或串流之外,這可以是核心提供的新物件,也可以是檔案系統實作的物件。不過,這需要變更檔案系統 API 介面,才能支援傳遞新的參照權杖,可能不可行。參考子項可與 VMO 互換使用,因此支援參考子項時,可保留現有的檔案系統 API,大幅簡化支援程序。在 VMO 外部進行外部參照計數,也容易發生錯誤,因為可能會意外捨棄權杖。

調整切片大小

參考子項與切片的主要差異在於參考子項可調整大小,因此,您可以定義切片環境中的大小調整語意,而不需建立新的 VMO 子項型別。不過,切片可以跨越父項中的子範圍,這會讓調整大小的推論變得更加困難。

舉例來說,假設切片的大小調整為大於建立時的大小,如果切片現在發現先前無法看到的父項頁面,這會令人驚訝。另一方面,切片不會直接擁有任何頁面,因此如果要在擴展範圍中分叉零個頁面,就會很奇怪。如果父項大小調整為較大尺寸,也會發生類似問題。由於動機使用案例需要在父項和子項之間傳播大小調整,這也需要調整任何切片的大小,這可能再次令人意外。

調整右側大小

目前 VMO 的大小調整功能是由 ZX_RIGHT_WRITE 控制。日後可藉由獨立的 ZX_RIGHT_RESIZE,更嚴格地執行政策。舉例來說,它可以允許使用者建立可寫入的非可調整大小 VMO 控制代碼,並與用戶端共用。目前無法建立這類 VMO 控制代碼;可寫入的 VMO 控制代碼也可調整大小 (如果 VMO 是以可調整大小的方式建立)。

既有技術和參考資料

zx_vmo_create_child()

zx_vmar_map()

zx_stream_create()