• Yan Zhao's avatar
    KVM: x86/mmu: Introduce a quirk to control memslot zap behavior · aa8d1f48
    Yan Zhao authored
    Introduce the quirk KVM_X86_QUIRK_SLOT_ZAP_ALL to allow users to select
    KVM's behavior when a memslot is moved or deleted for KVM_X86_DEFAULT_VM
    VMs. Make sure KVM behave as if the quirk is always disabled for
    non-KVM_X86_DEFAULT_VM VMs.
    
    The KVM_X86_QUIRK_SLOT_ZAP_ALL quirk offers two behavior options:
    - when enabled:  Invalidate/zap all SPTEs ("zap-all"),
    - when disabled: Precisely zap only the leaf SPTEs within the range of the
                     moving/deleting memory slot ("zap-slot-leafs-only").
    
    "zap-all" is today's KVM behavior to work around a bug [1] where the
    changing the zapping behavior of memslot move/deletion would cause VM
    instability for VMs with an Nvidia GPU assigned; while
    "zap-slot-leafs-only" allows for more precise zapping of SPTEs within the
    memory slot range, improving performance in certain scenarios [2], and
    meeting the functional requirements for TDX.
    
    Previous attempts to select "zap-slot-leafs-only" include a per-VM
    capability approach [3] (which was not preferred because the root cause of
    the bug remained unidentified) and a per-memslot flag approach [4]. Sean
    and Paolo finally recommended the implementation of this quirk and
    explained that it's the least bad option [5].
    
    By default, the quirk is enabled on KVM_X86_DEFAULT_VM VMs to use
    "zap-all". Users have the option to disable the quirk to select
    "zap-slot-leafs-only" for specific KVM_X86_DEFAULT_VM VMs that are
    unaffected by this bug.
    
    For non-KVM_X86_DEFAULT_VM VMs, the "zap-slot-leafs-only" behavior is
    always selected without user's opt-in, regardless of if the user opts for
    "zap-all".
    This is because it is assumed until proven otherwise that non-
    KVM_X86_DEFAULT_VM VMs will not be exposed to the bug [1], and most
    importantly, it's because TDX must have "zap-slot-leafs-only" always
    selected. In TDX's case a memslot's GPA range can be a mixture of "private"
    or "shared" memory. Shared is roughly analogous to how EPT is handled for
    normal VMs, but private GPAs need lots of special treatment:
    1) "zap-all" would require to zap private root page or non-leaf entries or
       at least leaf-entries beyond the deleting memslot scope. However, TDX
       demands that the root page of the private page table remains unchanged,
       with leaf entries being zapped before non-leaf entries, and any dropped
       private guest pages must be re-accepted by the guest.
    2) if "zap-all" zaps only shared page tables, it would result in private
       pages still being mapped when the memslot is gone. This may affect even
       other processes if later the gmem fd was whole punched, causing the
       pages being freed on the host while still mapped in the TD, because
       there's no pgoff to the gfn information to zap the private page table
       after memslot is gone.
    
    So, simply go "zap-slot-leafs-only" as if the quirk is always disabled for
    non-KVM_X86_DEFAULT_VM VMs to avoid manual opt-in for every VM type [6] or
    complicating quirk disabling interface (current quirk disabling interface
    is limited, no way to query quirks, or force them to be disabled).
    
    Add a new function kvm_mmu_zap_memslot_leafs() to implement
    "zap-slot-leafs-only". This function does not call kvm_unmap_gfn_range(),
    bypassing special handling to APIC_ACCESS_PAGE_PRIVATE_MEMSLOT, as
    1) The APIC_ACCESS_PAGE_PRIVATE_MEMSLOT cannot be created by users, nor can
       it be moved. It is only deleted by KVM when APICv is permanently
       inhibited.
    2) kvm_vcpu_reload_apic_access_page() effectively does nothing when
       APIC_ACCESS_PAGE_PRIVATE_MEMSLOT is deleted.
    3) Avoid making all cpus request of KVM_REQ_APIC_PAGE_RELOAD can save on
       costly IPIs.
    Suggested-by: default avatarKai Huang <kai.huang@intel.com>
    Suggested-by: default avatarSean Christopherson <seanjc@google.com>
    Suggested-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
    Link: https://patchwork.kernel.org/project/kvm/patch/20190205210137.1377-11-sean.j.christopherson@intel.com [1]
    Link: https://patchwork.kernel.org/project/kvm/patch/20190205210137.1377-11-sean.j.christopherson@intel.com/#25054908 [2]
    Link: https://lore.kernel.org/kvm/20200713190649.GE29725@linux.intel.com/T/#mabc0119583dacf621025e9d873c85f4fbaa66d5c [3]
    Link: https://lore.kernel.org/all/20240515005952.3410568-3-rick.p.edgecombe@intel.com [4]
    Link: https://lore.kernel.org/all/7df9032d-83e4-46a1-ab29-6c7973a2ab0b@redhat.com [5]
    Link: https://lore.kernel.org/all/ZnGa550k46ow2N3L@google.com [6]
    Co-developed-by: default avatarRick Edgecombe <rick.p.edgecombe@intel.com>
    Signed-off-by: default avatarRick Edgecombe <rick.p.edgecombe@intel.com>
    Signed-off-by: default avatarYan Zhao <yan.y.zhao@intel.com>
    Message-ID: <20240703021043.13881-1-yan.y.zhao@intel.com>
    Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
    aa8d1f48
kvm.h 20.5 KB