[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <995074de8754f559b59c65d76aeb940fac122bd3.1770116050.git.isaku.yamahata@intel.com>
Date: Tue, 3 Feb 2026 10:16:45 -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,
Yang Zhong <yang.zhong@...ux.intel.com>
Subject: [PATCH 02/32] KVM: x86: Implement APIC virt timer helpers with callbacks
From: Yang Zhong <yang.zhong@...ux.intel.com>
Add an additional APIC emulation mode for APIC timer virtualization. When
the guest programs the local APIC timer mode, switch to this new emulation
mode using the newly added hooks when APIC timer virtualization is
available.
Add five x86 KVM callbacks for APIC timer virtualization. These callbacks
are analogous to those used for the preemption timer and will be invoked by
kvm/lapic. These helpers start/stop the timer once the APIC virt timer
feature is enabled and the guest sets the MSR_IA32_TSC_DEADLINE. Upon
updating the TSC deadline mode in the APIC_LVTT register, KVM's LAPIC will
initiate the APIC virt timer instead of the preemption timer.
Co-developed-by: Yang Zhong <yang.zhong@...ux.intel.com>
Signed-off-by: Yang Zhong <yang.zhong@...ux.intel.com>
Co-developed-by: Isaku Yamahata <isaku.yamahata@...el.com>
Signed-off-by: Isaku Yamahata <isaku.yamahata@...el.com>
---
arch/x86/include/asm/kvm-x86-ops.h | 5 ++
arch/x86/include/asm/kvm_host.h | 6 +++
arch/x86/kvm/lapic.c | 81 +++++++++++++++++++++++++++++-
arch/x86/kvm/lapic.h | 1 +
4 files changed, 91 insertions(+), 2 deletions(-)
diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h
index de709fb5bd76..09f664aa72c1 100644
--- a/arch/x86/include/asm/kvm-x86-ops.h
+++ b/arch/x86/include/asm/kvm-x86-ops.h
@@ -118,6 +118,11 @@ KVM_X86_OP_OPTIONAL_RET0(dy_apicv_has_pending_interrupt)
KVM_X86_OP_OPTIONAL(protected_apic_has_interrupt)
KVM_X86_OP_OPTIONAL(set_hv_timer)
KVM_X86_OP_OPTIONAL(cancel_hv_timer)
+KVM_X86_OP_OPTIONAL(can_use_apic_virt_timer)
+KVM_X86_OP_OPTIONAL(set_apic_virt_timer)
+KVM_X86_OP_OPTIONAL(cancel_apic_virt_timer)
+KVM_X86_OP_OPTIONAL(set_guest_tsc_deadline_virt)
+KVM_X86_OP_OPTIONAL(get_guest_tsc_deadline_virt)
KVM_X86_OP(setup_mce)
#ifdef CONFIG_KVM_SMM
KVM_X86_OP(smi_allowed)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 5a3bfa293e8b..9fabaf532e41 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1896,6 +1896,12 @@ struct kvm_x86_ops {
int (*set_hv_timer)(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc,
bool *expired);
void (*cancel_hv_timer)(struct kvm_vcpu *vcpu);
+ bool (*can_use_apic_virt_timer)(struct kvm_vcpu *vcpu);
+ void (*set_apic_virt_timer)(struct kvm_vcpu *vcpu, u16 vector);
+ void (*cancel_apic_virt_timer)(struct kvm_vcpu *vcpu);
+ void (*set_guest_tsc_deadline_virt)(struct kvm_vcpu *vcpu,
+ u64 tscdeadline);
+ u64 (*get_guest_tsc_deadline_virt)(struct kvm_vcpu *vcpu);
void (*setup_mce)(struct kvm_vcpu *vcpu);
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 1597dd0b0cc6..b942210c6a25 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -131,7 +131,8 @@ static inline u32 kvm_x2apic_id(struct kvm_lapic *apic)
static bool kvm_can_post_timer_interrupt(struct kvm_vcpu *vcpu)
{
return pi_inject_timer && kvm_vcpu_apicv_active(vcpu) &&
- (kvm_mwait_in_guest(vcpu->kvm) || kvm_hlt_in_guest(vcpu->kvm));
+ (kvm_mwait_in_guest(vcpu->kvm) || kvm_hlt_in_guest(vcpu->kvm)) &&
+ !vcpu->arch.apic->lapic_timer.apic_virt_timer_in_use;
}
static bool kvm_can_use_hv_timer(struct kvm_vcpu *vcpu)
@@ -1817,17 +1818,81 @@ static void limit_periodic_timer_frequency(struct kvm_lapic *apic)
}
static void cancel_hv_timer(struct kvm_lapic *apic);
+static void cancel_apic_virt_timer(struct kvm_lapic *apic);
-static void cancel_apic_timer(struct kvm_lapic *apic)
+static void __cancel_apic_timer(struct kvm_lapic *apic)
{
hrtimer_cancel(&apic->lapic_timer.timer);
preempt_disable();
if (apic->lapic_timer.hv_timer_in_use)
cancel_hv_timer(apic);
+ else if (apic->lapic_timer.apic_virt_timer_in_use)
+ cancel_apic_virt_timer(apic);
preempt_enable();
+}
+
+static void cancel_apic_timer(struct kvm_lapic *apic)
+{
+ __cancel_apic_timer(apic);
atomic_set(&apic->lapic_timer.pending, 0);
}
+static void start_apic_timer(struct kvm_lapic *apic);
+
+static void cancel_apic_virt_timer(struct kvm_lapic *apic)
+{
+ struct kvm_vcpu *vcpu = apic->vcpu;
+
+ apic->lapic_timer.tscdeadline = kvm_x86_call(get_guest_tsc_deadline_virt)(vcpu);
+ kvm_x86_call(set_guest_tsc_deadline_virt)(vcpu, 0);
+
+ kvm_x86_call(cancel_apic_virt_timer)(vcpu);
+ apic->lapic_timer.apic_virt_timer_in_use = false;
+}
+
+static void apic_cancel_apic_virt_timer(struct kvm_lapic *apic)
+{
+ if (!apic->lapic_timer.apic_virt_timer_in_use)
+ return;
+
+ cancel_apic_virt_timer(apic);
+ start_apic_timer(apic);
+}
+
+static void apic_set_apic_virt_timer(struct kvm_lapic *apic)
+{
+ struct kvm_timer *ktimer = &apic->lapic_timer;
+ struct kvm_vcpu *vcpu = apic->vcpu;
+ u8 vector;
+ u32 reg;
+
+ if (apic->lapic_timer.apic_virt_timer_in_use)
+ return;
+
+ reg = kvm_lapic_get_reg(apic, APIC_LVTT);
+ vector = reg & APIC_VECTOR_MASK;
+
+ __cancel_apic_timer(apic);
+ kvm_x86_call(set_apic_virt_timer)(vcpu, vector);
+ kvm_x86_call(set_guest_tsc_deadline_virt)(vcpu, ktimer->tscdeadline);
+ ktimer->apic_virt_timer_in_use = true;
+}
+
+static bool kvm_can_use_apic_virt_timer(struct kvm_vcpu *vcpu)
+{
+ return kvm_x86_ops.can_use_apic_virt_timer &&
+ apic_lvt_enabled(vcpu->arch.apic, APIC_LVTT) &&
+ kvm_x86_call(can_use_apic_virt_timer)(vcpu);
+}
+
+static void apic_update_apic_virt_timer(struct kvm_lapic *apic)
+{
+ if (kvm_can_use_apic_virt_timer(apic->vcpu))
+ apic_set_apic_virt_timer(apic);
+ else
+ apic_cancel_apic_virt_timer(apic);
+}
+
static void apic_update_lvtt(struct kvm_lapic *apic)
{
u32 timer_mode = kvm_lapic_get_reg(apic, APIC_LVTT) &
@@ -1840,10 +1905,19 @@ static void apic_update_lvtt(struct kvm_lapic *apic)
kvm_lapic_set_reg(apic, APIC_TMICT, 0);
apic->lapic_timer.period = 0;
apic->lapic_timer.tscdeadline = 0;
+
+ if (apic->lapic_timer.apic_virt_timer_in_use)
+ kvm_x86_call(set_guest_tsc_deadline_virt)(apic->vcpu, 0);
}
apic->lapic_timer.timer_mode = timer_mode;
limit_periodic_timer_frequency(apic);
}
+
+ /*
+ * Update on not only timer mode change, but also mask change
+ * for the case of timer_mode = TSCDEADLINE, mask = 1.
+ */
+ apic_update_apic_virt_timer(apic);
}
/*
@@ -2265,6 +2339,9 @@ static void restart_apic_timer(struct kvm_lapic *apic)
if (!apic_lvtt_period(apic) && atomic_read(&apic->lapic_timer.pending))
goto out;
+ if (apic->lapic_timer.apic_virt_timer_in_use)
+ goto out;
+
if (!start_hv_timer(apic))
start_sw_timer(apic);
out:
diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h
index 282b9b7da98c..42fbb66f1e4e 100644
--- a/arch/x86/kvm/lapic.h
+++ b/arch/x86/kvm/lapic.h
@@ -57,6 +57,7 @@ struct kvm_timer {
u32 timer_advance_ns;
atomic_t pending; /* accumulated triggered timers */
bool hv_timer_in_use;
+ bool apic_virt_timer_in_use;
};
struct kvm_lapic {
--
2.45.2
Powered by blists - more mailing lists