FAILED: patch "[PATCH] KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired" failed to apply to 5.10-stable tree
Subsystems (3)
[email protected]
maintainerThe patch below does not apply to the 5.10-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.10.y
git checkout FETCH_HEAD
git cherry-pick -x e800decd9c0ac4349bcd8f8f9b29fd21fe93165e
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026072512-swept-john-385a@gregkh' --subject-prefix 'PATCH 5.10.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From e800decd9c0ac4349bcd8f8f9b29fd21fe93165e Mon Sep 17 00:00:00 2001
From: Venkatesh Srinivas <[email protected]>
Date: Wed, 15 Jul 2026 23:42:35 +0000
Subject: [PATCH] KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired
on KVM_RUN
On Intel platforms with a VMX preemption timer and APICv, if a VMM calls KVM_GET_LAPIC before KVM_GET_MSRS to save the vCPU state, it is possible to lose a pending timer interrupt.
If the thread running these ioctls is migrated to another core after calling KVM_GET_LAPIC but before KVM_GET_MSRS and the guest is using their LAPIC timer in TSC-deadline mode, not only does the save LAPIC state not carry the pending interrupt, the TSCDEADLINE MSR will be zeroed.
After migration across CPUs, KVM_GET_MSRS calls vcpu_load, posting the interrupt and clearing the MSR:vcpu_load() ->
kvm_arch_vcpu_load() ->
kvm_lapic_restart_hv_timer() ->
start_hv_timer() ->
apic_timer_expired() ->
kvm_apic_inject_pending_timer_irqs()
. post interrupt into the LAPIC state
. clear IA32_TSCDEADLINEThe saved LAPIC state will be missing the pending interrupt and the saved MSR will be zero. Oops.
Fix by only posting an interrupt when we're attempting to enter the guest (vcpu->wants_to_run == true), not for vcpu_load from other paths.
Message-ID: <[email protected]>
arch/x86/kvm/lapic.c +1 -1
@@ -2052,7 +2052,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn)if (apic_lvtt_tscdeadline (apic) || ktimer->hv_timer_in_use) ktimer->expired_tscdeadline = ktimer->tscdeadline; -if (!from_timer_fn && apic->apicv_active) { +if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {WARN_ON (kvm_get_running_vcpu () != vcpu);kvm_apic_inject_pending_timer_irqs (apic);return ;
Sasha Levin
maintainerPATCH 1/5 KVM: x86: Drop @vcpu parameter from kvm_x86_ops.hwapic_isr_update()
From: Sean Christopherson <[email protected]>
[ Upstream commit d39850f57d2102c6b46feb21237bc23bc42de4f7 ]
Drop the unused @vcpu parameter from hwapic_isr_update(). AMD/AVIC is unlikely to implement the helper, and VMX/APICv doesn't need the vCPU as it operates on the current VMCS. The result is somewhat odd, but allows for a decent amount of (future) cleanup in the APIC code.
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: e800decd9c0a ("KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN")
--- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/kvm/lapic.c | 10 ++++------ arch/x86/kvm/svm/avic.c | 2 +- arch/x86/kvm/svm/svm.h | 2 +- arch/x86/kvm/vmx/vmx.c | 2 +- 5 files changed, 8 insertions(+), 10 deletions(-)
arch/x86/include/asm/kvm_host.h +1 -1
@@ -1179,7 +1179,7 @@ struct kvm_x86_ops {void (*pre_update_apicv_exec_ctrl)(struct kvm *kvm,bool activate);void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu);void (*hwapic_irr_update)(struct kvm_vcpu *vcpu,int max_irr); -void (*hwapic_isr_update)(struct kvm_vcpu *vcpu,int isr); +void (*hwapic_isr_update)(int isr);bool (*guest_apic_has_interrupt)(struct kvm_vcpu *vcpu);void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu,u64 *eoi_exit_bitmap);void (*set_virtual_apic_mode)(struct kvm_vcpu *vcpu);
arch/x86/kvm/lapic.c +4 -6
@@ -520,7 +520,7 @@ static inline void apic_set_isr(int vec, struct kvm_lapic *apic)* just set SVI. */ if (unlikely (vcpu->arch.apicv_active)) - kvm_x86_ops.hwapic_isr_update (vcpu, vec); + kvm_x86_ops.hwapic_isr_update (vec);else { ++apic->isr_count;BUG_ON (apic->isr_count >MAX_APIC_VECTOR );
@@ -568,8 +568,7 @@ static inline void apic_clear_isr(int vec, struct kvm_lapic *apic)* and must be left alone. */ if (unlikely (vcpu->arch.apicv_active)) - kvm_x86_ops.hwapic_isr_update (vcpu, -apic_find_highest_isr (apic)); + kvm_x86_ops.hwapic_isr_update (apic_find_highest_isr (apic));else { --apic->isr_count;BUG_ON (apic->isr_count <0 );
@@ -2383,7 +2382,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event)if (vcpu->arch.apicv_active) { kvm_x86_ops.apicv_post_state_restore (vcpu); kvm_x86_ops.hwapic_irr_update (vcpu,-1 ); - kvm_x86_ops.hwapic_isr_update (vcpu,-1 ); + kvm_x86_ops.hwapic_isr_update (-1 ); } vcpu->arch.apic_arb_prio =0 ;
@@ -2651,8 +2650,7 @@ int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) kvm_x86_ops.apicv_post_state_restore (vcpu); kvm_x86_ops.hwapic_irr_update (vcpu,apic_find_highest_irr (apic)); - kvm_x86_ops.hwapic_isr_update (vcpu, -apic_find_highest_isr (apic)); + kvm_x86_ops.hwapic_isr_update (apic_find_highest_isr (apic)); }kvm_make_request (KVM_REQ_EVENT , vcpu);if (ioapic_in_kernel (vcpu->kvm))
arch/x86/kvm/svm/avic.c +1 -1
@@ -603,7 +603,7 @@ void svm_hwapic_irr_update(struct kvm_vcpu *vcpu, int max_irr) { } -void svm_hwapic_isr_update (struct kvm_vcpu *vcpu,int max_isr) +void svm_hwapic_isr_update (int max_isr) { }
arch/x86/kvm/svm/svm.h +1 -1
@@ -457,7 +457,7 @@ bool svm_check_apicv_inhibit_reasons(ulong bit);void svm_pre_update_apicv_exec_ctrl (struct kvm *kvm,bool activate);void svm_load_eoi_exitmap (struct kvm_vcpu *vcpu,u64 *eoi_exit_bitmap);void svm_hwapic_irr_update (struct kvm_vcpu *vcpu,int max_irr); -void svm_hwapic_isr_update (struct kvm_vcpu *vcpu,int max_isr); +void svm_hwapic_isr_update (int max_isr);int svm_deliver_avic_intr (struct kvm_vcpu *vcpu,int vec);bool svm_dy_apicv_has_pending_interrupt (struct kvm_vcpu *vcpu); int svm_update_pi_irte(struct kvm *kvm, unsigned int host_irq,
arch/x86/kvm/vmx/vmx.c +1 -1
@@ -6403,7 +6403,7 @@ static void vmx_set_apic_access_page_addr(struct kvm_vcpu *vcpu)put_page (page); } -static void vmx_hwapic_isr_update (struct kvm_vcpu *vcpu,int max_isr) +static void vmx_hwapic_isr_update (int max_isr) {u16 status;u8 old;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/5 KVM: x86: Check for in-kernel xAPIC when querying APICv for directed yield
From: Sean Christopherson <[email protected]>
[ Upstream commit ae801e1303e939ad5ebd9f390bdcc57275ada33b ]
Use kvm_vcpu_apicv_active() to check if APICv is active when seeing if a vCPU is a candidate for directed yield due to a pending ACPIv interrupt. This will allow moving apicv_active into kvm_lapic without introducing a potential NULL pointer deref (kvm_vcpu_apicv_active() effectively adds a pre-check on the vCPU having an in-kernel APIC).
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: e800decd9c0a ("KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN")
--- arch/x86/kvm/x86.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
arch/x86/kvm/x86.c +2 -1
@@ -11122,7 +11122,8 @@ bool kvm_arch_dy_runnable(struct kvm_vcpu *vcpu) kvm_test_request(KVM_REQ_EVENT, vcpu))return true; -if (vcpu->arch.apicv_active && kvm_x86_ops.dy_apicv_has_pending_interrupt (vcpu)) +if (kvm_vcpu_apicv_active (vcpu) && + kvm_x86_ops.dy_apicv_has_pending_interrupt (vcpu))return true;return false;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/5 KVM: x86: Move "apicv_active" into "struct kvm_lapic"
In reply to Sasha LevinFrom: Sean Christopherson <[email protected]>
[ Upstream commit ce0a58f4756c14d7646cfdf279dbaada9d7712a0 ]
Move the per-vCPU apicv_active flag into KVM's local APIC instance. APICv is fully dependent on an in-kernel local APIC, but that's not at all clear when reading the current code due to the flag being stored in the generic kvm_vcpu_arch struct.
No functional change intended.
Message-Id: <[email protected]>
Stable-dep-of: e800decd9c0a ("KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN")
--- arch/x86/include/asm/kvm_host.h | 1 - arch/x86/kvm/lapic.c | 31 ++++++++++--------------------- arch/x86/kvm/lapic.h | 3 ++- arch/x86/kvm/svm/avic.c | 2 +- arch/x86/kvm/vmx/vmx.c | 7 ++++--- arch/x86/kvm/x86.c | 12 ++++++------ 6 files changed, 23 insertions(+), 33 deletions(-)
arch/x86/include/asm/kvm_host.h +0 -1
@@ -544,7 +544,6 @@ struct kvm_vcpu_arch {u64 efer;u64 apic_base;struct kvm_lapic *apic;/* kernel irqchip context */ -bool apicv_active;bool load_eoi_exitmap_pending;DECLARE_BITMAP (ioapic_handled_vectors,256 );unsigned long apic_attention;
arch/x86/kvm/lapic.c +10 -21
@@ -482,14 +482,10 @@ static inline int apic_find_highest_irr(struct kvm_lapic *apic)static inline void apic_clear_irr (int vec,struct kvm_lapic *apic) { -struct kvm_vcpu *vcpu; - - vcpu = apic->vcpu; - -if (unlikely (vcpu->arch.apicv_active)) { +if (unlikely (apic->apicv_active)) {/* need to update RVI */ kvm_lapic_clear_vector (vec, apic->regs +APIC_IRR ); - kvm_x86_ops.hwapic_irr_update (vcpu, + kvm_x86_ops.hwapic_irr_update (apic->vcpu,apic_find_highest_irr (apic)); }else { apic->irr_pending = false;
@@ -507,19 +503,15 @@ EXPORT_SYMBOL_GPL(kvm_apic_clear_irr);static inline void apic_set_isr (int vec,struct kvm_lapic *apic) { -struct kvm_vcpu *vcpu; -if (__apic_test_and_set_vector (vec, apic->regs +APIC_ISR ))return ; - vcpu = apic->vcpu; -/* * With APIC virtualization enabled, all caching is disabled * because the processor can modify ISR under the hood. Instead * just set SVI. */ -if (unlikely (vcpu->arch.apicv_active)) +if (unlikely (apic->apicv_active)) kvm_x86_ops.hwapic_isr_update (vec);else { ++apic->isr_count;
@@ -554,12 +546,9 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic)static inline void apic_clear_isr (int vec,struct kvm_lapic *apic) { -struct kvm_vcpu *vcpu;if (!__apic_test_and_clear_vector (vec, apic->regs +APIC_ISR ))return ; - vcpu = apic->vcpu; -/* * We do get here for APIC virtualization enabled if the guest * uses the Hyper-V APIC enlightenment. In this case we may need
@@ -567,7 +556,7 @@ static inline void apic_clear_isr(int vec, struct kvm_lapic *apic)* on the other hand isr_count and highest_isr_cache are unused * and must be left alone. */ -if (unlikely (vcpu->arch.apicv_active)) +if (unlikely (apic->apicv_active)) kvm_x86_ops.hwapic_isr_update (apic_find_highest_isr (apic));else { --apic->isr_count;
@@ -706,7 +695,7 @@ static void pv_eoi_clr_pending(struct kvm_vcpu *vcpu)static int apic_has_interrupt_for_ppr (struct kvm_lapic *apic,u32 ppr) {int highest_irr; -if (apic->vcpu->arch.apicv_active) +if (apic->apicv_active) highest_irr = kvm_x86_ops.sync_pir_to_irr (apic->vcpu);else highest_irr =apic_find_highest_irr (apic);
@@ -1538,7 +1527,7 @@ static bool lapic_timer_int_injected(struct kvm_vcpu *vcpu)int vec = reg &APIC_VECTOR_MASK ;void *bitmap = apic->regs +APIC_ISR ; -if (vcpu->arch.apicv_active) +if (apic->apicv_active) bitmap = apic->regs +APIC_IRR ;if (apic_test_vector (vec, bitmap))
@@ -1647,7 +1636,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn)if (apic_lvtt_tscdeadline (apic) || ktimer->hv_timer_in_use) ktimer->expired_tscdeadline = ktimer->tscdeadline; -if (!from_timer_fn && vcpu->arch.apicv_active) { +if (!from_timer_fn && apic->apicv_active) {WARN_ON (kvm_get_running_vcpu () != vcpu);kvm_apic_inject_pending_timer_irqs (apic);return ;
@@ -2317,7 +2306,7 @@ void kvm_apic_update_apicv(struct kvm_vcpu *vcpu) {struct kvm_lapic *apic = vcpu->arch.apic; -if (vcpu->arch.apicv_active) { +if (apic->apicv_active) {/* irr_pending is always true when apicv is activated. */ apic->irr_pending = true; apic->isr_count =1 ;
@@ -2379,7 +2368,7 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event) vcpu->arch.apic_base | MSR_IA32_APICBASE_BSP); vcpu->arch.pv_eoi.msr_val =0 ;apic_update_ppr (apic); -if (vcpu->arch.apicv_active) { +if (apic->apicv_active) { kvm_x86_ops.apicv_post_state_restore (vcpu); kvm_x86_ops.hwapic_irr_update (vcpu,-1 ); kvm_x86_ops.hwapic_isr_update (-1 );
@@ -2646,7 +2635,7 @@ int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s)__start_apic_timer (apic,APIC_TMCCT );kvm_apic_update_apicv (vcpu); apic->highest_isr_cache =-1 ; -if (vcpu->arch.apicv_active) { +if (apic->apicv_active) { kvm_x86_ops.apicv_post_state_restore (vcpu); kvm_x86_ops.hwapic_irr_update (vcpu,apic_find_highest_irr (apic));
arch/x86/kvm/lapic.h +2 -1
@@ -47,6 +47,7 @@ struct kvm_lapic {struct kvm_timer lapic_timer;u32 divide_count;struct kvm_vcpu *vcpu; +bool apicv_active;bool sw_enabled;bool irr_pending;bool lvt0_in_nmi_mode;
@@ -216,7 +217,7 @@ static inline int apic_x2apic_mode(struct kvm_lapic *apic)static inline bool kvm_vcpu_apicv_active (struct kvm_vcpu *vcpu) { -return vcpu->arch.apic && vcpu->arch.apicv_active; +return vcpu->arch.apic && vcpu->arch.apic->apicv_active; }static inline bool kvm_apic_has_events (struct kvm_vcpu *vcpu)
arch/x86/kvm/svm/avic.c +1 -1
@@ -673,7 +673,7 @@ void svm_load_eoi_exitmap(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap)int svm_deliver_avic_intr (struct kvm_vcpu *vcpu,int vec) { -if (!vcpu->arch.apicv_active) +if (!kvm_vcpu_apicv_active (vcpu))return -1 ;kvm_lapic_set_irr (vec, vcpu->arch.apic);
arch/x86/kvm/vmx/vmx.c +4 -3
@@ -4116,7 +4116,8 @@ static int vmx_deliver_posted_interrupt(struct kvm_vcpu *vcpu, int vector)if (!r)return 0 ; -if (!vcpu->arch.apicv_active) +/* Note, this is called iff the local APIC is in-kernel. */ +if (!vcpu->arch.apic->apicv_active)return -1 ;if (pi_test_and_set_pir (vector, &vmx->pi_desc))
@@ -6457,7 +6458,7 @@ static int vmx_sync_pir_to_irr(struct kvm_vcpu *vcpu)int max_irr;bool max_irr_updated; -WARN_ON (!vcpu->arch.apicv_active); +WARN_ON (!kvm_vcpu_apicv_active (vcpu));if (pi_test_on (&vmx->pi_desc)) {pi_clear_on (&vmx->pi_desc);/*
@@ -6983,7 +6984,7 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu)* but it would incur the cost of a retpoline for now. * Revisit once static calls are available. */ -if (vcpu->arch.apicv_active) +if (kvm_vcpu_apicv_active (vcpu))vmx_sync_pir_to_irr (vcpu); goto reenter_guest; }
arch/x86/kvm/x86.c +6 -6
@@ -4182,7 +4182,7 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)static int kvm_vcpu_ioctl_get_lapic (struct kvm_vcpu *vcpu,struct kvm_lapic_state *s) { -if (vcpu->arch.apicv_active) +if (kvm_vcpu_apicv_active (vcpu)) kvm_x86_ops.sync_pir_to_irr (vcpu);return kvm_apic_get_state (vcpu, s);
@@ -8504,7 +8504,7 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu)if (!lapic_in_kernel (vcpu))return ; -if (vcpu->arch.apicv_active) +if (vcpu->arch.apic->apicv_active)return ;if (!vcpu->arch.apic->vapic_addr)
@@ -8967,7 +8967,7 @@ void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu)if (!lapic_in_kernel (vcpu))return ; - vcpu->arch.apicv_active =kvm_apicv_activated (vcpu->kvm); + vcpu->arch.apic->apicv_active =kvm_apicv_activated (vcpu->kvm);kvm_apic_update_apicv (vcpu); kvm_x86_ops.refresh_apicv_exec_ctrl (vcpu); }
@@ -9031,7 +9031,7 @@ static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)if (irqchip_split (vcpu->kvm))kvm_scan_ioapic_routes (vcpu, vcpu->arch.ioapic_handled_vectors);else { -if (vcpu->arch.apicv_active) +if (kvm_vcpu_apicv_active (vcpu)) kvm_x86_ops.sync_pir_to_irr (vcpu);if (ioapic_in_kernel (vcpu->kvm))kvm_ioapic_scan_entry (vcpu, vcpu->arch.ioapic_handled_vectors);
@@ -9275,7 +9275,7 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)* This handles the case where a posted interrupt was * notified with kvm_vcpu_kick. */ -if (kvm_lapic_enabled (vcpu) && vcpu->arch.apicv_active) +if (kvm_lapic_enabled (vcpu) &&kvm_vcpu_apicv_active (vcpu)) kvm_x86_ops.sync_pir_to_irr (vcpu);if (kvm_vcpu_exit_request (vcpu)) {
@@ -10254,7 +10254,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)if (r <0 ) goto fail_mmu_destroy;if (kvm_apicv_activated (vcpu->kvm)) - vcpu->arch.apicv_active = true; + vcpu->arch.apic->apicv_active = true; }else static_key_slow_inc (&kvm_no_apic_vcpu);
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 4/5 KVM: Introduce vcpu->wants_to_run
In reply to Sasha LevinFrom: David Matlack <[email protected]>
[ Upstream commit a6816314af5749cd88944bfdceb270c627cdf348 ]
Introduce vcpu->wants_to_run to indicate when a vCPU is in its core run loop, i.e. when the vCPU is running the KVM_RUN ioctl and immediate_exit was not set.
Replace all references to vcpu->run->immediate_exit with !vcpu->wants_to_run to avoid TOCTOU races with userspace. For example, a malicious userspace could invoked KVM_RUN with immediate_exit=true and then after KVM reads it to set wants_to_run=false, flip it to false. This would result in the vCPU running in KVM_RUN with wants_to_run=false. This wouldn't cause any real bugs today but is a dangerous landmine.
Stable-dep-of: e800decd9c0a ("KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN")
--- arch/arm64/kvm/arm.c | 2 +- arch/mips/kvm/mips.c | 2 +- arch/powerpc/kvm/powerpc.c | 2 +- arch/s390/kvm/kvm-s390.c | 2 +- arch/x86/kvm/x86.c | 4 ++-- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 3 +++ 7 files changed, 10 insertions(+), 6 deletions(-)
arch/arm64/kvm/arm.c +1 -1
@@ -686,7 +686,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)return ret; } -if (run->immediate_exit) +if (!vcpu->wants_to_run)return -EINTR ;vcpu_load (vcpu);
arch/mips/kvm/mips.c +1 -1
@@ -464,7 +464,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) vcpu->mmio_needed =0 ; } -if (vcpu->run->immediate_exit) +if (!vcpu->wants_to_run) goto out;lose_fpu (1 );
arch/powerpc/kvm/powerpc.c +1 -1
@@ -1832,7 +1832,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)kvm_sigset_activate (vcpu); -if (run->immediate_exit) +if (!vcpu->wants_to_run) r = -EINTR ;else r =kvmppc_vcpu_run (vcpu);
arch/s390/kvm/kvm-s390.c +1 -1
@@ -4373,7 +4373,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)struct kvm_run *kvm_run = vcpu->run;int rc; -if (kvm_run->immediate_exit) +if (!vcpu->wants_to_run)return -EINTR ; if (kvm_run->kvm_valid_regs & ~KVM_SYNC_S390_VALID_FIELDS ||
arch/x86/kvm/x86.c +2 -2
@@ -9648,7 +9648,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)kvm_load_guest_fpu (vcpu);if (unlikely (vcpu->arch.mp_state ==KVM_MP_STATE_UNINITIALIZED )) { -if (kvm_run->immediate_exit) { +if (!vcpu->wants_to_run) { r = -EINTR ; goto out; }
@@ -9692,7 +9692,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) }else WARN_ON (vcpu->arch.pio.count || vcpu->mmio_needed); -if (kvm_run->immediate_exit) +if (!vcpu->wants_to_run) r = -EINTR ;else r =vcpu_run (vcpu);
include/linux/kvm_host.h +1 -0
@@ -318,6 +318,7 @@ struct kvm_vcpu { bool dy_eligible; } spin_loop;#endif +bool wants_to_run;bool preempted;bool ready;struct kvm_vcpu_arch arch;
virt/kvm/kvm_main.c +3 -0
@@ -3366,7 +3366,10 @@ static long kvm_vcpu_ioctl(struct file *filp,synchronize_rcu ();put_pid (oldpid); } + vcpu->wants_to_run = !READ_ONCE (vcpu->run->immediate_exit); r =kvm_arch_vcpu_ioctl_run (vcpu); + vcpu->wants_to_run = false; +trace_kvm_userspace_exit (vcpu->run->exit_reason, r);break ; }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 5/5 KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN
In reply to Sasha LevinFrom: Venkatesh Srinivas <[email protected]>
[ Upstream commit e800decd9c0ac4349bcd8f8f9b29fd21fe93165e ]
On Intel platforms with a VMX preemption timer and APICv, if a VMM calls KVM_GET_LAPIC before KVM_GET_MSRS to save the vCPU state, it is possible to lose a pending timer interrupt.
If the thread running these ioctls is migrated to another core after calling KVM_GET_LAPIC but before KVM_GET_MSRS and the guest is using their LAPIC timer in TSC-deadline mode, not only does the save LAPIC state not carry the pending interrupt, the TSCDEADLINE MSR will be zeroed.
After migration across CPUs, KVM_GET_MSRS calls vcpu_load, posting the interrupt and clearing the MSR:vcpu_load() ->
kvm_arch_vcpu_load() ->
kvm_lapic_restart_hv_timer() ->
start_hv_timer() ->
apic_timer_expired() ->
kvm_apic_inject_pending_timer_irqs()
. post interrupt into the LAPIC state
. clear IA32_TSCDEADLINEThe saved LAPIC state will be missing the pending interrupt and the saved MSR will be zero. Oops.
Fix by only posting an interrupt when we're attempting to enter the guest (vcpu->wants_to_run == true), not for vcpu_load from other paths.
Message-ID: <[email protected]>
--- arch/x86/kvm/lapic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/x86/kvm/lapic.c +1 -1
@@ -1636,7 +1636,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn)if (apic_lvtt_tscdeadline (apic) || ktimer->hv_timer_in_use) ktimer->expired_tscdeadline = ktimer->tscdeadline; -if (!from_timer_fn && apic->apicv_active) { +if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {WARN_ON (kvm_get_running_vcpu () != vcpu);kvm_apic_inject_pending_timer_irqs (apic);return ;
Signature · 1 line
2.53.0