[RFC: timer passthrough 8/9] KVM: vmx: Dynamically open or close the timer-passthrough for pre-vm

Zhimin Feng
2021-02-05T10:09:49Z

Timer passthrough is default disabled

Signed-off-by: Zhimin Feng <fengzhimin@bytedance.com>
---
arch/x86/include/asm/kvm_host.h | 3 +--
arch/x86/kvm/lapic.c | 10 +-------
arch/x86/kvm/vmx/vmx.c | 52 +++++++++++++++++++++++++++++++++++++----
arch/x86/kvm/x86.c | 6 +++++
include/linux/kvm_host.h | 1 +
include/uapi/linux/kvm.h | 2 ++
tools/include/uapi/linux/kvm.h | 2 ++
virt/kvm/kvm_main.c | 1 +
8 files changed, 62 insertions(+), 15 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 7971c9e755a4..9855ef419793 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1304,9 +1304,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);
void (*switch_to_sw_timer)(struct kvm_vcpu *vcpu);
+ int (*set_timer_passth_state)(struct kvm *kvm, void *argp);
};

struct kvm_x86_nested_ops {
diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index 9b2f8b99fbf6..9ba4157f9b81 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -1508,15 +1508,6 @@ 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);
- }
- }
}
}

@@ -2219,6 +2210,7 @@ void kvm_set_lapic_tscdeadline_msr(struct kvm_vcpu *vcpu, u64 data)

hrtimer_cancel(&apic->lapic_timer.timer);
apic->lapic_timer.tscdeadline = data;
+ vcpu->arch.tscd = data;
start_apic_timer(apic);
}

diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c
index 98eca70d4251..b88f744478e9 100644
--- a/arch/x86/kvm/vmx/vmx.c
+++ b/arch/x86/kvm/vmx/vmx.c
@@ -216,6 +216,8 @@ static DEFINE_MUTEX(vmx_l1d_flush_mutex);
/* Storage for pre module init parameter parsing */
static enum vmx_l1d_flush_state __read_mostly vmentry_l1d_flush_param = VMENTER_L1D_FLUSH_AUTO;

+static void vmx_set_timer_passthrough(struct kvm_vcpu *vcpu, bool enable);
+
static const struct {
const char *option;
bool for_parse;
@@ -6742,9 +6744,9 @@ static void vmx_restore_passth_timer(struct kvm_vcpu *vcpu)
host_tscd = local_timer_info->host_tscd;
rdmsrl(MSR_IA32_TSC_DEADLINE, guest_tscd);

- if (guest_tscd != 0 &&
- guest_tscd != host_tscd) {
+ if (guest_tscd != 0 && guest_tscd != host_tscd) {
vcpu->arch.tscd = guest_tscd;
+ vcpu->arch.apic->lapic_timer.tscdeadline = vcpu->arch.tscd;
}

if (host_tscd > rdtsc())
@@ -6873,6 +6875,15 @@ static fastpath_t vmx_vcpu_run(struct kvm_vcpu *vcpu)

kvm_wait_lapic_expire(vcpu);

+ if (vcpu->arch.timer_passth_enable) {
+ if (!atomic_read(&vcpu->kvm->timer_passth_state)) {
+ vcpu->arch.apic->lapic_timer.tscdeadline =
+ vcpu->arch.tscd;
+ vmx_set_timer_passthrough(vcpu, false);
+ }
+ } else if (atomic_read(&vcpu->kvm->timer_passth_state)) {
+ vmx_set_timer_passthrough(vcpu, true);
+ }
vmx_host_lapic_timer_offload(vcpu);

/*
@@ -7838,6 +7849,40 @@ static bool vmx_check_apicv_inhibit_reasons(ulong bit)
return supported & BIT(bit);
}

+static int vmx_set_timer_passth_state(struct kvm *kvm, void *argp)
+{
+ int r = -1;
+ int i;
+ struct kvm_vcpu *vcpu;
+ int state;
+
+ if (copy_from_user(&state, argp, sizeof(int)))
+ goto out;
+
+ if (!!state) {
+ /* judge whether support timer-pasth */
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ if (!vmx_host_timer_can_passth(vcpu) ||
+ (vcpu->arch.apic->lapic_timer.timer_mode !=
+ APIC_LVT_TIMER_TSCDEADLINE)) {
+ pr_err("host don't support timer passthrough\n");
+ goto out;
+ }
+ }
+ }
+
+ if (kvm->timer_passth_state.counter != (!!state)) {
+ atomic_set(&kvm->timer_passth_state, !!state);
+ kvm_for_each_vcpu(i, vcpu, kvm) {
+ kvm_vcpu_kick(vcpu);
+ }
+ }
+ r = 0;
+
+out:
+ return r;
+}
+
static struct kvm_x86_ops vmx_x86_ops __initdata = {
.hardware_unsetup = hardware_unsetup,

@@ -7966,9 +8011,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,
.switch_to_sw_timer = vmx_passth_switch_to_sw_timer,
+ .set_timer_passth_state = vmx_set_timer_passth_state,
};

static __init int hardware_setup(void)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 2b4aa925d6d9..7db74bd9d362 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5692,6 +5692,12 @@ long kvm_arch_vm_ioctl(struct file *filp,
case KVM_X86_SET_MSR_FILTER:
r = kvm_vm_ioctl_set_msr_filter(kvm, argp);
break;
+ case KVM_SET_TIMER_PASSTH_STATE: {
+ r = -EFAULT;
+ if (kvm_x86_ops.set_timer_passth_state)
+ r = kvm_x86_ops.set_timer_passth_state(kvm, argp);
+ break;
+ }
default:
r = -ENOTTY;
}
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 7f2e2a09ebbd..b3de12c3f473 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -505,6 +505,7 @@ struct kvm {
struct srcu_struct irq_srcu;
pid_t userspace_pid;
unsigned int max_halt_poll_ns;
+ atomic_t timer_passth_state;
};

#define kvm_err(fmt, ...) \
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index ca41220b40b8..6e26bc342599 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1557,6 +1557,8 @@ struct kvm_pv_cmd {
/* Available with KVM_CAP_X86_MSR_FILTER */
#define KVM_X86_SET_MSR_FILTER _IOW(KVMIO, 0xc6, struct kvm_msr_filter)

+#define KVM_SET_TIMER_PASSTH_STATE _IO(KVMIO, 0xc7)
+
/* Secure Encrypted Virtualization command */
enum sev_cmd_id {
/* Guest initialization commands */
diff --git a/tools/include/uapi/linux/kvm.h b/tools/include/uapi/linux/kvm.h
index ca41220b40b8..6e26bc342599 100644
--- a/tools/include/uapi/linux/kvm.h
+++ b/tools/include/uapi/linux/kvm.h
@@ -1557,6 +1557,8 @@ struct kvm_pv_cmd {
/* Available with KVM_CAP_X86_MSR_FILTER */
#define KVM_X86_SET_MSR_FILTER _IOW(KVMIO, 0xc6, struct kvm_msr_filter)

+#define KVM_SET_TIMER_PASSTH_STATE _IO(KVMIO, 0xc7)
+
/* Secure Encrypted Virtualization command */
enum sev_cmd_id {
/* Guest initialization commands */
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 2541a17ff1c4..7e7a3adede62 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -751,6 +751,7 @@ static struct kvm *kvm_create_vm(unsigned long type)
mutex_init(&kvm->irq_lock);
mutex_init(&kvm->slots_lock);
INIT_LIST_HEAD(&kvm->devices);
+ atomic_set(&kvm->timer_passth_state, 0);

BUILD_BUG_ON(KVM_MEM_SLOTS_NUM > SHRT_MAX);

--
2.11.0