休眠的客戶虛擬機器¶
背景¶
Linux 支援自身休眠以節省電量的功能。休眠有時被稱為“掛起到磁碟”,因為它會將記憶體映像寫入磁碟,並將硬體置於最低的功耗狀態。從休眠狀態恢復時,硬體會重新啟動,記憶體映像會從磁碟恢復,以便系統可以從中斷處繼續執行。請參閱《系統休眠狀態》中的“休眠”部分。
休眠通常在單使用者裝置上進行,例如個人筆記型電腦。例如,筆記型電腦在合上蓋子時進入休眠狀態,在再次開啟蓋子時恢復。休眠和恢復發生在相同的硬體上,負責協調休眠步驟的 Linux 核心程式碼假定在休眠狀態下硬體配置不會改變。
可以在 Linux 中透過向 /sys/power/state 寫入“disk”或呼叫帶有適當引數的 reboot 系統呼叫來啟動休眠。此功能可能由使用者空間命令(例如“systemctl hibernate”)封裝,這些命令可以直接從命令列執行,或響應諸如筆記型電腦蓋子關閉等事件。
客戶虛擬機器休眠的注意事項¶
Hyper-V 上的 Linux 客戶機也可以休眠,在這種情況下,硬體是 Hyper-V 為客戶虛擬機器提供的虛擬硬體。只有目標客戶虛擬機器進入休眠,而其他客戶虛擬機器和底層 Hyper-V 主機則繼續正常執行。雖然底層執行的 Windows Hyper-V 和物理硬體也可以使用 Windows 主機中的休眠功能進行休眠,但主機休眠及其對客戶虛擬機器的影響不屬於本文件的範圍。
恢復一個休眠的客戶虛擬機器比恢復物理硬體更具挑戰性,因為虛擬機器使得在休眠和恢復之間更改硬體配置變得非常容易。即使在休眠的同一虛擬機器上進行恢復,記憶體大小也可能已更改,或者可能已新增或移除虛擬網絡卡或 SCSI 控制器。分配給虛擬機器的虛擬 PCI 裝置可能已新增或移除。大多數此類更改會導致恢復步驟失敗,儘管新增新的虛擬網絡卡、SCSI 控制器或 vPCI 裝置應該可行。
額外複雜性可能隨之而來,因為休眠的虛擬機器的磁碟可以移動到另一個新建立的、具有相同虛擬硬體配置的虛擬機器上。雖然希望在此類移動後能成功從休眠中恢復,但仍存在挑戰。有關此場景及其限制的詳細資訊,請參閱下面的“在不同虛擬機器上恢復”部分。
Hyper-V 還提供了將虛擬機器從一個 Hyper-V 主機移動到另一個主機的方法。Hyper-V 嘗試使用 VM 配置版本來確保處理器型號和 Hyper-V 版本的相容性,並阻止移動到不相容的主機。Linux 透過在啟動時檢測主機和處理器差異來適應這些差異,但在休眠映像中恢復執行時不會進行此類檢測。如果虛擬機器在一個主機上休眠,然後在處理器型號或 Hyper-V 版本不同的主機上恢復,則休眠映像中記錄的設定可能與新主機不匹配。由於 Linux 在恢復休眠映像時不會檢測此類不匹配,因此可能導致未定義行為和失敗。
啟用客戶虛擬機器休眠¶
Hyper-V 客戶虛擬機器休眠預設是停用的,因為休眠與 Hyper-V 氣球驅動程式提供的記憶體熱新增功能不相容。如果使用了熱新增功能且虛擬機器休眠,則它會以比啟動時更多的記憶體量休眠。但當虛擬機器從休眠中恢復時,Hyper-V 只會向虛擬機器分配最初分配的記憶體,記憶體大小不匹配會導致恢復失敗。
要啟用 Hyper-V 虛擬機器進行休眠,Hyper-V 管理員必須在 Hyper-V 為客戶虛擬機器提供的 ACPI 配置中啟用 ACPI 虛擬 S4 睡眠狀態。此啟用操作透過修改虛擬機器的 WMI 屬性來完成,具體步驟超出了本文件的範圍,但可在網上查閱。此啟用被視為管理員優先考慮虛擬機器中的 Linux 休眠而非熱新增的指示,因此 Linux 中的 Hyper-V 氣球驅動程式會停用熱新增。如果 /sys/power/disk 的內容包含“platform”作為選項,則表示已啟用。此啟用在 /sys/bus/vmbus/hibernation 中也可見。請參閱函式 hv_is_hibernation_supported()。
Linux 在 x86 上支援 ACPI 睡眠狀態,但在 arm64 上不支援。
因此,在 Hyper-V 上,arm64 不支援 Linux 客戶虛擬機器休眠。¶
客戶虛擬機器可以使用標準 Linux 方法自行啟動休眠,即向 /sys/power/state 寫入“disk”或使用 reboot 系統呼叫。作為額外的一層,Hyper-V 上的 Linux 客戶機支援“關機”整合服務,透過該服務,Hyper-V 管理員可以使用虛擬機器外部的命令來指示 Linux 虛擬機器休眠。該命令會向 Linux 中的 Hyper-V 關機驅動程式生成一個請求,該驅動程式會發送 uevent “EVENT=hibernate”。請參閱核心函式 shutdown_onchannelcallback() 和 send_hibernate_uevent()。虛擬機器中必須提供一個 udev 規則來處理此事件並啟動休眠。
休眠和恢復期間處理 VMBus 裝置¶
VMBus 匯流排驅動程式以及各個 VMBus 裝置驅動程式都實現了掛起和恢復功能,這些功能作為 Linux 協調休眠和從休眠恢復過程的一部分被呼叫。總體方法是保留主 VMBus 通道及其相關 Linux 裝置(例如 SCSI 控制器等)的資料結構,以便它們被捕獲到休眠映像中。
這種方法允許與裝置相關的任何狀態在休眠/恢復過程中持久化。當虛擬機器恢復時,Hyper-V 會重新提供裝置,並將它們連線到已恢復的休眠映像中已存在的資料結構。
VMBus 裝置透過類別和例項 GUID 標識。(請參閱《VMBus》文件中“VMBus 裝置建立/刪除”一節。)從休眠中恢復時,恢復功能預期 Hyper-V 提供的裝置具有與休眠時存在的裝置相同的類別/例項 GUID。擁有相同的類別/例項 GUID 允許將提供的裝置與現在已恢復的休眠映像記憶體中的主 VMBus 通道資料結構進行匹配。如果提供的任何裝置與已存在的主 VMBus 通道資料結構不匹配,它們將作為新新增的裝置正常處理。如果恢復的休眠映像中存在的主 VMBus 通道與恢復的虛擬機器中提供的裝置不匹配,恢復序列會等待 10 秒,然後繼續進行。但未匹配的裝置很可能導致恢復的虛擬機器中出現錯誤。
恢復現有主 VMBus 通道時,新提供的 relid 可能不同,因為 relid 每次虛擬機器啟動時都可能改變,即使虛擬機器配置未發生變化。VMBus 匯流排驅動程式的恢復功能會匹配類別/例項 GUID,並在 relid 發生變化時進行更新。
VMBus 子通道不會持久化到休眠映像中。每個 VMBus 裝置驅動程式的掛起功能必須在休眠前關閉所有子通道。關閉子通道會導致 Hyper-V 傳送 RESCIND_CHANNELOFFER 訊息,Linux 會透過釋放通道資料結構來處理此訊息,從而移除子通道的所有痕跡。相比之下,主通道被標記為關閉,其環形緩衝區被釋放,但 Hyper-V 不傳送撤銷訊息,因此通道資料結構繼續存在。恢復時,裝置驅動程式的恢復功能會重新分配環形緩衝區並重新開啟現有通道。然後它會與 Hyper-V 通訊,從頭開始重新開啟子通道。
Hyper-V 套接字的 Linux 端在休眠時被強制關閉。客戶機無法強制關閉套接字的主機端,但主機端上的任何主機側操作都將產生錯誤。
VMBus 裝置在“凍結”和“斷電”階段使用相同的掛起功能,在“解凍”和“恢復”階段使用相同的恢復功能。有關這些階段的順序,請參閱《裝置電源管理基礎》中的“進入休眠”部分。
詳細休眠序列¶
Linux 電源管理 (PM) 子系統透過凍結使用者空間程序並分配記憶體以容納休眠映像來準備休眠。
作為“凍結”階段的一部分,Linux PM 依次呼叫每個 VMBus 裝置的“掛起”功能。如上所述,此功能會移除子通道,並將主通道置於關閉狀態。
Linux PM 呼叫 VMBus 匯流排的“掛起”功能,該功能會關閉所有 Hyper-V 套接字通道並解除安裝與 Hyper-V 主機的頂級 VMBus 連線。
Linux PM 停用非啟動 CPU,在之前分配的記憶體中建立休眠映像,然後重新啟用非啟動 CPU。休眠映像包含已關閉主通道的記憶體資料結構,但不包含子通道。
作為“解凍”階段的一部分,Linux PM 呼叫 VMBus 匯流排的“恢復”功能,該功能會重新建立頂級 VMBus 連線,並請求 Hyper-V 重新提供 VMBus 裝置。當主通道收到提供時,relid 會按前述方式更新。
Linux PM 呼叫每個 VMBus 裝置的“恢復”功能。每個裝置重新開啟其主通道,並酌情與 Hyper-V 通訊以重新建立子通道。子通道被重新建立為新通道,因為它們之前已在步驟 2 中完全移除。
VMBus 裝置現在再次工作,Linux PM 將休眠映像從記憶體寫入磁碟。
Linux PM 重複上述步驟 2 和 3,作為“斷電”階段的一部分。VMBus 通道關閉,頂級 VMBus 連線被解除安裝。
Linux PM 停用非啟動 CPU,然後進入 ACPI 睡眠狀態 S4。休眠現已完成。
詳細恢復序列¶
客戶虛擬機器啟動一個新的 Linux 作業系統例項。在啟動過程中,會建立頂級 VMBus 連線,並啟用合成裝置。這透過不涉及休眠的正常路徑發生。
Linux PM 休眠程式碼讀取交換空間,以查詢並將休眠映像讀入記憶體。如果沒有休眠映像,則此次啟動將成為正常啟動。
如果這是從休眠中恢復,則“凍結”階段用於關閉執行中的新作業系統例項中的 VMBus 裝置並解除安裝頂級 VMBus 連線,就像休眠序列中的步驟 2 和 3 一樣。
Linux PM 停用非啟動 CPU,並將控制權轉移到已讀入的休眠映像。在當前執行的休眠映像中,非啟動 CPU 會重新啟動。
作為“恢復”階段的一部分,Linux PM 重複休眠序列中的步驟 5 和 6。重新建立頂級 VMBus 連線,並接收提供並與映像中的主通道進行匹配。Relid 被更新。VMBus 裝置恢復功能重新開啟主通道並重新建立子通道。
Linux PM 退出休眠恢復序列,虛擬機器現在從休眠映像正常執行。
鍵值對 (KVP) 偽裝置異常¶
VMBus KVP 裝置的行為與 Hyper-V 提供的其他偽裝置不同。當 KVP 主通道關閉時,Hyper-V 會發送一個撤銷訊息,導致裝置的所有痕跡被移除。但 Hyper-V 隨後會重新提供該裝置,導致其被重新建立。移除和重新建立發生在休眠的“凍結”階段,因此休眠映像包含重新建立的 KVP 裝置。在恢復序列的“凍結”階段,當仍在新作業系統例項中時,也會發生類似的行為。但在兩種情況下,頂級 VMBus 連線隨後都會被解除安裝,這會導致裝置在 Hyper-V 端被丟棄。因此沒有造成損害,一切仍然正常工作。
虛擬 PCI 裝置¶
虛擬 PCI 裝置是物理 PCI 裝置,它們直接對映到虛擬機器的物理地址空間中,以便虛擬機器可以直接與硬體互動。vPCI 裝置包括透過 Hyper-V 稱為“獨立裝置分配”(DDA) 訪問的裝置,以及 SR-IOV 網絡卡虛擬功能 (VF) 裝置。請參閱《PCI 直通裝置》。
Hyper-V DDA 裝置在建立頂級 VMBus 連線後提供給客戶虛擬機器,就像 VMBus 合成裝置一樣。它們被靜態分配給虛擬機器,並且其例項 GUID 不會改變,除非 Hyper-V 管理員更改了配置。DDA 裝置在 Linux 中表示為虛擬 PCI 裝置,它們既有 VMBus 標識,也有 PCI 標識。因此,Linux 客戶機休眠首先將 DDA 裝置作為 VMBus 裝置處理,以管理 VMBus 通道。但隨後它們也作為 PCI 裝置處理,使用其原生 PCI 驅動程式實現的休眠功能。
SR-IOV 網絡卡 VF 也具有 VMBus 標識和 PCI 標識,總體處理方式與 DDA 裝置類似。一個區別是,在虛擬機器首次啟動時,VF 不會提供給虛擬機器。相反,VMBus 合成網絡卡驅動程式首先開始執行並告知 Hyper-V 已準備好接受 VF,然後才提供 VF。然而,VMBus 連線可能稍後被解除安裝,然後在不重啟虛擬機器的情況下重新建立,如上文《詳細休眠序列》中的步驟 3 和 5 以及《詳細恢復序列》中所示。在這種情況下,VF 很可能在初始啟動時就成為了虛擬機器的一部分,因此當 VMBus 連線重新建立時,VF 會在重新建立的連線上提供,無需合成網絡卡驅動程式介入。
UIO 裝置¶
VMBus 裝置可以使用 Hyper-V UIO 驅動程式 (uio_hv_generic.c) 暴露給使用者空間,以便使用者空間驅動程式可以控制和操作該裝置。然而,VMBus UIO 驅動程式不支援休眠所需的掛起和恢復操作。如果 VMBus 裝置配置為使用 UIO 驅動程式,則虛擬機器休眠會失敗,Linux 繼續正常執行。Hyper-V UIO 驅動程式最常見的用途是 DPDK 網路,但也有其他用途。
在不同虛擬機器上恢復¶
此場景發生在 Azure 公有云中,休眠的客戶虛擬機器僅以儲存的配置和磁碟形式存在——虛擬機器不再存在於任何 Hyper-V 主機上。當客戶虛擬機器恢復時,會建立一個配置相同的新 Hyper-V 虛擬機器,很可能在不同的 Hyper-V 主機上。該新 Hyper-V 虛擬機器成為恢復的客戶虛擬機器,Linux 核心從休眠映像恢復所採取的步驟必須在該新虛擬機器中起作用。
雖然磁碟及其內容從原始虛擬機器中保留下來,但 Hyper-V 提供的磁碟控制器和其他合成裝置的 VMBus 例項 GUID 通常會不同。這種差異會導致從休眠中恢復失敗,因此需要採取一些措施來解決這個問題
對於僅支援單個例項的 VMBus 合成裝置,Hyper-V 總是分配相同的例項 GUID。例如,Hyper-V 滑鼠、關機偽裝置、時間同步偽裝置等,無論是在本地 Hyper-V 安裝還是在 Azure 雲中,它們始終具有相同的例項 GUID。
VMBus 合成 SCSI 控制器在一個虛擬機器中可能存在多個例項,在一般情況下,例項 GUID 會因虛擬機器而異。然而,Azure 虛擬機器總是精確地擁有兩個合成 SCSI 控制器,並且 Azure 程式碼會覆蓋正常的 Hyper-V 行為,因此這些控制器總是被分配相同的兩個例項 GUID。因此,當客戶虛擬機器在新建立的虛擬機器上恢復時,例項 GUID 會匹配。但此保證不適用於本地 Hyper-V 安裝。
類似地,VMBus 合成網絡卡在一個虛擬機器中可能存在多個例項,並且例項 GUID 會因虛擬機器而異。同樣,Azure 程式碼會覆蓋正常的 Hyper-V 行為,以確保客戶虛擬機器中合成網絡卡的例項 GUID 不會改變,即使客戶虛擬機器被解除分配或休眠,然後在新建立的虛擬機器上重新組成。與 SCSI 控制器一樣,此行為不適用於本地 Hyper-V 安裝。
在新建虛擬機器上從休眠中恢復時,vPCI 裝置不具有相同的例項 GUID。因此,Azure 不支援對具有 DDA 裝置(如 NVMe 控制器或 GPU)的虛擬機器進行休眠。對於 SR-IOV 網絡卡 VF,Azure 在虛擬機器休眠前會從虛擬機器中移除 VF,以便休眠映像不包含 VF 裝置。當虛擬機器恢復時,它會例項化一個新的 VF,而不是嘗試匹配休眠映像中存在的 VF。因為 Azure 必須在啟動休眠之前移除所有 VF,所以 Azure 虛擬機器休眠必須從 Azure 門戶或 Azure CLI 外部啟動,這反過來又使用關機整合服務來指示 Linux 執行休眠。如果在 Azure 虛擬機器內部自行啟動休眠,則 VF 會保留在休眠映像中,並且無法正常恢復。