diff --git a/hypervisor/src/kvm/mod.rs b/hypervisor/src/kvm/mod.rs index e61987ea7..e4e2c4264 100644 --- a/hypervisor/src/kvm/mod.rs +++ b/hypervisor/src/kvm/mod.rs @@ -78,7 +78,7 @@ use x86_64::check_required_kvm_extensions; pub use x86_64::{CpuId, ExtendedControlRegisters, MsrEntries, VcpuKvmState}; #[cfg(target_arch = "x86_64")] -use crate::ClockData; +use crate::{ClockData, ClockState}; #[cfg(target_arch = "x86_64")] use crate::arch::x86::{ CpuIdEntry, FpuState, LapicState, MTRR_MSR_INDICES, MsrEntry, NUM_IOAPIC_PINS, @@ -1264,6 +1264,18 @@ impl vm::Vm for KvmVm { .map_err(|e| vm::HypervisorVmError::SetClock(e.into())) } + /// Capture kvmclock (filling realtime) for snapshot/migration. + #[cfg(target_arch = "x86_64")] + fn snapshot_clock(&self) -> vm::Result> { + Ok(Some(self.get_clock()?.with_realtime_filled())) + } + + /// Restore kvmclock before the vCPUs resume. + #[cfg(target_arch = "x86_64")] + fn restore_clock(&self, state: &ClockState) -> vm::Result<()> { + self.set_clock(state) + } + /// Create a device that is used for passthrough fn create_passthrough_device(&self) -> vm::Result { let mut vfio_dev = kvm_create_device { diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 299a32390..eb4ca6bac 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -49,6 +49,8 @@ mod cpu; mod device; use std::sync::Arc; +#[cfg(target_arch = "x86_64")] +use std::time::SystemTime; use anyhow::anyhow; use concat_idents::concat_idents; @@ -196,7 +198,7 @@ impl ClockData { } } - pub fn set_realtime(&mut self, realtime: std::time::SystemTime) { + pub fn set_realtime(&mut self, realtime: SystemTime) { match self { #[cfg(feature = "kvm")] ClockData::Kvm(s) => { @@ -211,8 +213,23 @@ impl ClockData { } } } + + /// Returns the clock with `CLOCK_REALTIME` filled from the host wall clock + /// when absent, so a later restore can advance the guest to current wall + /// time. No-op for backends without a realtime field (e.g. MSHV). + pub fn with_realtime_filled(mut self) -> Self { + if !self.has_realtime() { + self.set_realtime(SystemTime::now()); + } + self + } } +/// Guest clock state preserved across pause/resume and snapshot/restore +/// (`ClockData` on x86). +#[cfg(target_arch = "x86_64")] +pub type ClockState = ClockData; + #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] pub struct HypervisorVmConfig { #[cfg(feature = "tdx")] diff --git a/hypervisor/src/mshv/mod.rs b/hypervisor/src/mshv/mod.rs index 858317705..74f0029d0 100644 --- a/hypervisor/src/mshv/mod.rs +++ b/hypervisor/src/mshv/mod.rs @@ -78,7 +78,7 @@ pub use { }; #[cfg(target_arch = "x86_64")] -use crate::ClockData; +use crate::{ClockData, ClockState}; #[cfg(target_arch = "aarch64")] use crate::arch::aarch64::gic::{Vgic, VgicConfig}; #[cfg(target_arch = "aarch64")] @@ -2200,6 +2200,18 @@ impl vm::Vm for MshvVm { .map_err(|e| vm::HypervisorVmError::SetClock(e.into())) } + /// Capture the partition reference time for snapshot/migration. + #[cfg(target_arch = "x86_64")] + fn snapshot_clock(&self) -> vm::Result> { + Ok(Some(self.get_clock()?.with_realtime_filled())) + } + + /// Restore the partition reference time before the vCPUs resume. + #[cfg(target_arch = "x86_64")] + fn restore_clock(&self, state: &ClockState) -> vm::Result<()> { + self.set_clock(state) + } + /// Downcast to the underlying MshvVm type fn as_any(&self) -> &dyn Any { self diff --git a/hypervisor/src/vm.rs b/hypervisor/src/vm.rs index f671204c0..b6fbf5ae5 100644 --- a/hypervisor/src/vm.rs +++ b/hypervisor/src/vm.rs @@ -23,7 +23,7 @@ use thiserror::Error; use vmm_sys_util::eventfd::EventFd; #[cfg(target_arch = "x86_64")] -use crate::ClockData; +use crate::{ClockData, ClockState}; #[cfg(target_arch = "aarch64")] use crate::arch::aarch64::gic::{Vgic, VgicConfig}; #[cfg(target_arch = "riscv64")] @@ -384,6 +384,17 @@ pub trait Vm: Send + Sync + Any { /// Set guest clock. #[cfg(target_arch = "x86_64")] fn set_clock(&self, data: &ClockData) -> Result<()>; + /// Capture the guest clock for snapshot/migration while the VM is paused. + /// `Ok(None)` means this backend has no clock to preserve. + #[cfg(target_arch = "x86_64")] + fn snapshot_clock(&self) -> Result> { + Ok(None) + } + /// Re-establish the guest clock before the vCPUs resume. + #[cfg(target_arch = "x86_64")] + fn restore_clock(&self, _state: &ClockState) -> Result<()> { + Ok(()) + } /// Create a device that is used for passthrough fn create_passthrough_device(&self) -> Result; /// Start logging dirty pages