From 56e891a4057a22dc5c18af3a68937a4ded983698 Mon Sep 17 00:00:00 2001 From: Daniel Verkamp Date: Tue, 19 May 2026 12:31:51 -0700 Subject: [PATCH] hypervisor: kvm: preserve kvmclock realtime and fill if needed If `KVM_GET_CLOCK` already filled out the `realtime` field, it sets the `KVM_CLOCK_REALTIME` flag, but if we instead preserve this flag, the kernel will automatically adjust the kvmclock clock when calling `KVM_SET_CLOCK` based on the elapsed wall-clock time between pause and resume. This just requires removing the `reset_flags()` function, which allows the `KVM_CLOCK_REALTIME` flag to persist in the serialized clock state. However, the kernel does not always fill the `realtime` field, depending on clock source; in this case, fill `realtime` during pause based on the system time. This is not as precise as the automatic `KVM_GET_CLOCK` version, since we query the time slightly after the vCPU was paused, but it allows the clock to be resumed mostly in sync instead of being wildly off. In this case, we also set the `KVM_CLOCK_REALTIME` flag in the saved `struct kvmclock` so `KVM_SET_CLOCK` will adjust the clock on resume. Basic test case: 1. Run a VM with a Linux guest. 2. Pause the guest via `vm.pause` API. 3. Wait several minutes. 4. Resume the guest via `vm.resume` API. 5. Verify the guest time (e.g. via `date` command) is valid. 6. Verify guest is still using `kvm-clock` timesource: cat /sys/devices/system/clocksource/clocksource0/current_clocksource Before applying the patch, the guest clock would be off by the delta time between pause and resume; after the patch, the clock is (more or less) in sync with the correct wall-clock time. Old snapshots will not have the `KVM_CLOCK_REALTIME` flag populated, so they will not be affected by the new behavior. Signed-off-by: Daniel Verkamp --- hypervisor/src/lib.rs | 22 +++++++++++++++++++--- vmm/src/vm.rs | 4 +++- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/hypervisor/src/lib.rs b/hypervisor/src/lib.rs index 6800cc66e..865ea02db 100644 --- a/hypervisor/src/lib.rs +++ b/hypervisor/src/lib.rs @@ -184,12 +184,28 @@ pub enum ClockData { #[cfg(target_arch = "x86_64")] impl ClockData { - pub fn reset_flags(&mut self) { + pub fn has_realtime(&self) -> bool { match self { #[cfg(feature = "kvm")] - ClockData::Kvm(s) => s.flags = 0, + ClockData::Kvm(s) => s.flags & kvm_bindings::KVM_CLOCK_REALTIME != 0, #[allow(unreachable_patterns)] - _ => {} + _ => false, + } + } + + pub fn set_realtime(&mut self, realtime: std::time::SystemTime) { + match self { + #[cfg(feature = "kvm")] + ClockData::Kvm(s) => { + if let Ok(time_since_epoch) = realtime.duration_since(std::time::UNIX_EPOCH) { + s.realtime = time_since_epoch.as_nanos() as u64; + s.flags |= kvm_bindings::KVM_CLOCK_REALTIME; + } + } + #[allow(unreachable_patterns)] + _ => { + let _ = realtime; + } } } } diff --git a/vmm/src/vm.rs b/vmm/src/vm.rs index f397892a9..1cbc20a30 100644 --- a/vmm/src/vm.rs +++ b/vmm/src/vm.rs @@ -3244,7 +3244,9 @@ impl Pausable for Vm { .vm .get_clock() .map_err(|e| MigratableError::Pause(anyhow!("Could not get VM clock: {e}")))?; - clock.reset_flags(); + if !clock.has_realtime() { + clock.set_realtime(std::time::SystemTime::now()); + } self.saved_clock = Some(clock); }