時鐘和定時器¶
arm64¶
在 arm64 上,Hyper-V 虛擬化了 ARMv8 架構系統計數器和定時器。Guest VM 使用此虛擬化硬體作為 Linux 時鐘源和時鐘事件,透過標準的 arm_arch_timer.c 驅動程式,就像在裸機上一樣。Linux vDSO 對架構系統計數器的支援在 Hyper-V 上的 Guest VM 中正常執行。雖然 Hyper-V 還提供了合成系統時鐘和四個合成的每 CPU 定時器,如 TLFS 中所述,但 Linux 核心在 arm64 上的 Hyper-V guest 中不使用它們。然而,早期版本的 arm64 Hyper-V 僅部分虛擬化 ARMv8 架構定時器,因此定時器不會在 VM 中生成中斷。由於此限制,在這些早期 Hyper-V 版本上運行當前 Linux 核心版本需要一個樹外補丁,以使用 Hyper-V 合成時鐘/定時器代替。
x86/x64¶
在 x86/x64 上,Hyper-V 為 Guest VM 提供了一個合成系統時鐘和四個合成的每 CPU 定時器,如 TLFS 中所述。Hyper-V 還透過 RDTSC 及相關指令提供對虛擬化 TSC 的訪問。這些 TSC 指令不會陷入到 hypervisor,因此在 VM 中提供了出色的效能。Hyper-V 執行 TSC 校準,並透過合成 MSR 將 TSC 頻率提供給 Guest VM。Linux 中的 Hyper-V 初始化程式碼讀取此 MSR 以獲取頻率,因此它會跳過 TSC 校準並設定 tsc_reliable。Hyper-V 提供了虛擬化的 PIT(僅在 Hyper-V 第 1 代 VM 中)、本地 APIC 定時器和 RTC。Hyper-V 不在 Guest VM 中提供虛擬化的 HPET。
可以透過合成 MSR 讀取 Hyper-V 合成系統時鐘,但此訪問會陷入到 hypervisor。作為一種更快的替代方案,guest 可以配置一個記憶體頁,使其在 guest 和 hypervisor 之間共享。Hyper-V 使用 64 位比例值和偏移值填充此記憶體頁。要讀取合成時鐘值,guest 讀取 TSC,然後應用 TLFS 中所述的比例和偏移。生成的價值以恆定的 10 MHz 頻率推進。如果即時遷移到具有不同 TSC 頻率的主機,Hyper-V 會調整共享頁面中的比例和偏移值,以便保持 10 MHz 頻率。
從 Windows Server 2022 Hyper-V 開始,Hyper-V 使用硬體支援 TSC 頻率縮放,以實現在 TSC 頻率可能不同的 Hyper-V 主機之間即時遷移 VM。當 Linux guest 檢測到此 Hyper-V 功能可用時,它更喜歡使用 Linux 的標準基於 TSC 的時鐘源。否則,它使用透過共享頁面實現的 Hyper-V 合成系統時鐘的時鐘源(標識為“hyperv_clocksource_tsc_page”)。
Hyper-V 合成系統時鐘可以透過 vDSO 提供給使用者空間,gettimeofday() 及相關係統呼叫可以完全在使用者空間中執行。vDSO 透過將具有比例和偏移值的共享頁面對映到使用者空間來實現。使用者空間程式碼執行相同的演算法,讀取 TSC 並應用比例和偏移以獲得恆定的 10 MHz 時鐘。
Linux 時鐘事件基於 Hyper-V 合成定時器 0 (stimer0)。雖然 Hyper-V 為每個 CPU 提供 4 個合成定時器,但 Linux 僅使用定時器 0。在早期版本的 Hyper-V 中,來自 stimer0 的中斷會導致 VMBus 控制訊息,該訊息由 vmbus_isr() 解複用,如 VMBus 文件中所述。在較新版本的 Hyper-V 中,stimer0 中斷可以對映到架構中斷,這被稱為“直接模式”。Linux 更喜歡在可用時使用直接模式。由於 x86/x64 不支援每 CPU 中斷,因此直接模式在所有 CPU 上靜態分配一個 x86 中斷向量 (HYPERV_STIMER0_VECTOR),並顯式編碼以呼叫 stimer0 中斷處理程式。因此,來自 stimer0 的中斷記錄在 /proc/interrupts 中的“HVS”行上,而不是與 Linux IRQ 相關聯。基於虛擬化 PIT 和本地 APIC 定時器的時鐘事件也可以工作,但首選 Hyper-V stimer0。
Hyper-V 合成系統時鐘和定時器的驅動程式是 drivers/clocksource/hyperv_timer.c。