Re: [BUG REPORT] KVM: x86: Keep kvmclock_offset monotonic across same-host pause/resume
From: David Woodhouse
Date: Tue Nov 03 2026 - 18:44:51 EST
On Sun, 2026-11-01 at 12:30 -0800, Dongli Zhang wrote:
> + David.
Thanks.
> FYI there were some prior discussions related to this topic.
>
> David has a patch at KVM to enhance the KVM API to resolve the issue. It is
> still not in mainline.
>
> [PATCH v7 31/36] KVM: x86: Add KVM_[GS]ET_CLOCK_GUEST for accurate KVM clock
> migration
> https://lore.kernel.org/all/20260728144954.355376-32-dwmw2@xxxxxxxxxxxxx
>
> I used to try to enhance from QEMU side to avoid clock drift while accounting
> for downtime.
>
> [PATCH 1/1] target/i386/kvm: account blackout downtime for kvm-clock and guest TSC
> https://lore.kernel.org/qemu-devel/20251009095831.46297-1-dongli.zhang@xxxxxxxxxx
>
> Here is my RCA about forward drift and backward drift from another perspective.
>
> https://lore.kernel.org/all/935312be-9a86-49fd-8bb4-2c998a68e2df@xxxxxxxxxx
>
>
> On Sun, Nov 1, 2026 8:19:14AM -0800, Chenzhendong (alex) wrote:
> > Hi,
> >
> > While testing storage snapshots that suspend and resume a VM on the same
> > host (vm_stop -> snapshot -> vm_start), we observed that the guest
> > CLOCK_MONOTONIC jumps *backward* by the freeze duration after every
> > resume.
> >
> > The problem
> > -----------
> >
> > On a same-host pause/resume cycle, QEMU saves the kvmclock value at the
> > freeze instant (T1) via KVM_GET_CLOCK in vm_stop, and feeds it back via
> > KVM_SET_CLOCK in vm_start (T2 = T1 + freeze_duration).
The whole of this in KVM upstream today is completely insane.
Note that those are completely different clock domains. Your T1 and T2
are CLOCK_REALTIME, which doesn't even run at the same *rate* as the
host's CLOCK_MONOTONIC_RAW which the kvmclock is theoretically bound
to. And if you do it during a leap second, it's doubly hosed because
it's UTC not TAI.
Ignoring the leap seconds, it's almost tolerable to use KVM_SET_CLOCK
with the KVM_CLOCK_REALTIME flag for live *migration*, but it makes
zero sense for live update on the *same* host.
The source of timekeeping is the TSC. The guest's TSC is just an
*offset* (and optional scaling) from the host's TSC. So if you're
running on the same host, the guest *TSC* can be reconstituted with
*exactly* the same relationship to the host TSC as before. Not a single
cycle of difference.
The kvmclock is then advertised to the guest as a mathematical function
of the guest's TSC. There's not much excuse for even a single
nanosecond of difference in that *either*. In fact, even with
KVM_[GS]ET_CLOCK_GUEST in my patch series, there's still a ±1ns
variance across get/set. Fixing that would require a lot *more*
refactoring than I've already done, so probably isn't worth it.
Guests should never be using kvmclock on modern CPUs where the TSC is
reliable, *anyway*.
> > kvm_vm_ioctl_set_clock() recomputes the offset unconditionally:
> >
> > kvmclock_offset = data.clock - now_raw_ns
> >
> > with now_raw_ns sampled at T2. Since
> >
> > data.clock = now_raw_ns(T1) + old_kvmclock_offset
> >
> > this gives
> >
> > new_offset = old_kvmclock_offset - freeze_duration
> >
> > i.e. the offset is pulled back by the freeze duration.
Hm? There's no `freeze_duration` in kvm_vm_ioctl_set_clock(). It looks
like this:
/*
* This pairs with kvm_guest_time_update(): when masterclock
is
* in use, we use master_kernel_ns + kvmclock_offset to set
* unsigned 'system_time' so if we use get_kvmclock_ns()
(which
* is slightly ahead) here we risk going negative on unsigned
* 'system_time' when 'data.clock' is very small.
*/
if (data.flags & KVM_CLOCK_REALTIME) {
u64 now_real_ns = ktime_get_real_ns();
/*
* Avoid stepping the kvmclock backwards.
*/
if (now_real_ns > data.realtime)
data.clock += now_real_ns - data.realtime;
}
Does that not already *have* the monotonicity protection that you are
trying to add?
> > The guest computes CLOCK_MONOTONIC from the pvclock page as:
> >
> > CLOCK_MONOTONIC = system_time + (rdtsc - tsc_timestamp) * mult >> shift
>
> CLOCK_MONOTONIC and kvm-clock are different topics.
>
> I would suggest here use kvm-clock or PV clock.
Right, don't use CLOCK_MONOTONIC in the guest; use CLOCK_MONOTONIC_RAW.
You can do this with KVM selftests though; perform the save/restore
from the host and then just run some code in the guest which checks for
monotonicity. Didn't my series already add a test which is very similar
to that?
Attachment:
smime.p7s
Description: S/MIME cryptographic signature