mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
hypervisor: aarch64: advance the guest counter on restore and migration
Currently, Cloud Hypervisor round-trips CNTVCT_EL0 through KVM_GET_REG_LIST/SET_ONE_REG, which leaves a cold-restored or migrated guest behind real UTC by the downtime. Same-host pause/resume self-corrects (the physical counter keeps running across the pause), so only restore and migration cases required the clock to catch up to wall clock time. Since ARM has no kernel helper, compute the difference in wall clock time and compute the ticks so that it can advance the CNTVCT correctly. It is set via vcpu0 only as it affects a single VM wide value after Linux 6.4. For older kernels, it was a truly vcpu value which needs to be invoked for every vcpu. Gated on all(target_arch = "aarch64", feature = "kvm"); x86 is unchanged. Basic manual test case (aarch64 + KVM) verified both in intra host and inter host snapshot save/restore: 1. Boot a Linux guest; in the guest, `date -u` tracks the host's UTC. 2. Pause and snapshot the VM (ch-remote pause; ch-remote snapshot file:///<dir>). 3. Leave it down for several minutes (the off-host interval). 4. Restore and resume into a fresh VMM (ch-remote restore source_url=file:///<dir>,resume=true). 5. In the guest, run `date -u` again and compare to the host: the guest now tracks current UTC, having advanced by ~the time it spent down. Before this change the restored guest reads behind real UTC by the downtime; after it, the guest clock is back in sync (to within the snapshot-to-restore sampling slop). Signed-off-by: Atish Patra <atishp@meta.com>
This commit is contained in:
committed by
Rob Bradford
parent
69637dde69
commit
25271c9d0c
@@ -24,7 +24,6 @@ use vmm_sys_util::eventfd::EventFd;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::ClockData;
|
||||
use crate::ClockState;
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
use crate::arch::aarch64::gic::{Vgic, VgicConfig};
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
@@ -32,7 +31,7 @@ use crate::arch::riscv64::aia::{Vaia, VaiaConfig};
|
||||
#[cfg(feature = "tdx")]
|
||||
use crate::arch::x86::CpuIdEntry;
|
||||
use crate::cpu::Vcpu;
|
||||
use crate::{IoEventAddress, IrqRoutingEntry};
|
||||
use crate::{ClockRestoreMode, ClockState, IoEventAddress, IrqRoutingEntry};
|
||||
|
||||
///
|
||||
/// I/O events data matches (32 or 64 bits).
|
||||
@@ -147,6 +146,20 @@ pub enum HypervisorVmError {
|
||||
#[error("Failed to capture the guest timer state")]
|
||||
CaptureTimerState(#[source] anyhow::Error),
|
||||
///
|
||||
/// Restore guest timer state error (aarch64)
|
||||
///
|
||||
#[cfg(all(target_arch = "aarch64", feature = "kvm"))]
|
||||
#[error("Failed to restore the guest timer state")]
|
||||
RestoreTimerState(#[source] anyhow::Error),
|
||||
///
|
||||
/// Counter frequency mismatch on restore (aarch64)
|
||||
///
|
||||
#[cfg(all(target_arch = "aarch64", feature = "kvm"))]
|
||||
#[error(
|
||||
"Saved counter frequency ({saved} Hz) != host ({host} Hz); refusing to advance guest counter"
|
||||
)]
|
||||
CntfrqMismatch { saved: u64, host: u64 },
|
||||
///
|
||||
/// Create passthrough device
|
||||
///
|
||||
#[error("Failed to create passthrough device")]
|
||||
@@ -398,9 +411,17 @@ pub trait Vm: Send + Sync + Any {
|
||||
fn snapshot_clock(&self, _boot_vcpu: &dyn Vcpu) -> Result<Option<ClockState>> {
|
||||
Ok(None)
|
||||
}
|
||||
/// Re-establish the guest clock before the vCPUs resume.
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
fn restore_clock(&self, _state: &ClockState) -> Result<()> {
|
||||
/// Re-establish the guest clock before the vCPUs resume. `mode` distinguishes a
|
||||
/// same-host pause/resume (the clock kept running) from a restore/migration where
|
||||
/// it must catch up to wall time; x86 ignores it (kvmclock is re-applied on every
|
||||
/// resume). aarch64 writes the counter on all `vcpus` (older kernels track the
|
||||
/// offset per-vCPU); x86 ignores `vcpus`.
|
||||
fn restore_clock(
|
||||
&self,
|
||||
_vcpus: &[&dyn Vcpu],
|
||||
_state: &ClockState,
|
||||
_mode: ClockRestoreMode,
|
||||
) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
/// Create a device that is used for passthrough
|
||||
|
||||
Reference in New Issue
Block a user