[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <642d76482132d7b0ebc4c2468bc24e0f65cc48da.1770116050.git.isaku.yamahata@intel.com>
Date: Tue, 3 Feb 2026 10:16:51 -0800
From: isaku.yamahata@...el.com
To: kvm@...r.kernel.org
Cc: isaku.yamahata@...el.com,
isaku.yamahata@...il.com,
Paolo Bonzini <pbonzini@...hat.com>,
Sean Christopherson <seanjc@...gle.com>,
linux-kernel@...r.kernel.org
Subject: [PATCH 08/32] KVM: nVMX: Disallow/allow guest APIC timer virtualization switch to/from L2
From: Isaku Yamahata <isaku.yamahata@...el.com>
Disable guest APIC timer virtualization on nested VMEnter, enable it on
nested vmexit.
With VMX APIC timer virtualization, the CPU directly injects a guest timer
interrupt without VMExit. When the L1 APIC timer fires while running the
nested (L2) vCPU, KVM should emulate VMExit from L2 to L1. Switch to the
hv timer (preemption timer) or the sw timer when VMEntering from L1 to L2,
switch to guest APIC timer virtualization when VM exiting from L2 to L1.
Signed-off-by: Isaku Yamahata <isaku.yamahata@...el.com>
---
arch/x86/kvm/lapic.c | 29 +++++++++++++++++++++++++++++
arch/x86/kvm/lapic.h | 2 ++
arch/x86/kvm/vmx/nested.c | 13 +++++++++++++
arch/x86/kvm/vmx/vmx.c | 5 +++++
4 files changed, 49 insertions(+)
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index a2f714eb78b1..7c3ec0565a8f 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -1862,6 +1862,35 @@ static void apic_cancel_apic_virt_timer(struct kvm_lapic *apic)
start_apic_timer(apic);
}
+void kvm_sync_apic_virt_timer(struct kvm_vcpu *vcpu)
+{
+ struct kvm_lapic *apic = vcpu->arch.apic;
+
+ WARN_ON_ONCE(is_guest_mode(vcpu));
+
+ if (!apic->lapic_timer.apic_virt_timer_in_use)
+ return;
+
+ apic->lapic_timer.tscdeadline = kvm_x86_call(get_guest_tsc_deadline_virt)(vcpu);
+}
+EXPORT_SYMBOL_GPL(kvm_sync_apic_virt_timer);
+
+void kvm_cancel_apic_virt_timer(struct kvm_vcpu *vcpu)
+{
+ struct kvm_lapic *apic = vcpu->arch.apic;
+
+ WARN_ON_ONCE(!is_guest_mode(vcpu));
+
+ if (!apic->lapic_timer.apic_virt_timer_in_use)
+ return;
+
+ apic->lapic_timer.apic_virt_timer_in_use = false;
+ trace_kvm_apic_virt_timer_state(vcpu->vcpu_id, false);
+
+ start_apic_timer(apic);
+}
+EXPORT_SYMBOL_GPL(kvm_cancel_apic_virt_timer);
+
static void apic_set_apic_virt_timer(struct kvm_lapic *apic)
{
struct kvm_timer *ktimer = &apic->lapic_timer;
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index 3c597b670e7e..2ebe294fe0f9 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -251,6 +251,8 @@ void kvm_lapic_expired_hv_timer(struct kvm_vcpu *vcpu);
bool kvm_lapic_hv_timer_in_use(struct kvm_vcpu *vcpu);
void kvm_lapic_restart_hv_timer(struct kvm_vcpu *vcpu);
void kvm_update_apic_virt_timer(struct kvm_vcpu *vcpu);
+void kvm_sync_apic_virt_timer(struct kvm_vcpu *vcpu);
+void kvm_cancel_apic_virt_timer(struct kvm_vcpu *vcpu);
static inline bool kvm_lapic_apic_virt_timer_in_use(struct kvm_vcpu *vcpu)
{
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 6137e5307d0f..77521e37cfc6 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -3634,6 +3634,8 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
if (!enable_ept)
vmcs_writel(GUEST_CR3, vcpu->arch.cr3);
+ kvm_sync_apic_virt_timer(vcpu);
+
vmx_switch_vmcs(vcpu, &vmx->nested.vmcs02);
prepare_vmcs02_early(vmx, &vmx->vmcs01, vmcs12);
@@ -3709,6 +3711,14 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu,
vmx_start_preemption_timer(vcpu, timer_value);
}
+ /*
+ * Disable apic virtual timer for L1 to use sw timer (hr timer) or
+ * hypervisor timer (VMX preemption timer).
+ * When L1 timer interrupt occurs during running L2, KVM emulates
+ * VMExit from L2 to L1. Not directly injecting the interrupt into L2.
+ */
+ kvm_cancel_apic_virt_timer(vcpu);
+
/*
* Note no nested_vmx_succeed or nested_vmx_fail here. At this point
* we are no longer running L1, and VMLAUNCH/VMRESUME has not yet
@@ -5181,6 +5191,9 @@ void __nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason,
/* in case we halted in L2 */
kvm_set_mp_state(vcpu, KVM_MP_STATE_RUNNABLE);
+ /* If apic virtual timer is supported, switch back to it. */
+ kvm_update_apic_virt_timer(vcpu);
+
if (likely(!vmx->fail)) {
if (vm_exit_reason != -1)
trace_kvm_nested_vmexit_inject(vmcs12->vm_exit_reason,
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 82e1a0b2a8d2..c625c46658dc 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -8277,6 +8277,9 @@ bool vmx_can_use_apic_virt_timer(struct kvm_vcpu *vcpu)
if (vcpu->kvm->arch.vm_type != KVM_X86_DEFAULT_VM)
return false;
+ if (is_guest_mode(vcpu))
+ return false;
+
return cpu_has_vmx_apic_timer_virt() &&
/* VMX guest virtual timer supports only TSC deadline mode. */
kvm_lapic_lvtt_timer_mode(vcpu) == APIC_LVT_TIMER_TSCDEADLINE &&
@@ -8288,6 +8291,8 @@ bool vmx_can_use_apic_virt_timer(struct kvm_vcpu *vcpu)
void vmx_set_apic_virt_timer(struct kvm_vcpu *vcpu, u16 vector)
{
+ WARN_ON_ONCE(is_guest_mode(vcpu));
+
vmcs_write16(GUEST_APIC_TIMER_VECTOR, vector);
vmx_disable_intercept_for_msr(vcpu, MSR_IA32_TSC_DEADLINE, MSR_TYPE_RW);
tertiary_exec_controls_setbit(to_vmx(vcpu), TERTIARY_EXEC_GUEST_APIC_TIMER);
--
2.45.2
Powered by blists - more mailing lists