mirror of https://gitee.com/openkylin/linux.git
KVM: x86: hyper-v: Move the remote TLB flush logic out of vmx
Currently the remote TLB flush logic is specific to VMX. Move it to a common place so that SVM can use it as well. Signed-off-by: Vineeth Pillai <viremana@linux.microsoft.com> Message-Id: <4f4e4ca19778437dae502f44363a38e99e3ef5d1.1622730232.git.viremana@linux.microsoft.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
parent
32431fb253
commit
3c86c0d3db
|
@ -852,6 +852,10 @@ struct kvm_vcpu_arch {
|
||||||
|
|
||||||
/* Protected Guests */
|
/* Protected Guests */
|
||||||
bool guest_state_protected;
|
bool guest_state_protected;
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_HYPERV)
|
||||||
|
hpa_t hv_root_tdp;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kvm_lpage_info {
|
struct kvm_lpage_info {
|
||||||
|
@ -1131,6 +1135,11 @@ struct kvm_arch {
|
||||||
* allocated for any newly created or modified memslots.
|
* allocated for any newly created or modified memslots.
|
||||||
*/
|
*/
|
||||||
bool memslots_have_rmaps;
|
bool memslots_have_rmaps;
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_HYPERV)
|
||||||
|
hpa_t hv_root_tdp;
|
||||||
|
spinlock_t hv_root_tdp_lock;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct kvm_vm_stat {
|
struct kvm_vm_stat {
|
||||||
|
|
|
@ -18,6 +18,11 @@ kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \
|
||||||
i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
|
i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
|
||||||
hyperv.o debugfs.o mmu/mmu.o mmu/page_track.o \
|
hyperv.o debugfs.o mmu/mmu.o mmu/page_track.o \
|
||||||
mmu/spte.o
|
mmu/spte.o
|
||||||
|
|
||||||
|
ifdef CONFIG_HYPERV
|
||||||
|
kvm-y += kvm_onhyperv.o
|
||||||
|
endif
|
||||||
|
|
||||||
kvm-$(CONFIG_X86_64) += mmu/tdp_iter.o mmu/tdp_mmu.o
|
kvm-$(CONFIG_X86_64) += mmu/tdp_iter.o mmu/tdp_mmu.o
|
||||||
kvm-$(CONFIG_KVM_XEN) += xen.o
|
kvm-$(CONFIG_KVM_XEN) += xen.o
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* KVM L1 hypervisor optimizations on Hyper-V.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/kvm_host.h>
|
||||||
|
#include <asm/mshyperv.h>
|
||||||
|
|
||||||
|
#include "hyperv.h"
|
||||||
|
#include "kvm_onhyperv.h"
|
||||||
|
|
||||||
|
static int kvm_fill_hv_flush_list_func(struct hv_guest_mapping_flush_list *flush,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
struct kvm_tlb_range *range = data;
|
||||||
|
|
||||||
|
return hyperv_fill_flush_guest_mapping_list(flush, range->start_gfn,
|
||||||
|
range->pages);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int hv_remote_flush_root_tdp(hpa_t root_tdp,
|
||||||
|
struct kvm_tlb_range *range)
|
||||||
|
{
|
||||||
|
if (range)
|
||||||
|
return hyperv_flush_guest_mapping_range(root_tdp,
|
||||||
|
kvm_fill_hv_flush_list_func, (void *)range);
|
||||||
|
else
|
||||||
|
return hyperv_flush_guest_mapping(root_tdp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hv_remote_flush_tlb_with_range(struct kvm *kvm,
|
||||||
|
struct kvm_tlb_range *range)
|
||||||
|
{
|
||||||
|
struct kvm_arch *kvm_arch = &kvm->arch;
|
||||||
|
struct kvm_vcpu *vcpu;
|
||||||
|
int ret = 0, i, nr_unique_valid_roots;
|
||||||
|
hpa_t root;
|
||||||
|
|
||||||
|
spin_lock(&kvm_arch->hv_root_tdp_lock);
|
||||||
|
|
||||||
|
if (!VALID_PAGE(kvm_arch->hv_root_tdp)) {
|
||||||
|
nr_unique_valid_roots = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Flush all valid roots, and see if all vCPUs have converged
|
||||||
|
* on a common root, in which case future flushes can skip the
|
||||||
|
* loop and flush the common root.
|
||||||
|
*/
|
||||||
|
kvm_for_each_vcpu(i, vcpu, kvm) {
|
||||||
|
root = vcpu->arch.hv_root_tdp;
|
||||||
|
if (!VALID_PAGE(root) || root == kvm_arch->hv_root_tdp)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the tracked root to the first valid root. Keep
|
||||||
|
* this root for the entirety of the loop even if more
|
||||||
|
* roots are encountered as a low effort optimization
|
||||||
|
* to avoid flushing the same (first) root again.
|
||||||
|
*/
|
||||||
|
if (++nr_unique_valid_roots == 1)
|
||||||
|
kvm_arch->hv_root_tdp = root;
|
||||||
|
|
||||||
|
if (!ret)
|
||||||
|
ret = hv_remote_flush_root_tdp(root, range);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop processing roots if a failure occurred and
|
||||||
|
* multiple valid roots have already been detected.
|
||||||
|
*/
|
||||||
|
if (ret && nr_unique_valid_roots > 1)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The optimized flush of a single root can't be used if there
|
||||||
|
* are multiple valid roots (obviously).
|
||||||
|
*/
|
||||||
|
if (nr_unique_valid_roots > 1)
|
||||||
|
kvm_arch->hv_root_tdp = INVALID_PAGE;
|
||||||
|
} else {
|
||||||
|
ret = hv_remote_flush_root_tdp(kvm_arch->hv_root_tdp, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock(&kvm_arch->hv_root_tdp_lock);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(hv_remote_flush_tlb_with_range);
|
||||||
|
|
||||||
|
int hv_remote_flush_tlb(struct kvm *kvm)
|
||||||
|
{
|
||||||
|
return hv_remote_flush_tlb_with_range(kvm, NULL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(hv_remote_flush_tlb);
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/*
|
||||||
|
* KVM L1 hypervisor optimizations on Hyper-V.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __ARCH_X86_KVM_KVM_ONHYPERV_H__
|
||||||
|
#define __ARCH_X86_KVM_KVM_ONHYPERV_H__
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_HYPERV)
|
||||||
|
int hv_remote_flush_tlb_with_range(struct kvm *kvm,
|
||||||
|
struct kvm_tlb_range *range);
|
||||||
|
int hv_remote_flush_tlb(struct kvm *kvm);
|
||||||
|
|
||||||
|
static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
|
||||||
|
{
|
||||||
|
struct kvm_arch *kvm_arch = &vcpu->kvm->arch;
|
||||||
|
|
||||||
|
if (kvm_x86_ops.tlb_remote_flush == hv_remote_flush_tlb) {
|
||||||
|
spin_lock(&kvm_arch->hv_root_tdp_lock);
|
||||||
|
vcpu->arch.hv_root_tdp = root_tdp;
|
||||||
|
if (root_tdp != kvm_arch->hv_root_tdp)
|
||||||
|
kvm_arch->hv_root_tdp = INVALID_PAGE;
|
||||||
|
spin_unlock(&kvm_arch->hv_root_tdp_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else /* !CONFIG_HYPERV */
|
||||||
|
static inline void hv_track_root_tdp(struct kvm_vcpu *vcpu, hpa_t root_tdp)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
#endif /* !CONFIG_HYPERV */
|
||||||
|
|
||||||
|
#endif
|
|
@ -52,6 +52,7 @@
|
||||||
#include "cpuid.h"
|
#include "cpuid.h"
|
||||||
#include "evmcs.h"
|
#include "evmcs.h"
|
||||||
#include "hyperv.h"
|
#include "hyperv.h"
|
||||||
|
#include "kvm_onhyperv.h"
|
||||||
#include "irq.h"
|
#include "irq.h"
|
||||||
#include "kvm_cache_regs.h"
|
#include "kvm_cache_regs.h"
|
||||||
#include "lapic.h"
|
#include "lapic.h"
|
||||||
|
@ -458,86 +459,6 @@ static unsigned long host_idt_base;
|
||||||
static bool __read_mostly enlightened_vmcs = true;
|
static bool __read_mostly enlightened_vmcs = true;
|
||||||
module_param(enlightened_vmcs, bool, 0444);
|
module_param(enlightened_vmcs, bool, 0444);
|
||||||
|
|
||||||
static int kvm_fill_hv_flush_list_func(struct hv_guest_mapping_flush_list *flush,
|
|
||||||
void *data)
|
|
||||||
{
|
|
||||||
struct kvm_tlb_range *range = data;
|
|
||||||
|
|
||||||
return hyperv_fill_flush_guest_mapping_list(flush, range->start_gfn,
|
|
||||||
range->pages);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int hv_remote_flush_root_ept(hpa_t root_ept,
|
|
||||||
struct kvm_tlb_range *range)
|
|
||||||
{
|
|
||||||
if (range)
|
|
||||||
return hyperv_flush_guest_mapping_range(root_ept,
|
|
||||||
kvm_fill_hv_flush_list_func, (void *)range);
|
|
||||||
else
|
|
||||||
return hyperv_flush_guest_mapping(root_ept);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hv_remote_flush_tlb_with_range(struct kvm *kvm,
|
|
||||||
struct kvm_tlb_range *range)
|
|
||||||
{
|
|
||||||
struct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm);
|
|
||||||
struct kvm_vcpu *vcpu;
|
|
||||||
int ret = 0, i, nr_unique_valid_roots;
|
|
||||||
hpa_t root;
|
|
||||||
|
|
||||||
spin_lock(&kvm_vmx->hv_root_ept_lock);
|
|
||||||
|
|
||||||
if (!VALID_PAGE(kvm_vmx->hv_root_ept)) {
|
|
||||||
nr_unique_valid_roots = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Flush all valid roots, and see if all vCPUs have converged
|
|
||||||
* on a common root, in which case future flushes can skip the
|
|
||||||
* loop and flush the common root.
|
|
||||||
*/
|
|
||||||
kvm_for_each_vcpu(i, vcpu, kvm) {
|
|
||||||
root = to_vmx(vcpu)->hv_root_ept;
|
|
||||||
if (!VALID_PAGE(root) || root == kvm_vmx->hv_root_ept)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the tracked root to the first valid root. Keep
|
|
||||||
* this root for the entirety of the loop even if more
|
|
||||||
* roots are encountered as a low effort optimization
|
|
||||||
* to avoid flushing the same (first) root again.
|
|
||||||
*/
|
|
||||||
if (++nr_unique_valid_roots == 1)
|
|
||||||
kvm_vmx->hv_root_ept = root;
|
|
||||||
|
|
||||||
if (!ret)
|
|
||||||
ret = hv_remote_flush_root_ept(root, range);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Stop processing roots if a failure occurred and
|
|
||||||
* multiple valid roots have already been detected.
|
|
||||||
*/
|
|
||||||
if (ret && nr_unique_valid_roots > 1)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The optimized flush of a single root can't be used if there
|
|
||||||
* are multiple valid roots (obviously).
|
|
||||||
*/
|
|
||||||
if (nr_unique_valid_roots > 1)
|
|
||||||
kvm_vmx->hv_root_ept = INVALID_PAGE;
|
|
||||||
} else {
|
|
||||||
ret = hv_remote_flush_root_ept(kvm_vmx->hv_root_ept, range);
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&kvm_vmx->hv_root_ept_lock);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
static int hv_remote_flush_tlb(struct kvm *kvm)
|
|
||||||
{
|
|
||||||
return hv_remote_flush_tlb_with_range(kvm, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
|
static int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
|
||||||
{
|
{
|
||||||
struct hv_enlightened_vmcs *evmcs;
|
struct hv_enlightened_vmcs *evmcs;
|
||||||
|
@ -565,21 +486,6 @@ static int hv_enable_direct_tlbflush(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
#endif /* IS_ENABLED(CONFIG_HYPERV) */
|
#endif /* IS_ENABLED(CONFIG_HYPERV) */
|
||||||
|
|
||||||
static void hv_track_root_ept(struct kvm_vcpu *vcpu, hpa_t root_ept)
|
|
||||||
{
|
|
||||||
#if IS_ENABLED(CONFIG_HYPERV)
|
|
||||||
struct kvm_vmx *kvm_vmx = to_kvm_vmx(vcpu->kvm);
|
|
||||||
|
|
||||||
if (kvm_x86_ops.tlb_remote_flush == hv_remote_flush_tlb) {
|
|
||||||
spin_lock(&kvm_vmx->hv_root_ept_lock);
|
|
||||||
to_vmx(vcpu)->hv_root_ept = root_ept;
|
|
||||||
if (root_ept != kvm_vmx->hv_root_ept)
|
|
||||||
kvm_vmx->hv_root_ept = INVALID_PAGE;
|
|
||||||
spin_unlock(&kvm_vmx->hv_root_ept_lock);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Comment's format: document - errata name - stepping - processor name.
|
* Comment's format: document - errata name - stepping - processor name.
|
||||||
* Refer from
|
* Refer from
|
||||||
|
@ -3184,7 +3090,7 @@ static void vmx_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa,
|
||||||
eptp = construct_eptp(vcpu, root_hpa, root_level);
|
eptp = construct_eptp(vcpu, root_hpa, root_level);
|
||||||
vmcs_write64(EPT_POINTER, eptp);
|
vmcs_write64(EPT_POINTER, eptp);
|
||||||
|
|
||||||
hv_track_root_ept(vcpu, root_hpa);
|
hv_track_root_tdp(vcpu, root_hpa);
|
||||||
|
|
||||||
if (!enable_unrestricted_guest && !is_paging(vcpu))
|
if (!enable_unrestricted_guest && !is_paging(vcpu))
|
||||||
guest_cr3 = to_kvm_vmx(kvm)->ept_identity_map_addr;
|
guest_cr3 = to_kvm_vmx(kvm)->ept_identity_map_addr;
|
||||||
|
@ -6966,9 +6872,6 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
|
||||||
vmx->pi_desc.nv = POSTED_INTR_VECTOR;
|
vmx->pi_desc.nv = POSTED_INTR_VECTOR;
|
||||||
vmx->pi_desc.sn = 1;
|
vmx->pi_desc.sn = 1;
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_HYPERV)
|
|
||||||
vmx->hv_root_ept = INVALID_PAGE;
|
|
||||||
#endif
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
free_vmcs:
|
free_vmcs:
|
||||||
|
@ -6985,10 +6888,6 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
|
||||||
|
|
||||||
static int vmx_vm_init(struct kvm *kvm)
|
static int vmx_vm_init(struct kvm *kvm)
|
||||||
{
|
{
|
||||||
#if IS_ENABLED(CONFIG_HYPERV)
|
|
||||||
spin_lock_init(&to_kvm_vmx(kvm)->hv_root_ept_lock);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!ple_gap)
|
if (!ple_gap)
|
||||||
kvm->arch.pause_in_guest = true;
|
kvm->arch.pause_in_guest = true;
|
||||||
|
|
||||||
|
|
|
@ -334,10 +334,6 @@ struct vcpu_vmx {
|
||||||
/* SGX Launch Control public key hash */
|
/* SGX Launch Control public key hash */
|
||||||
u64 msr_ia32_sgxlepubkeyhash[4];
|
u64 msr_ia32_sgxlepubkeyhash[4];
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_HYPERV)
|
|
||||||
u64 hv_root_ept;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct pt_desc pt_desc;
|
struct pt_desc pt_desc;
|
||||||
struct lbr_desc lbr_desc;
|
struct lbr_desc lbr_desc;
|
||||||
|
|
||||||
|
@ -355,11 +351,6 @@ struct kvm_vmx {
|
||||||
unsigned int tss_addr;
|
unsigned int tss_addr;
|
||||||
bool ept_identity_pagetable_done;
|
bool ept_identity_pagetable_done;
|
||||||
gpa_t ept_identity_map_addr;
|
gpa_t ept_identity_map_addr;
|
||||||
|
|
||||||
#if IS_ENABLED(CONFIG_HYPERV)
|
|
||||||
hpa_t hv_root_ept;
|
|
||||||
spinlock_t hv_root_ept_lock;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool nested_vmx_allowed(struct kvm_vcpu *vcpu);
|
bool nested_vmx_allowed(struct kvm_vcpu *vcpu);
|
||||||
|
|
|
@ -10494,6 +10494,10 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
||||||
vcpu->arch.pending_external_vector = -1;
|
vcpu->arch.pending_external_vector = -1;
|
||||||
vcpu->arch.preempted_in_kernel = false;
|
vcpu->arch.preempted_in_kernel = false;
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_HYPERV)
|
||||||
|
vcpu->arch.hv_root_tdp = INVALID_PAGE;
|
||||||
|
#endif
|
||||||
|
|
||||||
r = static_call(kvm_x86_vcpu_create)(vcpu);
|
r = static_call(kvm_x86_vcpu_create)(vcpu);
|
||||||
if (r)
|
if (r)
|
||||||
goto free_guest_fpu;
|
goto free_guest_fpu;
|
||||||
|
@ -10878,6 +10882,11 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
|
||||||
|
|
||||||
kvm->arch.guest_can_read_msr_platform_info = true;
|
kvm->arch.guest_can_read_msr_platform_info = true;
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_HYPERV)
|
||||||
|
spin_lock_init(&kvm->arch.hv_root_tdp_lock);
|
||||||
|
kvm->arch.hv_root_tdp = INVALID_PAGE;
|
||||||
|
#endif
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
|
INIT_DELAYED_WORK(&kvm->arch.kvmclock_update_work, kvmclock_update_fn);
|
||||||
INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
|
INIT_DELAYED_WORK(&kvm->arch.kvmclock_sync_work, kvmclock_sync_fn);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue