hypervisor: add a generic guest-clock save/restore abstraction

Preserving the guest clock across pause/resume and snapshot/restore is
currently open-coded in the VMM against the x86-only
get_clock/set_clock. aarch64 needs the same correction but via a
different mechanism (i.e. the architected counter, CNTVCT). Having a
common backend-agnostic interface that VMM can drive uniformly allows us
to keep the architecture details behind the Hypervisor abstraction.

This commit only introduces the abstraction while the future commits
will actually move the implementation to use it.

Use this opportunity to fix the full path to get SystemTime as well.

Suggested-by: Sebastien Boeuf <sboeuf@meta.com>
Signed-off-by: Atish Patra <atishp@meta.com>
This commit is contained in:
Atish Patra
2026-06-11 14:51:01 -07:00
committed by Rob Bradford
parent 00edf5b34d
commit d68b93ea93
4 changed files with 56 additions and 4 deletions

View File

@@ -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<Option<ClockState>> {
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<VfioDeviceFd> {
let mut vfio_dev = kvm_create_device {

View File

@@ -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")]

View File

@@ -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<Option<ClockState>> {
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

View File

@@ -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<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<()> {
Ok(())
}
/// Create a device that is used for passthrough
fn create_passthrough_device(&self) -> Result<vfio_ioctls::VfioDeviceFd>;
/// Start logging dirty pages