Skip to content

Commit 877ad95

Browse files
Tianyu Lanbonzini
authored andcommitted
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: Lan Tianyu <Tianyu.Lan@microsoft.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
1 parent b08660e commit 877ad95

File tree

1 file changed

+71
-1
lines changed

1 file changed

+71
-1
lines changed

arch/x86/kvm/vmx.c

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,12 +188,21 @@ module_param(ple_window_max, uint, 0444);
188188

189189
extern const ulong vmx_return;
190190

191+
enum ept_pointers_status {
192+
EPT_POINTERS_CHECK = 0,
193+
EPT_POINTERS_MATCH = 1,
194+
EPT_POINTERS_MISMATCH = 2
195+
};
196+
191197
struct kvm_vmx {
192198
struct kvm kvm;
193199

194200
unsigned int tss_addr;
195201
bool ept_identity_pagetable_done;
196202
gpa_t ept_identity_map_addr;
203+
204+
enum ept_pointers_status ept_pointers_match;
205+
spinlock_t ept_pointer_lock;
197206
};
198207

199208
#define NR_AUTOLOAD_MSRS 8
@@ -863,6 +872,7 @@ struct vcpu_vmx {
863872
*/
864873
u64 msr_ia32_feature_control;
865874
u64 msr_ia32_feature_control_valid_bits;
875+
u64 ept_pointer;
866876
};
867877

868878
enum segment_cache_field {
@@ -1357,6 +1367,48 @@ static void evmcs_sanitize_exec_ctrls(struct vmcs_config *vmcs_conf)
13571367
* GUEST_IA32_RTIT_CTL = 0x00002814,
13581368
*/
13591369
}
1370+
1371+
/* check_ept_pointer() should be under protection of ept_pointer_lock. */
1372+
static void check_ept_pointer_match(struct kvm *kvm)
1373+
{
1374+
struct kvm_vcpu *vcpu;
1375+
u64 tmp_eptp = INVALID_PAGE;
1376+
int i;
1377+
1378+
kvm_for_each_vcpu(i, vcpu, kvm) {
1379+
if (!VALID_PAGE(tmp_eptp)) {
1380+
tmp_eptp = to_vmx(vcpu)->ept_pointer;
1381+
} else if (tmp_eptp != to_vmx(vcpu)->ept_pointer) {
1382+
to_kvm_vmx(kvm)->ept_pointers_match
1383+
= EPT_POINTERS_MISMATCH;
1384+
return;
1385+
}
1386+
}
1387+
1388+
to_kvm_vmx(kvm)->ept_pointers_match = EPT_POINTERS_MATCH;
1389+
}
1390+
1391+
static int vmx_hv_remote_flush_tlb(struct kvm *kvm)
1392+
{
1393+
int ret;
1394+
1395+
spin_lock(&to_kvm_vmx(kvm)->ept_pointer_lock);
1396+
1397+
if (to_kvm_vmx(kvm)->ept_pointers_match == EPT_POINTERS_CHECK)
1398+
check_ept_pointer_match(kvm);
1399+
1400+
if (to_kvm_vmx(kvm)->ept_pointers_match != EPT_POINTERS_MATCH) {
1401+
ret = -ENOTSUPP;
1402+
goto out;
1403+
}
1404+
1405+
ret = hyperv_flush_guest_mapping(
1406+
to_vmx(kvm_get_vcpu(kvm, 0))->ept_pointer);
1407+
1408+
out:
1409+
spin_unlock(&to_kvm_vmx(kvm)->ept_pointer_lock);
1410+
return ret;
1411+
}
13601412
#else /* !IS_ENABLED(CONFIG_HYPERV) */
13611413
static inline void evmcs_write64(unsigned long field, u64 value) {}
13621414
static inline void evmcs_write32(unsigned long field, u32 value) {}
@@ -5041,18 +5093,28 @@ static u64 construct_eptp(struct kvm_vcpu *vcpu, unsigned long root_hpa)
50415093

50425094
static void vmx_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3)
50435095
{
5096+
struct kvm *kvm = vcpu->kvm;
50445097
unsigned long guest_cr3;
50455098
u64 eptp;
50465099

50475100
guest_cr3 = cr3;
50485101
if (enable_ept) {
50495102
eptp = construct_eptp(vcpu, cr3);
50505103
vmcs_write64(EPT_POINTER, eptp);
5104+
5105+
if (kvm_x86_ops->tlb_remote_flush) {
5106+
spin_lock(&to_kvm_vmx(kvm)->ept_pointer_lock);
5107+
to_vmx(vcpu)->ept_pointer = eptp;
5108+
to_kvm_vmx(kvm)->ept_pointers_match
5109+
= EPT_POINTERS_CHECK;
5110+
spin_unlock(&to_kvm_vmx(kvm)->ept_pointer_lock);
5111+
}
5112+
50515113
if (enable_unrestricted_guest || is_paging(vcpu) ||
50525114
is_guest_mode(vcpu))
50535115
guest_cr3 = kvm_read_cr3(vcpu);
50545116
else
5055-
guest_cr3 = to_kvm_vmx(vcpu->kvm)->ept_identity_map_addr;
5117+
guest_cr3 = to_kvm_vmx(kvm)->ept_identity_map_addr;
50565118
ept_load_pdptrs(vcpu);
50575119
}
50585120

@@ -7622,6 +7684,12 @@ static __init int hardware_setup(void)
76227684
if (enable_ept && !cpu_has_vmx_ept_2m_page())
76237685
kvm_disable_largepages();
76247686

7687+
#if IS_ENABLED(CONFIG_HYPERV)
7688+
if (ms_hyperv.nested_features & HV_X64_NESTED_GUEST_MAPPING_FLUSH
7689+
&& enable_ept)
7690+
kvm_x86_ops->tlb_remote_flush = vmx_hv_remote_flush_tlb;
7691+
#endif
7692+
76257693
if (!cpu_has_vmx_ple()) {
76267694
ple_gap = 0;
76277695
ple_window = 0;
@@ -10665,6 +10733,8 @@ static struct kvm_vcpu *vmx_create_vcpu(struct kvm *kvm, unsigned int id)
1066510733

1066610734
static int vmx_vm_init(struct kvm *kvm)
1066710735
{
10736+
spin_lock_init(&to_kvm_vmx(kvm)->ept_pointer_lock);
10737+
1066810738
if (!ple_gap)
1066910739
kvm->arch.pause_in_guest = true;
1067010740
return 0;

0 commit comments

Comments
 (0)