RFC-0152:改善 OOM 處理行為

RFC-0152:改善 OOM 處理行為
狀態已接受
區域
  • 驅動程式
  • 核心
  • 功率
說明

允許更多使用者空間程式碼退出,並為使用者空間建立信號路徑,以便在使用者空間清理完成時通知核心,改善記憶體處理方式。

問題
變更
作者
審查人員
提交日期 (年/月)2021-12-20
審查日期 (年/月)2022-02-09

摘要

這項 RFC 的目標是提高擷取偵錯資料的可靠性,並在記憶體不足 (「OOM」) 事件期間,盡量減少產生沒有幫助的資料。方法是依序關閉更多的使用者空間,而非現在使用的基本機制。

提振精神

在系統記憶體不足時收集資料,有助於找出事件的根本原因。我們目前會收集一些資料 (例如記憶體報告),但無法保證記錄可以協助我們進一步掌握系統發生的活動。如今收集所有相關資料並不容易,因為系統只有少數部分知道系統記憶體用盡時。此外,在 OOM 之後分析可用資料並不容易。OOM 的處理方式經常導致次要效果串聯,為資料增加雜訊。而這也導致許多人因混淆、冗長的討論和不實結論而浪費時間。

相關人員

講師:hjfreyer@google.com

審查人員:dgilhooley@google.com、frousseau@google.com、maniscalco@google.com、palmer@google.com、pankhurst@google.com、ppi@google.com、pshickel@google.com、rashaeqbal@google.com, shayba@google.com, sura

諮詢:adanis@google.com、alexlegg@google.com、geb@google.com、johngro@google.com、Wez@google.com

社交化:一開始在各種相關錯誤中討論過這個想法。等主要利害關係人針對建議解決方案獲得諮詢後。

背景

處理 Fuchsia 記憶體不足 (OOM) 條件的程序如下:

  1. 如果可用記憶體持續減少,記憶體監控計時器會偵測系統的可用記憶體達到 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 門檻。核心現已承諾重新啟動系統。
  2. 記憶體監控計時器會針對使用者空間產生 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 信號,並聲明「停止權杖」停止權杖的用途是防止核心中的多項功能嘗試並行重新啟動。
  3. 記憶體監控計時器重新啟動後,會休眠 8 秒。您無法使用使用者空間向核心表明其已準備好重新啟動。
  4. driver_manager 會觀察 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 信號。driver_manager 啟動時,系統會透過 system_get_event 系統呼叫訂閱信號 (這個呼叫需要根工作的控制代碼)。
  5. driver_manager 會指示 fshost 關閉,並通知所有驅動程式停止作業。系統會盡量減少遺失的資料,並在重新啟動前讓硬體維持一致狀態。
  6. driver_managerfshost 之外,使用者空間會持續執行,直到記憶體監控計時器的計時器到期為止。在此期間,通常會遇到各種當機情形,因為程式會嘗試存取已分頁且無法分頁,因為檔案系統已不存在。這些當機問題會產生大量的雜訊,並且在監聽序列記錄時記錄。

目前的 OOM 處理作業並未使用,但已有一個方式可妥善拆解使用者空間。這種機制會以反向依附元件順序,停止包括檔案系統和驅動程式等元件,並讓所有元件都能清理資料。

memory_monitor 不會參與 OOM 處理作業,但會對記憶體不足事件感興趣。當系統可用記憶體達到比 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 門檻更高的等級時,RFC-0091 就會建立 ZX_SYSTEM_EVENT_IMMINENT_OUT_OF_MEMORY 事件。memory_monitor 會觀察這個信號,並嘗試將記憶體設定檔資料保存到儲存空間。此為最佳做法,因為 memory_monitor 無法表示處理該事件,且系統可能會隨時達到較低門檻,因此可能會在 memory_monitor 清除其資料前關閉檔案系統。

設計

這項設計在概念上相當簡單,並採用大量現有的系統部分,但以全新方式編寫。大致上來說,核心的策略是:核心偵測到已經用盡記憶體,將記憶體記錄到記憶體中,向使用者空間發出關於 OOM 的信號。核心會等待使用者空間呼叫核心,然後使用者空間收到信號,多個使用者空間元件會依序關閉已建立的重新啟動角色,最後再把使用者空間呼叫存回核心,讓核心得以恢復至核心。

詳細流程如下:

  • 核心的記憶體監控計時器偵測到系統記憶體不足,
    • 索取停止權杖
    • 利用 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 信號使用者空間
    • 設定計時器
  • 使用者空間信號是由 pwrbtn-monitor 接收,並會透過 fuchsia.hardware.power.statecontrol/Admin.Reboot 呼叫與 power_manager 通訊。
  • 接著,power_manager 會透過 fuchsia.device.manager/SystemStateTransition.SetTerminationSystemState 呼叫告知 driver_manager,後者應在結束之前將系統移至 REBOOT_KERNEL_INITIATED 電源狀態。
  • power_manager 會呼叫 fuchsia.sys2/SystemController.Shutdown 呼叫,要求 component_manager 拆解元件拓撲。
  • component_manager 會以反向依附元件順序拆散元件拓撲,最終要求 driver_manager 結束。
  • driver_manager 會發現在退出前應執行轉場至 REBOOT_KERNEL_INITIATED 狀態。
  • driver_manager 會呼叫 zx_system_powerctl,並在結束之前為 cmd 值傳遞 ZX_SYSTEM_POWERCTL_ACK_KERNEL_INITIATED_REBOOT
  • 核心收到系統呼叫,並發出停止權杖。
  • 其餘 OOM 處理作業會執行,讓適當的資訊寫入 NVRAM,以便在重新啟動後讀取。
  • 核心會重新啟動系統。

實作

控制核心 OOM 逾時

這個 RFC 建議新增核心啟動選項來控制 OOM 逾時。目前 OOM 逾時在 8 秒時採用硬式編碼。實作此 RFC 會增加為了處理 OOM 而執行的程式碼數量,進而保證較大的逾時時間。

暫停權杖變更

這個 RFC 提議將暫停符記改為納入核心事件物件,而非單純的布林值。該停止權杖會繼續屬於不可撤銷的物件。系統會在核心內使用該權杖的事件物件來協調重新啟動作業。暫停權杖可讓事件物件無需接收權杖就能發出信號。

OOM 處理流程

目前,當核心偵測到 OOM 時,會產生使用者空間的 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 信號、取得暫停權杖,並啟動 8 秒計時器。這個 RFC 建議,每當核心想要重新啟動系統,也讓使用者空間有機會執行某個操作時,核心應先取得停止權杖,然後通知使用者空間,並等待一些受限時間。等待時間應等於使用者模式允許執行回應事件的時間上限。這與目前的實作不同,其逾時值為最大等待期「和」最短等待期。一旦收到停止權杖的事件信號或逾時,就會完成重新啟動作業。如果是 OOM,核心程式碼決定重新啟動後,就會存放在記憶體監控計時器中,此時系統會建立 OOM 當機記錄,將其儲存在 NVRAM 中並重新啟動,藉此完成 OOM 處理作業。

如先前所述,這個 RFC 提議新增透過核心啟動選項設定 OOM 逾時的功能。

目前使用者空間 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 處理常式位於 driver_manager。這個 RFC 建議將處理常式移至 pwrbtn-monitorpwrbtn-monitor 是現有元件,會出現在所有版本中,用於控制某些硬體的電源狀態。實際上,我們可以將 OOM 視為軟體產生的電源按鈕按下。有鑑於 pwrbtn-monitor 的責任增加,我們建議將其重新命名為 system-event-monitor

pwrbtn-monitor 收到信號後,會呼叫 fuchsia.hardware.power.statecontrol/Admin.Reboot。我們會為 OOM 新增 RebootReasonpwrbtn-monitor 會傳遞此呼叫。呼叫會啟動現有的使用者模式安全關閉路徑,以反向依附元件順序解構元件拓撲,並於 driver_manager 變更硬體電源狀態時結束。這個 RFC 假設在提供 OOM driver_manager 時,應一律使用使用 zx_system_powerctl 系統呼叫的重新啟動路徑,並傳遞新的 ZX_SYSTEM_POWERCTL_ACK_KERNEL_INITIATED_REBOOT 值做為 cmd 引數的值。Driver_manager 中的現有路徑會使用 zx_system_powerctl。在 x86 上,當 driver_manager 將重新啟動作業委派給主面板驅動程式庫,而主面板驅動程式庫就會發出系統呼叫時,就會發生這個情況。在 arm64 Driver_manager 直接執行 syscall。這個 RFC 中的變更正式需要在重新啟動路徑中執行 zx_system_powerctl

Zircon 收到 zx_system_powerctl (cmd 值為 ZX_SYSTEM_POWERCTL_ACK_KERNEL_INITIATED_REBOOT) 時,處理常式程式碼會嘗試發出停止權杖。如果未聲明停止憑證,表示失敗且系統呼叫會傳回錯誤。至於其他 cmd 值,處理常式程式碼會保持不變,具體來說,系統會嘗試接收停止權杖,如果無法這麼做,則會永久休眠。如果是因 OOM 而由核心啟動的重新啟動,如果發出信號,記憶體監控計時器將可讓記憶體監控計時器完成其工作並重新啟動系統。如果在記憶體監控計時器的逾時到期之前,使用者空間未呼叫 zx_system_powerctl,監控計時器會繼續執行關閉程序並重新啟動系統,而目前的實作方式維持不變。

效能

我們預期在某些情況下,OOM 處理作業所需時間會比目前更久。目前,在 OOM driver_manager 期間,系統會停止檔案系統,停止驅動程式,然後執行其他動作。核心偵測到 OOM 的 8 秒後,系統會重新啟動系統。

實作此 RFC 大量使用者空間後,有機會對系統即將重新啟動的情況做出回應。具體來說,power_manager 會透過 RebootWatcher 通訊協定通知事件監聽器,說明即將重新啟動。power_manager 的用戶端回應逾時為 5 秒。power_manager 通知重新啟動觀察器後,會指示 component_manager 拆除元件拓撲。元件拓撲會以反向依附元件的順序破壞,意味著並非所有元件都會同時停止。元件有停止的逾時期限。導入這個 RFC 後,有更多程式碼有機會在 OOM 中執行,而且可能會遇到各種逾時。這些因素可能會導致重新啟動時間超過 8 秒,不過在現今的多數系統中,這個流程只需不到 8 秒。

此 RFC 不會嘗試解決在核心信號 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 之後配置更多記憶體的問題。

回溯相容性

這項作業不會有回溯相容性問題,因此可透過軟轉換的方式進行變更。

安全性考量

這個 RFC 建議將 zx_system_get_event 系統呼叫從 driver_manager 移至 pwrbtn-monitor。這個系統呼叫需要根工作的控制代碼,此為高度敏感的控制代碼。pwrbtn-monitor 是一個聚焦的小型元件,已可透過 fuchsia.hardware.power.statecontrol/Admin 能力控制系統電源狀態。增加根工作的存取權會增加這個元件的權限。

此 RFC 也提議延長重新啟動逾時。只有在我們認為 OOM 屬於攻擊向量時,才會是問題,而重新啟動逾時會讓攻擊者有更多時間執行漏洞攻擊。

測試

當使用者空間在核心逾時前關閉或未正常關閉時,使用者空間都必須經過測試,才能驗證系統可正常重新啟動。如果這些測試不存在,將會新增這些測試。

我們也可能希望進行效能測試,分析使用者空間拆除所需要的時間。您可以使用這些剖析測試來告知核心逾時值。

說明文件

您應該更新各種 API 說明文件,但不必更新概念,因為這個 RFC 比從根本改變系統行為徹底改變的信號更為有效。

缺點、替代方案和未知

替代方法:Userspace 處理常式位置

有很多選項可以放置 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 的使用者空間處理常式。建議您將處理常式置於 ZBI 中,並顯示在所有產品上,以便盡早且一致地提供處理體驗。主要的替代候選項目為 power_manager、sil-shim 和 component_manager。選擇 pwrbtn-monitor 的主要原因是,此責任符合自行重新啟動系統以回應事件的整體工作,OOM 僅是軟體產生的事件,而非硬體事件。

替代方法:針對使用者空間啟動的所有重新啟動回報 NO_CRASH

目前 Zircon 會在 OOM 期間將資料寫入永久記憶體,而這個 RFC 建議繼續執行這項做法。另一個替代方案是,我們可以在每次使用者空間觸發呼叫zx_system_powerctl 重新啟動系統時,將相同的資料寫入永久 RAM 以重新啟動系統,無論核心是否偵測到 OOM 並向使用者傳送有關 OOM 的信號。如果我們執行上述做法,當 OOM 成功後關閉使用者空間後,「意見回饋」元件就會顯示 Zircon 的 NO_CRASH 重新啟動原因。如果核心計時器過期,且 Zircon 在 OOM 後重新啟動系統,則 Feedback 會看到 Zircon 的 OOM 重新啟動原因。

這種方法的缺點是,在處理 OOM 時出現使用者空間問題時,系統可能會知道應用程式已重新啟動,但不知道原因是 OOM 所致。在這個案例中,意見回饋會指出 Zircon 有 NO_CRASH 重新啟動的原因,但在磁碟上找不到永久當機資訊。在這種情況下,意見回饋仍會提交報告。

替代做法:允許在 OOM 重新啟動期間向 zx_system_powerctl 發出相容要求

這個 RFC 建議,在核心啟動的 OOM 重新啟動後,只會啟動其中兩項工作完成重新啟動:使用者空間呼叫 cmd 值為 ZX_SYSTEM_POWERCTL_ACK_KERNEL_INITIATED_REBOOTzx_system_powerctl,或核心的重新啟動計時器過期。或者,我們可以允許任何相容的呼叫 zx_system_powerctl 來完成重新啟動作業。無論傳遞的 cmd 值為何,相容的呼叫也會重新啟動系統。這樣做可以解決在使用者空間發出要求 OOM 條件之前,使用者空間獨立決定重新啟動系統等情況。核心或許會根據 zx_system_powerctl 呼叫中實際收到的 cmd 值,撰寫不同的重新啟動原因。這麼做可讓系統稽核系統是否通常會遵循預期的重新啟動路徑。

替代方法:透過核心物件傳送信號使用者空間處理完成

此 RFC 建議以特定 cmd 值呼叫 zx_system_powerctl 完成核心基因組的 OOM 重新啟動作業。您必須改為變更核心對使用者空間的信號機制,讓使用者空間能夠接收管道或事件物件。然後,使用者空間可以傳送訊息或斷言/宣告信號,指出可以繼續重新啟動。此替代方法的優點在於完成重新啟動的元件不需要存取根資源。您必須具備根資源的存取權,才能使用「zx_system_powerctl」。這個替代做法需要執行更多作業,因為目前的核心/使用者空間信號功能運作方式更為重大。

缺點: 有些競賽可能會更加艱難

目前,核心可能會偵測 OOM 條件並領取停止權杖,然後使用者空間呼叫 zx_system_powerctl,因為使用者空間先前已決定重新啟動。在此情況下,呼叫 zx_system_powerctl 將會失敗。driver_manager後再離開。component_manager 會繼續中斷元件拓撲,最終導致系統終止 power_manager。終止 power_manager 會導致根工作異常終止,因為 power_manager 已設為根工作的關鍵。在根工作失敗時,Zircon 通常會重新啟動系統,但由於 MemoryWatchdog 會保留停止權杖,因此不會重新啟動。系統最終會達到 MemoryWatchdog 的逾時時間,並重新啟動系統。

這項 RFC 允許類似的競賽。發生 OOM 條件時,使用者空間可能為了準備重新啟動而終止程序。可能表示 pwrbtn-monitor 已結束,這表示使用者空間中沒有任何內容來觀察核心的 OOM 信號。或者,pwrbtn-monitor 可能已執行,但由於 power_manager 僅允許一項執行中的要求關閉或重新啟動系統,因此嘗試重新啟動系統將會失敗。早期拆解要求最終會得到 driver_managerdriver_managerzx_system_powerctl 要求會如前所述失敗,根工作也會當機,但系統在 MemoryWatchdog 逾時到期前不會重新啟動。這個種族的處境有多大?使用者空間會自行清理,實在是很困難的。根工作異常終止時,使用者空間已經按照預期的方式清除。最大缺點是重新啟動較少的提示

另一個可能的競爭是,如果「reboot-on-terminate」元件在錯誤的時間結束時退出,元件可自行設定,以便在退出 component_manager 後重新啟動系統。component_manager 會呼叫 fuchsia.hardware.power.statecontrol/Admin.Reboot 來重新啟動系統。如果在收到 component_manager 通知拆除元件拓撲後,重新啟動後終止的元件退出,component_manager 就不會嘗試重新啟動系統。如果這類在元件呼叫 Admin.Reboot 後重新啟動後終止的元件結束,但在 power_manager 之前通知 component_manager 拆解拓撲時,如果呼叫 power_manager 失敗,系統會當機,因為 component_manager 恐慌。這個 RFC 並未建議修正這個問題。競賽可能會導致無法預測的行為,因為我們無法在發生 component_manager 恐慌時,瞭解系統正在執行多少作業。

不明:對記憶體完全用盡造成的影響

這項 RFC 在 OOM 處理期間完全耗盡記憶體的風險,會造成淨影響。提議的變更可能會降低系統完全耗盡記憶體的能力,因為大部分的使用者空間元件不會監聽結束信號,並在用戶端退出後立即終止。這應該很快就會開始釋出記憶體。我們預期會觀察結束信號的使用者空間元件應立即結束,並釋放記憶體。提議的變更可能會增加記憶體耗盡的機率,因為有些系統的關閉時間可能會比現有系統更長的逾時時間 (8 秒的逾時時間),因此讓某些元件有更多時間分配記憶體。此 RFC 也會延遲關閉檔案系統,因此現在很快會結束。檔案系統快取通常會採用由核心管理的可捨棄記憶體形式。我們無法確定執行時間較長的檔案系統是否會對記憶體造成重大負面影響。