RFC-0204:VMO 參考資料子項

RFC-0204:VMO 參照子項
狀態已接受
區域
  • 核心
說明

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

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

摘要

本文件的目標是介紹新的 VMO 子類型,用於明確追蹤 VMO 的參照。

提振精神

使用 VMOs 代表記憶體中檔案的檔案系統,需要追蹤這些 VMOs 的垃圾收集作業何時可安全執行。這可能包括將修改過的 VMO 內容刷到磁碟、從 userpager 服務中分離 VMO、拆解 VMO 本身,進而釋放其頁面,以及釋出檔案系統可能用於追蹤已開啟檔案的任何內部中繼資料。

檔案系統可以透過多種方式與用戶端共用檔案 VMOs。他們可以將重複的句柄提供給 VMO,用戶端就能直接讀取及寫入 VMO 中的頁面。用戶端也可以使用 zx_vmar_map() 搭配 VMO 句柄建立 VM 對應,然後放棄 VMO 句柄,並透過 VM 對應中的位址繼續存取 VMO 的頁面。檔案系統也可以使用 zx_stream_create() 搭配 VMO 建立串流,並將串流句柄發給用戶端。您必須使用統一的參照計數機制,才能追蹤所有這些不同類型的參照。

Blobfs 是 Fuchsia 上執行可執行檔的不可變動檔案系統,它會嘗試透過建立檔案 VMO 的 Copy-on-Write (ZX_VMO_CHILD_SNAPSHOT_AT_LEAST_ON_WRITE) 複本,解決這個參照計數問題。blobfs 會保留檔案 VMO 的唯一句柄,用於在用戶端每次要求檔案時建立 CoW 複本。這是 blobfs 提供的複本控制代碼,而非原始 VMO。參照計數會接著對應至追蹤未結結點數,而當複本數量降至零時,blobfs 可透過等待 ZX_VMO_ZERO_CHILDREN 信號在原始 VMO 上啟用,進行垃圾收集。這也適用於 VM 對應,因為即使 VMO 句柄已捨棄,VMO 仍會在內部保留為活動狀態。Blobfs 今天不會發布串流。

使用 CoW 複本的參照計數策略適用於不可變動的檔案系統,但如果套用至 fxfsminfs 等可變動的檔案系統,就會中斷。在可變動的檔案系統中,對用戶端對檔案的參照所做的任何變更,都應反映在原始檔案中。換句話說,在 CoW 複本中執行的任何寫入作業都應回傳至父項 VMO。這會違反 CoW 語意,因為在 CoW 副本中寫入網頁的當下,核心會在副本中建立父項網頁的副本;副本的變更永遠不會顯示在父項中。快照副本 (ZX_VMO_CHILD_SNAPSHOT) 也會發生類似問題。子項中的更新內容不會顯示在父項中,反之亦然。

這會留下 ZX_VMO_CHILD_SLICE VMO 子項類型。表面上看來,切片似乎是我們需要的東西,因為它們可提供直接的視窗,讓您前往父項網頁。不過,切片有以下限制:無法針對可調整大小的 VMOs 建立切片,也無法自行調整大小。可變動的檔案系統會希望允許用戶端調整檔案 VMOs 的大小。因此,目前可用的三種 VMO 子項類型,在使用 VMO 子項的參照計數方案中都不足。

相關人員

協助人員:

  • cpu@google.com

審查者:

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

諮詢:

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

社會化:

我們已將這項設計與 Zircon 團隊分享,並在 Kernel Evolution Working Group 中討論。

設計

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 信號的相同規則,也會套用至以現有子項類型參照子項。建立參照子項會停用信號 (如果信號先前處於啟用狀態),而銷毀最後一個子項則會啟用信號。

大小調整

只有在父項 VMO 是使用 ZX_VMO_RESIZABLE 建立時,ZX_VMO_CHILD_RESIZABLE 才會獲得許可。您可以建立無法調整大小的參照,或可調整大小的 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。
  • 您無法為可調整大小的 VMOs 建立切片,且這些切片本身也無法調整大小。您可以為可調整大小的 VMOs 建立參照,且這些參照本身也可調整大小 (如果父項可調整大小)。

支援 VMO 類型

您可以為所有使用 zx_vmo_create()zx_pager_create_vmo() 建立的 VMOs 和這些 VMOs 的子項建立參照子項。這項設定會排除使用 zx_vmo_create_contiguous()zx_vmo_create_physical() 建立的 VMOs,但不會完全排除這些用途。連續和實體 VMO 皆無法調整大小,因此使用者可以改為在整個 VMO 上建立切片,以便取得等同的行為。

與 VMO 作業互動

所有參照的 VMO 作業都會直接轉送至上層。例如:

  • VMO 讀取和寫入作業會直接在父項 VMO 上執行。
  • 提交和取消提交也會轉送至父項。
  • 針對參照子項建立的 VM 對應項目,會對應至父項中的對應頁面。

建立參照項目的子項

建立參照項目子項的功能,會受到與父項 VMO 上子項建立作業相同的規則控制。舉例來說,如果父項 VMO 是 pager 支援的 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 大小的方式存取及操作。

核心變更

這些是 VMOs 核心實作中需要的廣泛變更。

  • 參照子項會指向與其父項相同的網頁容器 (VmCowPages)。這會產生預期的效果,也就是將對參考項目執行的作業轉送至父項,因為兩者都會針對相同的內部物件執行。
  • 系統會在父項的子項清單中追蹤參照項目,以便在父項上使用 ZX_VMO_ZERO_CHILDREN 信號。
  • 父項 VMO 也會維護所有參照子項的新清單。這項功能適用於特定作業,例如將更新內容傳播至 VM 對應項目,而這些項目會在網頁容器外追蹤。
  • 參照項目也會與父項共用相同的 ContentSizeManager,以便正確更新內容大小,這些大小會用於針對 VMO 建立的資料流進行讀取和寫入。
  • 參照不會讓父項 VMO 保持運作。父項消失後,參照項目仍可繼續存取共用網頁容器,因為參照項目仍會指向該容器。參考子項會在祖父項 (如有) 中重新安置,就像現有子項類型一樣。父項參照清單會移至其中一個參照,以便 VM 對應更新繼續正常運作。這一切都是為了確保使用者觀察到的行為,與現今切片的現有行為一致。

實作

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

新的 ZX_RIGHT_RESIZE 會在建立時加入可調整大小的 VMOs 的預設權限組合。因此,在 zx_vmo_set_size() 中強制執行權限,不應影響可調整大小的 VMOs 現有使用者。我們可以先從簡單的做法開始,要求同時提供 ZX_RIGHT_WRITEZX_RIGHT_RESIZE。接著,您可以執行稽核作業,找出已移除 ZX_RIGHT_WRITE 以防止調整大小的現有案例,並在這些案例中移除 ZX_RIGHT_RESIZE,或在移除 ZX_RIGHT_WRITE 的情況下移除 ZX_RIGHT_RESIZE。日後,zx_vmo_set_size() 只能檢查 ZX_RIGHT_RESIZE

成效

對參照項目執行的作業,就好像直接在父項 VMO 上執行一樣。這項功能可透過在核心中共用相同的內部網頁容器來實現。因此,使用參考值而非原始 VMO,對多數作業而言,應該不會造成任何可觀察到的效能影響。我們會編寫 VM 微基準測試來驗證這項資訊。

安全性考量

所有可在參照子項上執行的作業,都可以在父項 VMO 上執行,您必須使用相關的句柄才能建立參照。參照只是取代原始 VMO 的特定用途,因此即使沒有參照,也能執行這些作業。

測試

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

說明文件

zx_vmo_create_child() 系統呼叫說明文件將更新為 ZX_VMO_CHILD_REFERENCE。其他 VMO 系統呼叫的文件必須更新,以便在適用情況下納入 ZX_RIGHT_RESIZE

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

計算句柄

另一種做法是引入通用的控制代碼計數方案,當物件只剩一個控制代碼時,系統就會在物件上啟用信號。不過,VMO 句柄並未正確呈現未結參照項目的數量。您可以透過 VM 對應和串流,保留 VMO 參照,而無須保留用於建立 VMO 的句柄。我們也需要考量 VM 對應、串流,以及日後可能會保留 VMO 參照的任何新物件。這很快就會變得複雜,而且無法擴充。此外,句柄計數本質上是會發生錯誤的,因此不建議用於除偵錯以外的用途。

參考子項方法可更準確地擷取檔案系統與其用戶端之間的關係,這其實是一種階層關係。檔案系統對 VMO 的參照可視為主要參照,而它提供給用戶端的所有其他參照則為次要參照。父項/子項關係就很好地代表這一點。另一方面,句柄是對稱的;在這種情況下,我們不僅要確保只保留一個參照,也要確保該參照是檔案系統保留的參照。

參考符記

我們可以鑄造新的參照符記物件,除了 VMO 或串流,檔案系統也可以發出這類物件。這可以是核心提供的新物件,或是檔案系統實作的物件。不過,這需要變更檔案系統 API 介面,以便支援傳遞新的參照符記,但這可能無法實現。參照子項可與 VMOs 互換使用,因此在保留現有檔案系統 API 的同時,也能更輕鬆地支援這些 API。在 VMO 外部使用外部參照計數概念也容易發生錯誤,因為可能會不小心放棄權杖。

調整切片大小

參照子項與切片的唯一主要差異,就是是否能調整大小。因此,您可以改為在切片的上下文中定義調整大小的語意,而非建立新的 VMO 子項類型。不過,切片可跨越父項中的子範圍,因此要推斷大小調整作業會變得更加困難。

舉例來說,假設您將切片的大小調整為大於建立大小的尺寸,如果切片現在會揭露先前無法顯示的父項網頁,那麼就會令人驚訝。另一方面,切片並未直接擁有任何頁面,因此如果在擴充範圍內分支零個頁面,會顯得相當奇怪。如果將父項的大小調整為較大,也會發生類似問題。由於動機用途需要在父項和子項之間傳播大小調整,因此也需要調整任何切片大小,這可能會讓人感到意外。

調整右側大小

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

既有技術與參考資料

zx_vmo_create_child()

zx_vmar_map()

zx_stream_create()