Commit d2060bd4 authored by Sean Christopherson's avatar Sean Christopherson Committed by Paolo Bonzini

KVM: nVMX: Open a window for pending nested VMX preemption timer

Add a kvm_x86_ops hook to detect a nested pending "hypervisor timer" and
use it to effectively open a window for servicing the expired timer.
Like pending SMIs on VMX, opening a window simply means requesting an
immediate exit.

This fixes a bug where an expired VMX preemption timer (for L2) will be
delayed and/or lost if a pending exception is injected into L2.  The
pending exception is rightly prioritized by vmx_check_nested_events()
and injected into L2, with the preemption timer left pending.  Because
no window opened, L2 is free to run uninterrupted.

Fixes: f4124500 ("KVM: nVMX: Fully emulate preemption timer")
Reported-by: default avatarJim Mattson <jmattson@google.com>
Cc: Oliver Upton <oupton@google.com>
Cc: Peter Shier <pshier@google.com>
Signed-off-by: default avatarSean Christopherson <sean.j.christopherson@intel.com>
Message-Id: <20200423022550.15113-3-sean.j.christopherson@intel.com>
[Check it in kvm_vcpu_has_events too, to ensure that the preemption
 timer is serviced promptly even if the vCPU is halted and L1 is not
 intercepting HLT. - Paolo]
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 6ce347af
...@@ -1256,6 +1256,7 @@ struct kvm_x86_ops { ...@@ -1256,6 +1256,7 @@ struct kvm_x86_ops {
struct kvm_x86_nested_ops { struct kvm_x86_nested_ops {
int (*check_events)(struct kvm_vcpu *vcpu); int (*check_events)(struct kvm_vcpu *vcpu);
bool (*hv_timer_pending)(struct kvm_vcpu *vcpu);
int (*get_state)(struct kvm_vcpu *vcpu, int (*get_state)(struct kvm_vcpu *vcpu,
struct kvm_nested_state __user *user_kvm_nested_state, struct kvm_nested_state __user *user_kvm_nested_state,
unsigned user_data_size); unsigned user_data_size);
......
...@@ -3687,6 +3687,12 @@ static void nested_vmx_update_pending_dbg(struct kvm_vcpu *vcpu) ...@@ -3687,6 +3687,12 @@ static void nested_vmx_update_pending_dbg(struct kvm_vcpu *vcpu)
vcpu->arch.exception.payload); vcpu->arch.exception.payload);
} }
static bool nested_vmx_preemption_timer_pending(struct kvm_vcpu *vcpu)
{
return nested_cpu_has_preemption_timer(get_vmcs12(vcpu)) &&
to_vmx(vcpu)->nested.preemption_timer_expired;
}
static int vmx_check_nested_events(struct kvm_vcpu *vcpu) static int vmx_check_nested_events(struct kvm_vcpu *vcpu)
{ {
struct vcpu_vmx *vmx = to_vmx(vcpu); struct vcpu_vmx *vmx = to_vmx(vcpu);
...@@ -3742,8 +3748,7 @@ static int vmx_check_nested_events(struct kvm_vcpu *vcpu) ...@@ -3742,8 +3748,7 @@ static int vmx_check_nested_events(struct kvm_vcpu *vcpu)
return 0; return 0;
} }
if (nested_cpu_has_preemption_timer(get_vmcs12(vcpu)) && if (nested_vmx_preemption_timer_pending(vcpu)) {
vmx->nested.preemption_timer_expired) {
if (block_nested_events) if (block_nested_events)
return -EBUSY; return -EBUSY;
nested_vmx_vmexit(vcpu, EXIT_REASON_PREEMPTION_TIMER, 0, 0); nested_vmx_vmexit(vcpu, EXIT_REASON_PREEMPTION_TIMER, 0, 0);
...@@ -6448,6 +6453,7 @@ __init int nested_vmx_hardware_setup(struct kvm_x86_ops *ops, ...@@ -6448,6 +6453,7 @@ __init int nested_vmx_hardware_setup(struct kvm_x86_ops *ops,
struct kvm_x86_nested_ops vmx_nested_ops = { struct kvm_x86_nested_ops vmx_nested_ops = {
.check_events = vmx_check_nested_events, .check_events = vmx_check_nested_events,
.hv_timer_pending = nested_vmx_preemption_timer_pending,
.get_state = vmx_get_nested_state, .get_state = vmx_get_nested_state,
.set_state = vmx_set_nested_state, .set_state = vmx_set_nested_state,
.get_vmcs12_pages = nested_get_vmcs12_pages, .get_vmcs12_pages = nested_get_vmcs12_pages,
......
...@@ -8355,6 +8355,10 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) ...@@ -8355,6 +8355,10 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
kvm_x86_ops.enable_nmi_window(vcpu); kvm_x86_ops.enable_nmi_window(vcpu);
if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win) if (kvm_cpu_has_injectable_intr(vcpu) || req_int_win)
kvm_x86_ops.enable_irq_window(vcpu); kvm_x86_ops.enable_irq_window(vcpu);
if (is_guest_mode(vcpu) &&
kvm_x86_ops.nested_ops->hv_timer_pending &&
kvm_x86_ops.nested_ops->hv_timer_pending(vcpu))
req_immediate_exit = true;
WARN_ON(vcpu->arch.exception.pending); WARN_ON(vcpu->arch.exception.pending);
} }
...@@ -10211,6 +10215,11 @@ static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu) ...@@ -10211,6 +10215,11 @@ static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu)
if (kvm_hv_has_stimer_pending(vcpu)) if (kvm_hv_has_stimer_pending(vcpu))
return true; return true;
if (is_guest_mode(vcpu) &&
kvm_x86_ops.nested_ops->hv_timer_pending &&
kvm_x86_ops.nested_ops->hv_timer_pending(vcpu))
return true;
return false; return false;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment