Skip to content

Commit e3cdaab

Browse files
committed
KVM: x86: SVM: fix nested PAUSE filtering when L0 intercepts PAUSE
Commit 74fd41e ("KVM: x86: nSVM: support PAUSE filtering when L0 doesn't intercept PAUSE") introduced passthrough support for nested pause filtering, (when the host doesn't intercept PAUSE) (either disabled with kvm module param, or disabled with '-overcommit cpu-pm=on') Before this commit, L1 KVM didn't intercept PAUSE at all; afterwards, the feature was exposed as supported by KVM cpuid unconditionally, thus if L1 could try to use it even when the L0 KVM can't really support it. In this case the fallback caused KVM to intercept each PAUSE instruction; in some cases, such intercept can slow down the nested guest so much that it can fail to boot. Instead, before the problematic commit KVM was already setting both thresholds to 0 in vmcb02, but after the first userspace VM exit shrink_ple_window was called and would reset the pause_filter_count to the default value. To fix this, change the fallback strategy - ignore the guest threshold values, but use/update the host threshold values unless the guest specifically requests disabling PAUSE filtering (either simple or advanced). Also fix a minor bug: on nested VM exit, when PAUSE filter counter were copied back to vmcb01, a dirty bit was not set. Thanks a lot to Suravee Suthikulpanit for debugging this! Fixes: 74fd41e ("KVM: x86: nSVM: support PAUSE filtering when L0 doesn't intercept PAUSE") Reported-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> Tested-by: Suravee Suthikulpanit <suravee.suthikulpanit@amd.com> Co-developed-by: Maxim Levitsky <mlevitsk@redhat.com> Message-Id: <20220518072709.730031-1-mlevitsk@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent ba8ec27 commit e3cdaab

2 files changed

Lines changed: 23 additions & 20 deletions

File tree

arch/x86/kvm/svm/nested.c

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,8 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
616616
struct kvm_vcpu *vcpu = &svm->vcpu;
617617
struct vmcb *vmcb01 = svm->vmcb01.ptr;
618618
struct vmcb *vmcb02 = svm->nested.vmcb02.ptr;
619+
u32 pause_count12;
620+
u32 pause_thresh12;
619621

620622
/*
621623
* Filled at exit: exit_code, exit_code_hi, exit_info_1, exit_info_2,
@@ -671,27 +673,25 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm)
671673
if (!nested_vmcb_needs_vls_intercept(svm))
672674
vmcb02->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK;
673675

676+
pause_count12 = svm->pause_filter_enabled ? svm->nested.ctl.pause_filter_count : 0;
677+
pause_thresh12 = svm->pause_threshold_enabled ? svm->nested.ctl.pause_filter_thresh : 0;
674678
if (kvm_pause_in_guest(svm->vcpu.kvm)) {
675-
/* use guest values since host doesn't use them */
676-
vmcb02->control.pause_filter_count =
677-
svm->pause_filter_enabled ?
678-
svm->nested.ctl.pause_filter_count : 0;
679+
/* use guest values since host doesn't intercept PAUSE */
680+
vmcb02->control.pause_filter_count = pause_count12;
681+
vmcb02->control.pause_filter_thresh = pause_thresh12;
679682

680-
vmcb02->control.pause_filter_thresh =
681-
svm->pause_threshold_enabled ?
682-
svm->nested.ctl.pause_filter_thresh : 0;
683-
684-
} else if (!vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_PAUSE)) {
685-
/* use host values when guest doesn't use them */
683+
} else {
684+
/* start from host values otherwise */
686685
vmcb02->control.pause_filter_count = vmcb01->control.pause_filter_count;
687686
vmcb02->control.pause_filter_thresh = vmcb01->control.pause_filter_thresh;
688-
} else {
689-
/*
690-
* Intercept every PAUSE otherwise and
691-
* ignore both host and guest values
692-
*/
693-
vmcb02->control.pause_filter_count = 0;
694-
vmcb02->control.pause_filter_thresh = 0;
687+
688+
/* ... but ensure filtering is disabled if so requested. */
689+
if (vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_PAUSE)) {
690+
if (!pause_count12)
691+
vmcb02->control.pause_filter_count = 0;
692+
if (!pause_thresh12)
693+
vmcb02->control.pause_filter_thresh = 0;
694+
}
695695
}
696696

697697
nested_svm_transition_tlb_flush(vcpu);
@@ -951,8 +951,11 @@ int nested_svm_vmexit(struct vcpu_svm *svm)
951951
vmcb12->control.event_inj = svm->nested.ctl.event_inj;
952952
vmcb12->control.event_inj_err = svm->nested.ctl.event_inj_err;
953953

954-
if (!kvm_pause_in_guest(vcpu->kvm) && vmcb02->control.pause_filter_count)
954+
if (!kvm_pause_in_guest(vcpu->kvm)) {
955955
vmcb01->control.pause_filter_count = vmcb02->control.pause_filter_count;
956+
vmcb_mark_dirty(vmcb01, VMCB_INTERCEPTS);
957+
958+
}
956959

957960
nested_svm_copy_common_state(svm->nested.vmcb02.ptr, svm->vmcb01.ptr);
958961

arch/x86/kvm/svm/svm.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,7 +921,7 @@ static void grow_ple_window(struct kvm_vcpu *vcpu)
921921
struct vmcb_control_area *control = &svm->vmcb->control;
922922
int old = control->pause_filter_count;
923923

924-
if (kvm_pause_in_guest(vcpu->kvm) || !old)
924+
if (kvm_pause_in_guest(vcpu->kvm))
925925
return;
926926

927927
control->pause_filter_count = __grow_ple_window(old,
@@ -942,7 +942,7 @@ static void shrink_ple_window(struct kvm_vcpu *vcpu)
942942
struct vmcb_control_area *control = &svm->vmcb->control;
943943
int old = control->pause_filter_count;
944944

945-
if (kvm_pause_in_guest(vcpu->kvm) || !old)
945+
if (kvm_pause_in_guest(vcpu->kvm))
946946
return;
947947

948948
control->pause_filter_count =

0 commit comments

Comments
 (0)