mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Dongli Zhang <dongli.zhang@oracle.com>
To: "Chenzhendong (alex)" <alex.chen@huawei.com>,
	Sean Christopherson <seanjc@google.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	"kvm@vger.kernel.org" <kvm@vger.kernel.org>,
	"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>
Cc: luolongmin <luolongmin@huawei.com>,
	"wangyanan (Y)" <wangyanan55@huawei.com>,
	David Woodhouse <dwmw2@infradead.org>
Subject: Re: [BUG REPORT] KVM: x86: Keep kvmclock_offset monotonic across same-host pause/resume
Date: Sun, 1 Nov 2026 12:30:25 -0800	[thread overview]
Message-ID: <eba55a4f-458e-47bc-be53-e72fcdb03f08@oracle.com> (raw)
In-Reply-To: <e99489abf983494d888f04bb0f901e06@huawei.com>

+ David.

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@infradead.org

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@oracle.com

Here is my RCA about forward drift and backward drift from another perspective.

https://lore.kernel.org/all/935312be-9a86-49fd-8bb4-2c998a68e2df@oracle.com


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).
> 
> 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.
> 
> 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.

> 
> where system_time = kernel_ns + kvmclock_offset is filled by the host at
> each vCPU entry. After resume the host re-samples tsc_timestamp, so the
> (rdtsc - tsc_timestamp) term starts growing normally again and never
> goes backward. The only term that can shrink is system_time: because
> kvmclock_offset was pulled back by freeze_duration, the new system_time
> is freeze_duration smaller than the value the guest last observed before
> the freeze. The guest's next clock_gettime(CLOCK_MONOTONIC) therefore
> reads a value freeze_duration smaller than before the freeze -- a
> backward jump, violating the invariant that CLOCK_MONOTONIC never goes
> backward.
> 
> The fix
> -------
> 
> Only allow kvmclock_offset to move forward (or stay the same). If the

Ideally, neither forward nor backward drift is appreciated.

Thank you very much!

Dongli Zhang


> newly computed offset would be smaller than the current one, keep the
> current value, so the guest clock keeps running forward and the freeze
> duration is simply skipped from the guest's perspective -- which is the
> expected behavior for a same-host pause/resume. This is a pure monotonic
> protection with no time threshold; the normal forward path (guest boot,
> KVM_CLOCK_REALTIME, cross-host migration toward a later destination host
> clock) is unaffected.
> 
>     old_off = ka->kvmclock_offset;
>     new_off = data.clock - now_raw_ns;
>     if (new_off >= old_off)
>             ka->kvmclock_offset = new_off;
> 
> Test
> ----
> 
> Inside the guest, pin a vCPU and call clock_gettime(CLOCK_MONOTONIC)
> twice in a row, flagging any backward step. Over 50 snapshot
> suspend/resume cycles the backward-step counter stays at 0 with this
> patch, while the unpatched kernel reports a backward jump on every cycle
> (measured freeze duration 27-36 ms).
> 
> To the best of our knowledge this scenario is not handled upstream. Any
> feedback on whether monotonic protection is the right approach (vs. e.g.
> teaching QEMU to feed a post-resume clock) would be much appreciated.
> 
> Signed-off-by: <Alex Chen> <alex.chen@huawei.com>
> ---
>  arch/x86/kvm/x86.c | 19 ++++++++++++++++++++-
>  1 file changed, 18 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index 7a0d0e8e5c1f..000000000000 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -XXXX,XX +XXXX,XX @@ static int kvm_vm_ioctl_set_clock(struct kvm *kvm, void __user *argp)
>  	struct kvm_arch *ka = &kvm->arch;
>  	struct kvm_clock_data data;
>  	u64 now_raw_ns;
> +	s64 old_off, new_off;
>  	...
>  	if (ka->use_master_clock)
>  		now_raw_ns = ka->master_kernel_ns;
>  	else
>  		now_raw_ns = get_kvmclock_base_ns();
> -	ka->kvmclock_offset = data.clock - now_raw_ns;
> +
> +	/*
> +	 * Monotonic protection: data.clock is the kvmclock value userspace
> +	 * saved at vm_stop (freeze instant); now_raw_ns is sampled at resume.
> +	 * The naive kvmclock_offset = data.clock - now_raw_ns pulls the
> +	 * offset back to the freeze instant, which makes pvti.system_time =
> +	 * kernel_ns + kvmclock_offset jump backward by the freeze duration ->
> +	 * guest vDSO CLOCK_MONOTONIC goes backward.
> +	 *
> +	 * Only allow kvmclock_offset to move forward (or stay). If the new
> +	 * offset would shrink, keep the old one so the guest clock keeps
> +	 * running forward (the freeze duration is simply skipped from the
> +	 * guest's perspective, which is the desired behavior for a same-host
> +	 * pause/resume). Pure monotonic protection, no time threshold.
> +	 */
> +	old_off = ka->kvmclock_offset;
> +	new_off = data.clock - now_raw_ns;
> +	if (new_off >= old_off)
> +		ka->kvmclock_offset = new_off;
>  	kvm_end_pvclock_update(kvm);
> 
> 


      reply	other threads:[~2026-11-01 20:30 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-01 16:19 Chenzhendong (alex)
2026-11-01 20:30 ` Dongli Zhang [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=eba55a4f-458e-47bc-be53-e72fcdb03f08@oracle.com \
    --to=dongli.zhang@oracle.com \
    --cc=alex.chen@huawei.com \
    --cc=dwmw2@infradead.org \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luolongmin@huawei.com \
    --cc=pbonzini@redhat.com \
    --cc=seanjc@google.com \
    --cc=wangyanan55@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®