Commit 877ad952 authored by Tianyu Lan's avatar Tianyu Lan Committed by Paolo Bonzini

KVM: vmx: Add tlb_remote_flush callback support

Register tlb_remote_flush callback for vmx when hyperv capability of
nested guest mapping flush is detected. The interface can help to
reduce overhead when flush ept table among vcpus for nested VM. The
tradition way is to send IPIs to all affected vcpus and executes
INVEPT on each vcpus. It will trigger several vmexits for IPI
and INVEPT emulation. Hyper-V provides such hypercall to do
flush for all vcpus and call the hypercall when all ept table
pointers of single VM are same.
Signed-off-by: default avatarLan Tianyu <Tianyu.Lan@microsoft.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent b08660e5
...@@ -188,12 +188,21 @@ module_param(ple_window_max, uint, 0444); ...@@ -188,12 +188,21 @@ module_param(ple_window_max, uint, 0444);
extern const ulong vmx_return; extern const ulong vmx_return;
enum ept_pointers_status {
EPT_POINTERS_CHECK = 0,
EPT_POINTERS_MATCH = 1,
EPT_POINTERS_MISMATCH = 2
};
struct kvm_vmx { struct kvm_vmx {
struct kvm kvm; struct kvm kvm;
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;
enum ept_pointers_status ept_pointers_match;
spinlock_t ept_pointer_lock;
}; };
#define NR_AUTOLOAD_MSRS 8 #define NR_AUTOLOAD_MSRS 8
...@@ -863,6 +872,7 @@ struct vcpu_vmx { ...@@ -863,6 +872,7 @@ struct vcpu_vmx {
*/ */
u64 msr_ia32_feature_control; u64 msr_ia32_feature_control;
u64 msr_ia32_feature_control_valid_bits; u64 msr_ia32_feature_control_valid_bits;
u64 ept_pointer;
}; };
enum segment_cache_field { enum segment_cache_field {
...@@ -1357,6 +1367,48 @@ static void evmcs_sanitize_exec_ctrls(struct vmcs_config *vmcs_conf) ...@@ -1357,6 +1367,48 @@ static void evmcs_sanitize_exec_ctrls(struct vmcs_config *vmcs_conf)
* GUEST_IA32_RTIT_CTL = 0x00002814, * GUEST_IA32_RTIT_CTL = 0x00002814,
*/ */
} }
/* check_ept_pointer() should be under protection of ept_pointer_lock. */
static void check_ept_pointer_match(struct kvm *kvm)
{
struct kvm_vcpu *vcpu;
u64 tmp_eptp = INVALID_PAGE;
int i;
kvm_for_each_vcpu(i, vcpu, kvm) {
if (!VALID_PAGE(tmp_eptp)) {
tmp_eptp = to_vmx(vcpu)->ept_pointer;
} else if (tmp_eptp != to_vmx(vcpu)->ept_pointer) {
to_kvm_vmx(kvm)->ept_pointers_match
= EPT_POINTERS_MISMATCH;
return;
}
}
to_kvm_vmx(kvm)->ept_pointers_match = EPT_POINTERS_MATCH;
}
static int vmx_hv_remote_flush_tlb(struct kvm *kvm)
{
int ret;
spin_lock(&to_kvm_vmx(kvm)->ept_pointer_lock);
if (to_kvm_vmx(kvm)->ept_pointers_match == EPT_POINTERS_CHECK)
check_ept_pointer_match(kvm);
if (to_kvm_vmx(kvm)->ept_pointers_match != EPT_POINTERS_MATCH) {
ret = -ENOTSUPP;
goto out;
}
ret = hyperv_flush_guest_mapping(
to_vmx(kvm_get_vcpu(kvm, 0))->ept_pointer);
out:
spin_unlock(&to_kvm_vmx(kvm)->ept_pointer_lock);
return ret;
}
#else /* !IS_ENABLED(CONFIG_HYPERV) */ #else /* !IS_ENABLED(CONFIG_HYPERV) */
static inline void evmcs_write64(unsigned long field, u64 value) {} static inline void evmcs_write64(unsigned long field, u64 value) {}
static inline void evmcs_write32(unsigned long field, u32 value) {} static inline void evmcs_write32(unsigned long field, u32 value) {}
...@@ -5041,6 +5093,7 @@ static u64 construct_eptp(struct kvm_vcpu *vcpu, unsigned long root_hpa) ...@@ -5041,6 +5093,7 @@ static u64 construct_eptp(struct kvm_vcpu *vcpu, unsigned long root_hpa)
static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
{ {
struct kvm *kvm = vcpu->kvm;
unsigned long guest_cr3; unsigned long guest_cr3;
u64 eptp; u64 eptp;
...@@ -5048,11 +5101,20 @@ static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) ...@@ -5048,11 +5101,20 @@ static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
if (enable_ept) { if (enable_ept) {
eptp = construct_eptp(vcpu, cr3); eptp = construct_eptp(vcpu, cr3);
vmcs_write64(EPT_POINTER, eptp); vmcs_write64(EPT_POINTER, eptp);
if (kvm_x86_ops->tlb_remote_flush) {
spin_lock(&to_kvm_vmx(kvm)->ept_pointer_lock);
to_vmx(vcpu)->ept_pointer = eptp;
to_kvm_vmx(kvm)->ept_pointers_match
= EPT_POINTERS_CHECK;
spin_unlock(&to_kvm_vmx(kvm)->ept_pointer_lock);
}
if (enable_unrestricted_guest || is_paging(vcpu) || if (enable_unrestricted_guest || is_paging(vcpu) ||
is_guest_mode(vcpu)) is_guest_mode(vcpu))
guest_cr3 = kvm_read_cr3(vcpu); guest_cr3 = kvm_read_cr3(vcpu);
else else
guest_cr3 = to_kvm_vmx(vcpu->kvm)->ept_identity_map_addr; guest_cr3 = to_kvm_vmx(kvm)->ept_identity_map_addr;
ept_load_pdptrs(vcpu); ept_load_pdptrs(vcpu);
} }
...@@ -7622,6 +7684,12 @@ static __init int hardware_setup(void) ...@@ -7622,6 +7684,12 @@ static __init int hardware_setup(void)
if (enable_ept && !cpu_has_vmx_ept_2m_page()) if (enable_ept && !cpu_has_vmx_ept_2m_page())
kvm_disable_largepages(); kvm_disable_largepages();
#if IS_ENABLED(CONFIG_HYPERV)
if (ms_hyperv.nested_features & HV_X64_NESTED_GUEST_MAPPING_FLUSH
&& enable_ept)
kvm_x86_ops->tlb_remote_flush = vmx_hv_remote_flush_tlb;
#endif
if (!cpu_has_vmx_ple()) { if (!cpu_has_vmx_ple()) {
ple_gap = 0; ple_gap = 0;
ple_window = 0; ple_window = 0;
...@@ -10665,6 +10733,8 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id) ...@@ -10665,6 +10733,8 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
static int vmx_vm_init(struct kvm *kvm) static int vmx_vm_init(struct kvm *kvm)
{ {
spin_lock_init(&to_kvm_vmx(kvm)->ept_pointer_lock);
if (!ple_gap) if (!ple_gap)
kvm->arch.pause_in_guest = true; kvm->arch.pause_in_guest = true;
return 0; return 0;
......
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