[RFC: timer passthrough 2/9] KVM: vmx: enable host lapic timer offload preemtion timer

Zhimin Feng
2021-02-06T00:52:10Z

Use preemption timer to handle host timer

Signed-off-by: Zhimin Feng <fengzhimin@bytedance.com>
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/vmx/vmx.c | 54 +++++++++++++++++++++++++++++++++++++++++
arch/x86/kvm/x86.c | 1 +
3 files changed, 56 insertions(+)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index eb6a611963b7..82a51f0d01a2 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -826,6 +826,7 @@ struct kvm_vcpu_arch {
} pv_cpuid;

bool timer_passth_enable;
+ u64 tscd;
};

struct kvm_lpage_info {
diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 38b8d80fa157..0bf9941df842 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -5629,6 +5629,13 @@ static fastpath_t handle_fastpath_preemption_timer(struct kvm_vcpu *vcpu)
{
struct vcpu_vmx *vmx = to_vmx(vcpu);

+ if (vcpu->arch.timer_passth_enable) {
+ local_irq_disable();
+ apic->send_IPI_mask(get_cpu_mask(vcpu->cpu), LOCAL_TIMER_VECTOR);
+ local_irq_enable();
+
+ return EXIT_FASTPATH_NONE;
+ }
if (!vmx->req_immediate_exit &&
!unlikely(vmx->loaded_vmcs->hv_timer_soft_disabled)) {
kvm_lapic_expired_hv_timer(vcpu);
@@ -6640,6 +6647,51 @@ static fastpath_t vmx_exit_handlers_fastpath(struct kvm_vcpu *vcpu)

bool __vmx_vcpu_run(struct vcpu_vmx *vmx, unsigned long *regs, bool launched);

+static void vmx_host_lapic_timer_offload(struct kvm_vcpu *vcpu)
+{
+ struct timer_passth_info *local_timer_info;
+ u64 tscl;
+ u64 guest_tscl;
+ u64 delta_tsc;
+ struct hrtimer *timer;
+
+ if (!vcpu->arch.timer_passth_enable)
+ return;
+
+ local_timer_info = &per_cpu(passth_info, smp_processor_id());
+
+ tscl = rdtsc();
+ guest_tscl = kvm_read_l1_tsc(vcpu, tscl);
+
+ timer = &vcpu->arch.apic->lapic_timer.timer;
+ if (hrtimer_active(timer))
+ hrtimer_cancel(timer);
+
+ if (local_timer_info->host_tscd > tscl) {
+ delta_tsc = (u32)((local_timer_info->host_tscd - tscl) >>
+ cpu_preemption_timer_multi);
+ vmcs_write32(VMX_PREEMPTION_TIMER_VALUE, delta_tsc);
+ vmcs_set_bits(PIN_BASED_VM_EXEC_CONTROL,
+ PIN_BASED_VMX_PREEMPTION_TIMER);
+ } else {
+ vmcs_clear_bits(PIN_BASED_VM_EXEC_CONTROL,
+ PIN_BASED_VMX_PREEMPTION_TIMER);
+ }
+
+ wrmsrl(MSR_IA32_TSCDEADLINE, 0);
+ if (vcpu->arch.tscd > guest_tscl) {
+ wrmsrl(MSR_IA32_TSCDEADLINE, vcpu->arch.tscd);
+ } else {
+ if (vcpu->arch.tscd > 0) {
+ if (!atomic_read(&vcpu->arch.apic->lapic_timer.pending)) {
+ atomic_inc(&vcpu->arch.apic->lapic_timer.pending);
+ kvm_inject_pending_timer_irqs(vcpu);
+ kvm_x86_ops.sync_pir_to_irr(vcpu);
+ }
+ }
+ }
+}
+
static noinstr void vmx_vcpu_enter_exit(struct kvm_vcpu *vcpu,
struct vcpu_vmx *vmx)
{
@@ -6761,6 +6813,8 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu)

kvm_wait_lapic_expire(vcpu);

+ vmx_host_lapic_timer_offload(vcpu);
+
/*
* If this vCPU has touched SPEC_CTRL, restore the guest's value if
* it's non-zero. Since vmentry is serialising on affected CPUs, there
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 5d353a9c9881..e51fd52a4862 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -9912,6 +9912,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
vcpu->arch.pending_external_vector = -1;
vcpu->arch.preempted_in_kernel = false;
vcpu->arch.timer_passth_enable = false;
+ vcpu->arch.tscd = 0;

kvm_hv_vcpu_init(vcpu);

--
2.11.0