lkml.org 
[lkml]   [2022]   [Apr]   [26]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.17 131/146] KVM: nVMX: Defer APICv updates while L2 is active until L1 is active
    Date
    From: Sean Christopherson <seanjc@google.com>

    commit 7c69661e225cc484fbf44a0b99b56714a5241ae3 upstream.

    Defer APICv updates that occur while L2 is active until nested VM-Exit,
    i.e. until L1 regains control. vmx_refresh_apicv_exec_ctrl() assumes L1
    is active and (a) stomps all over vmcs02 and (b) neglects to ever updated
    vmcs01. E.g. if vmcs12 doesn't enable the TPR shadow for L2 (and thus no
    APICv controls), L1 performs nested VM-Enter APICv inhibited, and APICv
    becomes unhibited while L2 is active, KVM will set various APICv controls
    in vmcs02 and trigger a failed VM-Entry. The kicker is that, unless
    running with nested_early_check=1, KVM blames L1 and chaos ensues.

    In all cases, ignoring vmcs02 and always deferring the inhibition change
    to vmcs01 is correct (or at least acceptable). The ABSENT and DISABLE
    inhibitions cannot truly change while L2 is active (see below).

    IRQ_BLOCKING can change, but it is firmly a best effort debug feature.
    Furthermore, only L2's APIC is accelerated/virtualized to the full extent
    possible, e.g. even if L1 passes through its APIC to L2, normal MMIO/MSR
    interception will apply to the virtual APIC managed by KVM.
    The exception is the SELF_IPI register when x2APIC is enabled, but that's
    an acceptable hole.

    Lastly, Hyper-V's Auto EOI can technically be toggled if L1 exposes the
    MSRs to L2, but for that to work in any sane capacity, L1 would need to
    pass through IRQs to L2 as well, and IRQs must be intercepted to enable
    virtual interrupt delivery. I.e. exposing Auto EOI to L2 and enabling
    VID for L2 are, for all intents and purposes, mutually exclusive.

    Lack of dynamic toggling is also why this scenario is all but impossible
    to encounter in KVM's current form. But a future patch will pend an
    APICv update request _during_ vCPU creation to plug a race where a vCPU
    that's being created doesn't get included in the "all vCPUs request"
    because it's not yet visible to other vCPUs. If userspaces restores L2
    after VM creation (hello, KVM selftests), the first KVM_RUN will occur
    while L2 is active and thus service the APICv update request made during
    VM creation.

    Cc: stable@vger.kernel.org
    Signed-off-by: Sean Christopherson <seanjc@google.com>
    Message-Id: <20220420013732.3308816-3-seanjc@google.com>
    Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
    ---
    arch/x86/kvm/vmx/nested.c | 5 +++++
    arch/x86/kvm/vmx/vmx.c | 5 +++++
    arch/x86/kvm/vmx/vmx.h | 1 +
    3 files changed, 11 insertions(+)

    --- a/arch/x86/kvm/vmx/nested.c
    +++ b/arch/x86/kvm/vmx/nested.c
    @@ -4618,6 +4618,11 @@ void nested_vmx_vmexit(struct kvm_vcpu *
    kvm_make_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu);
    }

    + if (vmx->nested.update_vmcs01_apicv_status) {
    + vmx->nested.update_vmcs01_apicv_status = false;
    + kvm_make_request(KVM_REQ_APICV_UPDATE, vcpu);
    + }
    +
    if ((vm_exit_reason != -1) &&
    (enable_shadow_vmcs || evmptr_is_valid(vmx->nested.hv_evmcs_vmptr)))
    vmx->nested.need_vmcs12_to_shadow_sync = true;
    --- a/arch/x86/kvm/vmx/vmx.c
    +++ b/arch/x86/kvm/vmx/vmx.c
    @@ -4182,6 +4182,11 @@ static void vmx_refresh_apicv_exec_ctrl(
    {
    struct vcpu_vmx *vmx = to_vmx(vcpu);

    + if (is_guest_mode(vcpu)) {
    + vmx->nested.update_vmcs01_apicv_status = true;
    + return;
    + }
    +
    pin_controls_set(vmx, vmx_pin_based_exec_ctrl(vmx));
    if (cpu_has_secondary_exec_ctrls()) {
    if (kvm_vcpu_apicv_active(vcpu))
    --- a/arch/x86/kvm/vmx/vmx.h
    +++ b/arch/x86/kvm/vmx/vmx.h
    @@ -183,6 +183,7 @@ struct nested_vmx {
    bool change_vmcs01_virtual_apic_mode;
    bool reload_vmcs01_apic_access_page;
    bool update_vmcs01_cpu_dirty_logging;
    + bool update_vmcs01_apicv_status;

    /*
    * Enlightened VMCS has been enabled. It does not mean that L1 has to

    \
     
     \ /
      Last update: 2022-04-26 11:35    [W:2.301 / U:0.084 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site