RFC-0209:記憶體優先順序設定檔

RFC-0209:記憶體優先順序設定檔
狀態已接受
區域
  • 核心
說明

使用設定檔為 VMAR 標示優先順序。

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)2022-08-16
審查日期 (年-月-日)2023-02-15

摘要

使用設定檔物件指示核心,允許將設定檔套用至 VMAR,藉此盡量減少可能會增加記憶體存取延遲的頁面錯誤和其他作業。

提振精神

Zircon 是超出承諾的系統,並採用不同的回收系統,嘗試支援這些應用程式。這些回收方法包括淘汰、頁面表回收、零頁面掃描等,可能會與截止日期工作相衝突,因為這些方法可能會在記憶體存取時增加難以接受的延遲時間。未來可能採用的回收方法 (例如網頁壓縮) 也會有問題。

音訊就是一個經常發生的例子,其中任何核心回收作業都會導致音訊執行緒中後續的頁面錯誤,進而導致音訊執行緒錯過期限。

目前的解決方案是使用核心中的特殊情況解決方法,只為音訊相關元件停用所有類型的回收功能。不過,這種做法非常不穩定,無法擴展至其他有類似需求的使用者。

本 RFC 旨在提供適合音訊的一般機制,以取代現有的臨時性解決方法,以及任何其他元件。

相關人員

協助人員:

jamesr@google.com

審查者:

eieio@google.com、rashaeqbal@google.com、tombegan@google.com

諮詢:

andresoportus@google.com

社會化:

我們已在文件中與 Zircon 團隊和核心演進工作小組 (KEWG) 討論過這個問題和提案。

API 設計

為讓使用者能控制回收作業,系統會以兩種方式擴充設定檔物件:

  1. 我們會新增 zx_profile_create 的額外標記 ZX_PROFILE_INFO_FLAG_MEMORY_PRIORITY,以及 memory_priority 欄位。
  2. 您可以透過 zx_object_set_profile 將設定檔套用至 VMAR。

一開始,系統只支援 memory_priority 欄位的兩個值:ZX_PRIORITY_DEFAULTZX_PRIORITY_HIGH,高值會永久停用所有聲明。Room 會在日後支援更多層級,以便在盡量減少延遲時間和避免 OOM 之間取得平衡。

當設定檔套用至 VMAR 時,假設該設定檔具有有效的 memory_priority,系統會立即將該設定檔套用至該 VMAR 及其所有子區域,並覆寫先前套用的任何設定檔。

核心必須遵循 ZX_PRIORITY_HIGH 設定,且必須在標記的 VMAR 中停用任何動態回收功能。

ZX_PRIORITY_DEFAULT 設定沒有特殊意義,也不需要遵守。特別是為了簡化實作,核心允許將 ZX_PRIORITY_HIGH 要求擴展至標示為 ZX_PRIORITY_DEFAULT 的更廣範圍。

雖然系統一律允許重複套用,但如果位址空間從擁有某些 ZX_PRIORITY_HIGH VMAR 變成沒有任何 VMAR,則核心應會返回與未套用任何設定檔時相同的狀態。

資訊查詢

zx_object_get_infoZX_INFO_KMEM_STATS_EXTENDED 主題將擴充為回報額外欄位:

    // The amount of memory in VMOs that would otherwise be tracked for
    // reclamation, but has had reclamation disabled.
    uint64_t vmo_reclaim_disabled_bytes;

元件用法

使用者空間元件通常不會直接使用 zx_profile_create 等,而是會叫用 ProfileProvider 服務。在此,ProfileProviderSetProfileByRole 方法會放寬,以便接受任意句柄,而非僅限於執行緒。

核心設計

本節說明如何變更核心中的物件,以便納入設定檔中的資訊。目標是確保系統的任何部分,如果需要知道記憶體優先順序才能做出決定,就能有效存取記憶體優先順序。在可行情況下,這個設計會假設設定檔應用程式相較於其他作業較少,因此偏好在設定檔應用程式點執行作業。

轉換為布林值

回收作業涉及的核心物件如下:

  • VmAspace:控制網頁資料表對應作業,並透過此處進行網頁資料表回收作業。
  • VmAddressRegion - 目前未參與回收作業,但所有網頁表對應項目的建立作業都是透過這個物件進行。
  • VmObject:任何淘汰或日後的網頁回收策略都會經過 VmObject

除了 VmAddressRegion (也就是套用設定檔的物件) 之外,這些物件中的每個子範圍都能查詢該範圍內的 VMAR 和套用的記憶體優先順序。

如果有多個 VMAR 套用至 VMO 區域,且優先順序不同,您可以採用套用最高優先順序的 VMAR 來解決這個問題。

為避免在所有 VMAR 中重複執行長時間搜尋,物件需要有效率的方式,瞭解是否有任何記憶體優先順序適用於這些物件。為了簡化追蹤,初步建議的實作方式會將任何優先範圍升級為完整物件。也就是說,如果設定檔參照 VmObject 的任何部分,整個物件都會被視為具有該設定檔。

這兩種簡化實作方式可讓記憶體設定檔查詢更有效率,只需要透過物件連結傳播布林值的聯集。

傳播

這項復原停用布林值的傳播方式,是根據邊緣轉換和計數而定。

VMAR 設定了設定檔後,您需要考量三種結果:

  1. 這個 VMAR 的聲明程序維持不變。
  2. 從停用轉為啟用。
  3. 從啟用轉為停用。

在第一種情況下,不會發生直接傳播,但仍必須遍歷所有子區域,並將設定檔套用至這些子區域。由於子區域已套用需要覆寫的不同設定檔,因此必須執行這項無條件遍歷作業。

在兩種轉場中,都需要更新潛在參照物件 VmAspaceVmObject

VmAspaceVmObject 物件將會包含計數器,用於計算參照這些物件的物件有多少個已停用回收功能。因此,判斷這些物件是否已停用回收功能,只需比較計數與零即可。

VmObject 可能有其他 VmObject 父項,需要將旗標傳播至這些父項。由於回收是由計數器是否為零控制,因此當計數器從或到零時,就會發生傳播。

這項參照計數傳播策略可確保設定檔變更盡可能有效率,且與只追蹤布林值相比,幾乎沒有額外負擔。

VmObject 轉換

除了套用回收標記,VmObject 還需要執行動作,以便在轉換期間更新其頁面。

停用回收功能後,所有原本可回收的頁面都會移至個別的頁面佇列。將網頁加入這個佇列,即可避免索回,並提供網頁計數方式。

同樣地,啟用回收功能後,頁面必須移回預設佇列,才能再次成為回收候選項目。當這些網頁放回預設佇列時,系統會將這些網頁的 age 視為何種狀態,這屬於實作細節。在沒有硬體存取標記的平台上,無法取得任何年齡資訊,因此必須自行設定年齡。

實作

實作作業會在一系列步驟中完成,從核心實作到後續的 API 層級。

核心實作

可以完全實作核心物件變更的設計,以便新增支援功能,在不變更任何行為的情況下設定記憶體優先順序。這項作業會在多個 CL 中執行,並透過核心單元測試進行測試。

核心 API 異動

ZX_INFO_KMEM_STATS_EXTENDED 查詢需要相當特權的系統資源,且只有少數用途,全部都在樹狀結構中。因此,這項查詢可以在單一 CL 中修改,而不需要多階段結構演進。

更新設定檔 API 和說明文件,並將設定檔系統呼叫連結至先前實作的核心支援。zx_profile_create 系統呼叫及其相關聯的設定結構體 zx_profile_info_t 是具有特權的系統呼叫,因此同樣可以在單一 CL 中修改。

ProfileProvider 異動

擴充 ProfileProvider 的實作方式,以支援在 .profiles 中指定記憶體優先順序的方式。

變更 ProfileProvider FIDL API,以便使用任意句柄,而非僅限於執行緒。由於這是 API 的放寬版本,因此不會破壞任何回溯相容性。

媒體遷移

媒體相關元件的相關設定檔會變更為包含 ZX_PRIORITY_HIGHmemory_priority,而任何媒體元件都會將這些設定檔套用至其根 VMAR,方法與套用至執行緒的方式完全相同。

確認設定檔運作正常後,即可移除現有的硬式編碼核心解決方法。

成效

建議的核心設計與核心中使用的臨時解決方法非常相似,但目前的臨時方法無法取消套用,且對所有相關的 VMAR 和 VMOs 而言是永久性的。因此,從這個方法切換至設定檔時,在功能和核心行為 (包括 CPU 和記憶體使用情形) 方面,應該會完全沒有任何動作。

安全性考量

使用設定檔,以及設定記憶體優先順序的能力,都需要根工作處理常數。因此,任何與阻斷服務攻擊相關的安全考量,都等同於 ProfileProvider 的現有阻斷服務可能性。

測試

大部分的測試可以著重於各自的核心和設定檔提供者實作項目的單元測試,並搭配一些整合測試來驗證完整的使用途徑。

說明文件

我們會更新設定檔物件、相關系統呼叫和 FIDL 通訊協定的相關說明文件。

替代方案

VMAR 中的資源或等同資源

您可以直接在 VMAR 物件上設定屬性或等效項目,用來表示優先順序,而非使用設定檔物件。這樣一來,您就不必擴充排程器物件,也能簡化使用者空間的使用方式,而且不需要使用 ProfileProvider

使用這種方法時,無法透過內建方式限制設定優先順序的功能。雖然任何元件都能分配任意記憶體並執行拒絕服務,但這不一定是理想做法,而且為了提升效能而停用回收功能可能會引人心動,進而導致常見的悲劇情境。

我們可以設計某種形式的專屬存取控制機制來解決這個問題,但現在已移除使用設定檔的優點。

透過污染推論

除了由使用者空間直接標記之外,您也可以假設任何期限執行緒都需要期限記憶體存取,並將其存取的任何記憶體標示為高優先順序。這項作業不需要變更使用者空間,但需要進行過度標記,也就是每個位址空間至少有一個截止日期執行緒,且所有對應項目都已標記,或是在截止日期執行緒使用時,對應項目 / VMO 會標記。

過度標記是不好的做法,因為並非所有截止時間執行緒都具有相同的記憶體延遲要求,也並非適用於所有位址空間。延遲污染表示無法預先發生錯誤,因此在最糟的情況下,期限執行緒可能會在發生錯誤項目時,一律錯過期限。

使用延遲標記方案時,也無法確定項目如何取消標記。

將 memory_priority 套用至執行緒

當設定檔套用至執行緒時,可以解讀 memory_priority 欄位,而不需要將設定檔套用至 VMAR,並將優先順序套用至其根 VMAR。

雖然這可簡化 ProfileProvider 通訊協定和系統呼叫介面,但會讓使用者和核心在日後無法選擇更有效率的做法。高效的元件可將延遲時間敏感的重要資料,安排在其位址空間的一個子區域中,將非重要資料安排在另一個區域中,並只將設定檔套用至重要區域。這可讓非關鍵資料仍可考慮回收,進而提升記憶體使用率。

單一優先順序欄位

您可以重複使用相同的 priority 欄位來設定執行緒優先順序,而不需要另外導入 memory_priority 欄位。這在理論上可節省簡單設定結構的成本,但如果您想要使用不同的記憶體和排程器優先順序,現在必須建立不同的設定檔物件。

擴充 ALWAYS_NEED 提示

現有的 API 可在 VMO 或 VMAR 上使用 ALWAYS_NEEDDONT_NEED 提示,藉此控制回收作業。目前這些提示僅適用於 pager 支援的 VMOs,但可擴充至匿名 VMOs。

只要將語意擴展至涵蓋匿名 VMOs,就會出現一些差距:

  • ALWAYS_NEED 只是提示,並不能保證不會發生回收問題。
  • 由於系統仍會追蹤年齡,因此網頁可能仍需要存取錯誤。
  • 不會停用頁面表重新整理功能。
  • 僅適用於現有的對應項目。

這些限制都可以透過類似於主要提案中所述的內部實作方式解決,但 API 本身有兩個基本問題。

主要提案提供了明確的方式,讓您知道何時重新啟用回收功能,方法是將設定檔套用至所有 VMAR (或僅限根 VMAR)。由於 DONT_NEEDALWAYS_NEED 更強,因此在使用提示時,無法移除 ALWAYS_NEED

提示 API 的動機之一是提示,而不是承諾,這是因為缺乏存取權控管,且可用於任何 VMAR 或 VMO。您可以使用工作政策或類似機制來控制提示的運作方式,但也需要設計這類機制。