[RFC: timer passthrough 3/9] KVM: vmx: enable passthrough timer to guest

Zhimin Feng
2021-02-06T00:51:09Z

Allow Guest to write tscdeadline msr directly.

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

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 82a51f0d01a2..500fa031297d 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -533,6 +533,7 @@ struct tick_device {

struct timer_passth_info {
u64 host_tscd;
+ bool host_in_tscdeadline;
struct clock_event_device *curr_dev;

void (*orig_event_handler)(struct clock_event_device *dev);
@@ -1302,6 +1303,8 @@ struct kvm_x86_ops {

void (*migrate_timers)(struct kvm_vcpu *vcpu);
void (*msr_filter_changed)(struct kvm_vcpu *vcpu);
+ void (*set_timer_passthrough)(struct kvm_vcpu *vcpu, bool enable);
+ int (*host_timer_can_passth)(struct kvm_vcpu *vcpu);
};

struct kvm_x86_nested_ops {
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 86c33d53c90a..9b2f8b99fbf6 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -1508,6 +1508,15 @@ static void apic_update_lvtt(struct kvm_lapic *apic)
}
apic->lapic_timer.timer_mode = timer_mode;
limit_periodic_timer_frequency(apic);
+
+ if (kvm_x86_ops.host_timer_can_passth(apic->vcpu)) {
+ if (apic_lvtt_tscdeadline(apic)) {
+ kvm_x86_ops.set_timer_passthrough(apic->vcpu, true);
+ } else {
+ if (apic->vcpu->arch.timer_passth_enable)
+ kvm_x86_ops.set_timer_passthrough(apic->vcpu, false);
+ }
+ }
}
}

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 0bf9941df842..0c1b5ee4bb8e 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -47,6 +47,7 @@
#include <asm/spec-ctrl.h>
#include <asm/virtext.h>
#include <asm/vmx.h>
+#include <asm/apicdef.h>

#include "capabilities.h"
#include "cpuid.h"
@@ -705,6 +706,8 @@ static bool is_valid_passthrough_msr(u32 msr)
case MSR_IA32_RTIT_ADDR0_A ... MSR_IA32_RTIT_ADDR3_B:
/* PT MSRs. These are handled in pt_update_intercept_for_msr() */
return true;
+ case MSR_IA32_TSC_DEADLINE:
+ return true;
}

r = possible_passthrough_msr_slot(msr) != -ENOENT;
@@ -7670,6 +7673,54 @@ static void vmx_migrate_timers(struct kvm_vcpu *vcpu)
}
}

+static void host_lapic_timer_in_deadline(void *junk)
+{
+ unsigned int v;
+ struct timer_passth_info *local_timer_info;
+ int cpu = smp_processor_id();
+
+ local_timer_info = &per_cpu(passth_info, cpu);
+ v = apic_read(APIC_LVTT);
+ local_timer_info->host_in_tscdeadline = (v & APIC_LVT_TIMER_TSCDEADLINE);
+}
+
+static bool host_all_cpu_in_tscdeadline(void)
+{
+ int cpu;
+ struct timer_passth_info *local_timer_info;
+
+ for_each_online_cpu(cpu) {
+ local_timer_info = &per_cpu(passth_info, cpu);
+ if (!local_timer_info->host_in_tscdeadline)
+ return false;
+ }
+
+ return true;
+}
+
+static int vmx_host_timer_can_passth(struct kvm_vcpu *vcpu)
+{
+ if (!enable_timer_passth || !cpu_has_vmx_msr_bitmap() ||
+ !host_all_cpu_in_tscdeadline())
+ return 0;
+ return 1;
+}
+
+static void vmx_set_timer_passthrough(struct kvm_vcpu *vcpu, bool enable)
+{
+ if (enable) {
+ vmx_disable_intercept_for_msr(vcpu, MSR_IA32_TSC_DEADLINE,
+ MSR_TYPE_RW);
+ vcpu->arch.timer_passth_enable = 1;
+ } else {
+ vmx_enable_intercept_for_msr(vcpu, MSR_IA32_TSC_DEADLINE,
+ MSR_TYPE_RW);
+ vmcs_clear_bits(PIN_BASED_VM_EXEC_CONTROL,
+ PIN_BASED_VMX_PREEMPTION_TIMER);
+ vcpu->arch.timer_passth_enable = 0;
+ }
+}
+
static void hardware_unsetup(void)
{
if (enable_timer_passth)
@@ -7817,6 +7868,8 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = {
.migrate_timers = vmx_migrate_timers,

.msr_filter_changed = vmx_msr_filter_changed,
+ .set_timer_passthrough = vmx_set_timer_passthrough,
+ .host_timer_can_passth = vmx_host_timer_can_passth,
};

static __init int hardware_setup(void)
@@ -7987,6 +8040,9 @@ static __init int hardware_setup(void)
vmx_set_cpu_caps();

if (enable_timer_passth)
+ on_each_cpu(host_lapic_timer_in_deadline, NULL, 1);
+
+ if (enable_timer_passth)
on_each_cpu(vmx_host_timer_passth_init, NULL, 1);

r = alloc_kvm_area();
--
2.11.0