All Virtuozzo development lists (kernel + QEMU)
 help / color / mirror / Atom feed
* [Devel] [PATCH hci-8.1] KVM: nVMX: Keep the emulated VMX-preemption timer on L1's TSC
@ 2026-11-06 13:43 Eva Kurchatova
  2026-11-06 13:48 ` no-reply
  0 siblings, 1 reply; 2+ messages in thread
From: Eva Kurchatova @ 2026-11-06 13:43 UTC (permalink / raw)
  To: khorenko; +Cc: devel

KVM emulates the VMX-preemption timer of L2 with an hrtimer, and turns
the timer value into nanoseconds with the TSC frequency of the vCPU.
The hrtimer runs on CLOCK_MONOTONIC, which NTP slews against the TSC,
so the VM-exit comes late or early in L1's TSC while the host clock is
slewed. vmx_preemption_timer_test then fails with "L2 PT expiry TSC >
L2 TSC deadline" when the clock is slowed down, and with "L1 PT expiry
TSC < L1 TSC deadline" when it is sped up.

Arm the hrtimer an eighth early, and re-arm it from its callback until
L1's TSC reaches the deadline that KVM keeps since
commit 850448f35aaf ("KVM: nVMX: Fix VMX preemption timer migration").
Count the timer value that is left from that deadline when starting
the timer and when saving it to vmcs12, as the hrtimer no longer tracks
it.

Fixes: f4124500c2c1 ("KVM: nVMX: Fully emulate preemption timer")
https://virtuozzo.atlassian.net/browse/VSTOR-152991
Feature: fix ms/kvm

Signed-off-by: Eva Kurchatova <eva.kurchatova@virtuozzo.com>
---
 arch/x86/kvm/vmx/nested.c | 67 +++++++++++++++++++++++++--------------
 1 file changed, 43 insertions(+), 24 deletions(-)

diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 8c771d8c35e3..3f6a606e97a0 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -2206,15 +2206,51 @@ void nested_sync_vmcs12_to_shadow(struct kvm_vcpu *vcpu)
 	vmx->nested.need_vmcs12_to_shadow_sync = false;
 }
 
+/* Ticks left until L1's TSC reaches the deadline, at most the 32-bit value */
+static u64 vmx_preemption_timer_left(struct kvm_vcpu *vcpu)
+{
+	u64 deadline = to_vmx(vcpu)->nested.preemption_timer_deadline;
+	u64 now = kvm_read_l1_tsc(vcpu, rdtsc()) >>
+		  VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE;
+
+	return deadline > now ? min(deadline - now, (u64)U32_MAX) : 0;
+}
+
+/*
+ * The hrtimer runs on CLOCK_MONOTONIC, which NTP may slew against the TSC by
+ * up to 10%: fire an eighth early while more than 100us are left, and let the
+ * callback re-arm until the deadline is reached.
+ */
+static ktime_t vmx_preemption_timer_expiry(struct kvm_vcpu *vcpu, u64 ticks)
+{
+	u64 ns = (ticks << VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE) * 1000000;
+
+	do_div(ns, vcpu->arch.virtual_tsc_khz);
+	if (ns > 100 * NSEC_PER_USEC)
+		ns -= ns >> 3;
+	return ktime_add_ns(ktime_get(), ns);
+}
+
+static void vmx_preemption_timer_expire(struct kvm_vcpu *vcpu)
+{
+	to_vmx(vcpu)->nested.preemption_timer_expired = true;
+	kvm_make_request(KVM_REQ_EVENT, vcpu);
+	kvm_vcpu_kick(vcpu);
+}
+
 static enum hrtimer_restart vmx_preemption_timer_fn(struct hrtimer *timer)
 {
 	struct vcpu_vmx *vmx =
 		container_of(timer, struct vcpu_vmx, nested.preemption_timer);
+	u64 left = vmx_preemption_timer_left(&vmx->vcpu);
 
-	vmx->nested.preemption_timer_expired = true;
-	kvm_make_request(KVM_REQ_EVENT, &vmx->vcpu);
-	kvm_vcpu_kick(&vmx->vcpu);
+	if (left) {
+		hrtimer_set_expires(timer,
+				    vmx_preemption_timer_expiry(&vmx->vcpu, left));
+		return HRTIMER_RESTART;
+	}
 
+	vmx_preemption_timer_expire(&vmx->vcpu);
 	return HRTIMER_NORESTART;
 }
 
@@ -2231,7 +2267,7 @@ static u64 vmx_calc_preemption_timer_value(struct kvm_vcpu *vcpu)
 			vmcs12->vmx_preemption_timer_value + l1_scaled_tsc;
 		vmx->nested.has_preemption_timer_deadline = true;
 	}
-	return vmx->nested.preemption_timer_deadline - l1_scaled_tsc;
+	return vmx_preemption_timer_left(vcpu);
 }
 
 static void vmx_start_preemption_timer(struct kvm_vcpu *vcpu,
@@ -2244,18 +2280,15 @@ static void vmx_start_preemption_timer(struct kvm_vcpu *vcpu,
 	 * a VMExit prior to executing any instructions in the guest.
 	 */
 	if (preemption_timeout == 0) {
-		vmx_preemption_timer_fn(&vmx->nested.preemption_timer);
+		vmx_preemption_timer_expire(vcpu);
 		return;
 	}
 
 	if (vcpu->arch.virtual_tsc_khz == 0)
 		return;
 
-	preemption_timeout <<= VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE;
-	preemption_timeout *= 1000000;
-	do_div(preemption_timeout, vcpu->arch.virtual_tsc_khz);
 	hrtimer_start(&vmx->nested.preemption_timer,
-		      ktime_add_ns(ktime_get(), preemption_timeout),
+		      vmx_preemption_timer_expiry(vcpu, preemption_timeout),
 		      HRTIMER_MODE_ABS_PINNED);
 }
 
@@ -4428,20 +4461,6 @@ static int vmx_check_nested_events(struct kvm_vcpu *vcpu)
 	return vmx_complete_nested_posted_interrupt(vcpu);
 }
 
-static u32 vmx_get_preemption_timer_value(struct kvm_vcpu *vcpu)
-{
-	ktime_t remaining =
-		hrtimer_get_remaining(&to_vmx(vcpu)->nested.preemption_timer);
-	u64 value;
-
-	if (ktime_to_ns(remaining) <= 0)
-		return 0;
-
-	value = ktime_to_ns(remaining) * vcpu->arch.virtual_tsc_khz;
-	do_div(value, 1000000);
-	return value >> VMX_MISC_EMULATED_PREEMPTION_TIMER_RATE;
-}
-
 static bool is_vmcs12_ext_field(unsigned long field)
 {
 	switch (field) {
@@ -4597,7 +4616,7 @@ static void sync_vmcs02_to_vmcs12(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12)
 	    vmcs12->vm_exit_controls & VM_EXIT_SAVE_VMX_PREEMPTION_TIMER &&
 	    !vmx->nested.nested_run_pending)
 		vmcs12->vmx_preemption_timer_value =
-			vmx_get_preemption_timer_value(vcpu);
+			vmx_preemption_timer_left(vcpu);
 
 	/*
 	 * In some cases (usually, nested EPT), L2 is allowed to change its
-- 
2.56.0

_______________________________________________
Devel mailing list
Devel@openvz.org
https://lists.openvz.org/mailman/listinfo/devel

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-06 13:50 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-06 13:43 [Devel] [PATCH hci-8.1] KVM: nVMX: Keep the emulated VMX-preemption timer on L1's TSC Eva Kurchatova
2026-11-06 13:48 ` no-reply

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.