RFC-0152:改善 OOM 處理行為

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

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

問題
Gerrit 變更
作者
審查人員
提交日期 (年-月-日)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、surajmalhotra@google.com

諮詢對象:adanis@google.com、alexlegg@google.com、geb@google.com、johngro@google.com、wez@google.com

Socialization:這個想法最初是在討論各種相關錯誤時提出。之後,我們向重要相關人員諮詢建議的解決方案。

背景

在 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 處理作業,但會關注低記憶體事件。RFC-0091 建立了 ZX_SYSTEM_EVENT_IMMINENT_OUT_OF_MEMORY 事件,當系統可用記憶體達到比 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 門檻稍高的程度時,就會由核心產生此事件。memory_monitor 會觀察這項信號,並嘗試將記憶體設定檔資料儲存到儲存空間。這是最佳努力做法,因為 memory_monitor 無法傳送訊號表示已處理事件,且系統隨時可能達到較低的門檻,在 memory_monitor 能夠清除資料之前,可能會關閉檔案系統。

設計

這項設計在概念上相當簡單,並使用許多現有系統元件,但以新方式組合。大致來說,這個策略是當核心偵測到記憶體不足時,會在記憶體中記錄一些狀態,並向使用者空間發出 OOM 信號,核心會等待使用者空間的逾時期限,然後再回呼至核心,使用者空間會接收信號,多個使用者空間元件會在有序關機時扮演已建立的角色,最後,使用者空間會回呼至核心,讓核心在 NVRAM 中儲存資料並完成重新啟動。

更詳細的順序如下:

  • 核心的記憶體監視器偵測到系統記憶體不足,因此會
    • 要求暫停權杖
    • 使用 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,並在退出前將 ZX_SYSTEM_POWERCTL_ACK_KERNEL_INITIATED_REBOOT 傳遞給 cmd 值。
  • 核心會接收系統呼叫,並發出停止符記信號。
  • 其餘的 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 會將此呼叫傳遞給 RebootReason。這個呼叫會啟動現有的使用者模式正常關機路徑,以相反的依附順序解構元件拓撲,並在結尾時透過 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 會直接執行系統呼叫。這份 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 更像是重新連線信號,而非從根本上改變系統行為。

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

替代方案:使用者空間處理常式位置

我們可以將 ZX_SYSTEM_EVENT_OUT_OF_MEMORY 的使用者空間處理常式放在多個位置。建議將處理程序放在 ZBI 中,並顯示在所有產品上,以便提早提供一致的處理體驗。主要的替代候選項目為 power_manager、shutdown-shim 和 component_manager。選擇 pwrbtn-monitor 的主要原因是,這個責任與其整體工作相符,也就是在事件發生時重新啟動系統,而 OOM 只是軟體產生的事件,而非硬體事件。

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

目前,Zircon 會在 OOM 期間將資料寫入永久性記憶體,而這份 RFC 則建議繼續採用這種做法。或者,我們可以在使用者空間觸發 zx_system_powerctl 呼叫以重新啟動系統時,將相同資料寫入持久性 RAM,無論核心是否偵測到 OOM 並向使用者空間發出 OOM 信號。如果我們執行這項操作,如果在 OOM 之後順利關閉使用者空間,則「意見回饋」元件會看到 Zircon 的 NO_CRASH 重新啟動原因。如果核心定時器到期,且 Zircon 在 OOM 後重新啟動系統,則「意見回饋」會看到 Zircon 的 OOM 重新啟動原因。

這種做法的缺點是,使用者空間處理 OOM 時發生問題,可能會導致系統知道已重新啟動,但不知道是 OOM 造成的。在這種情況下,Feedback 會發現 Zircon 有 NO_CRASH 重新啟動原因,但在磁碟上找不到任何持續性的當機資訊。在這種情況下,意見回饋仍會提交報告。

替代做法:在 OOM 重新啟動期間,允許相容要求傳送至 zx_system_powerctl

這份 RFC 建議,一旦核心啟動的 OOM 重新啟動程序開始,只有兩種情況會完成重新啟動:使用者空間以 cmdcmd 值呼叫 zx_system_powerctl,或核心的重新啟動計時器到期。ZX_SYSTEM_POWERCTL_ACK_KERNEL_INITIATED_REBOOT我們可以改為允許任何相容的呼叫至 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 的逾時期限到期為止。這場競賽的難度如何?這項問題相當輕微,因為使用者空間會自行清理。當根工作程式發生當機時,使用者空間已清理出預期的內容。最大的缺點是重新啟動速度會變慢。

另一種可能的競爭情況是,終止時重新啟動元件在錯誤的時間點中止。元件可以自行設定,如果退出 component_manager 會重新啟動系統。component_manager 會呼叫 fuchsia.hardware.power.statecontrol/Admin.Reboot 重新啟動系統。如果 component_manager 在收到拆除元件拓樸結構的指示後,退出一個「在結束時重新啟動」元件,component_manager 就不會嘗試重新啟動系統。如果此類「終止時重新啟動」元件在元件呼叫 Admin.Reboot 後退出,但在 power_manager 告知 component_manager 拆除拓樸之前,系統就會當機,因為如果 component_managerpower_manager 的呼叫失敗,就會發生恐慌。本 RFC 並未提出此競爭問題的修正方式。由於我們不知道 component_manager 發生恐慌時系統執行的程度,因此競爭可能會導致無法預測的行為。

未知:對記憶體完全耗盡的影響

這項 RFC 對系統在 OOM 處理期間完全耗盡記憶體的風險所造成的淨影響尚不明。由於大多數使用者空間元件不會監聽結束信號,且在用戶端一結束就會遭到終止,因此建議的變更可能會降低系統完全耗盡記憶體的機率。這應該會開始快速釋放記憶體。我們預期觀察到退出信號的使用者空間元件應立即退出,並釋放記憶體。建議的變更可能會增加記憶體耗盡的機率,因為某些系統的關機時間可能比現有的 8 秒逾時時間更長,因此某些元件會有更多時間來配置記憶體。此 RFC 也會延遲關閉目前快速關閉的檔案系統。檔案系統快取通常採用核心管理的捨棄式記憶體形式。目前尚不清楚系統執行較長時間的檔案系統是否會對記憶體造成顯著負面影響。