Commit 74a497fa authored by Paolo Bonzini's avatar Paolo Bonzini Committed by Radim Krčmář

KVM: nVMX: track dirty state of non-shadowed VMCS fields

VMCS12 fields that are not handled through shadow VMCS are rarely
written, and thus they are also almost constant in the vmcs02.  We can
thus optimize prepare_vmcs02 by skipping all the work for non-shadowed
fields in the common case.

This patch introduces the (pretty simple) tracking infrastructure; the
next patches will move work to prepare_vmcs02_full and save a few hundred
clock cycles per VMRESUME on a Haswell Xeon E5 system:

	                                before  after
	cpuid                           14159   13869
	vmcall                          15290   14951
	inl_from_kernel                 17703   17447
	outl_to_kernel                  16011   14692
	self_ipi_sti_nop                16763   15825
	self_ipi_tpr_sti_nop            17341   15935
	wr_tsc_adjust_msr               14510   14264
	rd_tsc_adjust_msr               15018   14311
	mmio-wildcard-eventfd:pci-mem   16381   14947
	mmio-datamatch-eventfd:pci-mem  18620   17858
	portio-wildcard-eventfd:pci-io  15121   14769
	portio-datamatch-eventfd:pci-io 15761   14831

(average savings 748, stdev 460).
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Signed-off-by: default avatarRadim Krčmář <rkrcmar@redhat.com>
parent c9e9deae
...@@ -436,6 +436,7 @@ struct nested_vmx { ...@@ -436,6 +436,7 @@ struct nested_vmx {
* data hold by vmcs12 * data hold by vmcs12
*/ */
bool sync_shadow_vmcs; bool sync_shadow_vmcs;
bool dirty_vmcs12;
bool change_vmcs01_virtual_x2apic_mode; bool change_vmcs01_virtual_x2apic_mode;
/* L2 must run next, and mustn't decide to exit to L1. */ /* L2 must run next, and mustn't decide to exit to L1. */
...@@ -7623,8 +7624,10 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu) ...@@ -7623,8 +7624,10 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu)
{ {
unsigned long field; unsigned long field;
gva_t gva; gva_t gva;
struct vcpu_vmx *vmx = to_vmx(vcpu);
unsigned long exit_qualification = vmcs_readl(EXIT_QUALIFICATION); unsigned long exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
u32 vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO); u32 vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
/* The value to write might be 32 or 64 bits, depending on L1's long /* The value to write might be 32 or 64 bits, depending on L1's long
* mode, and eventually we need to write that into a field of several * mode, and eventually we need to write that into a field of several
* possible lengths. The code below first zero-extends the value to 64 * possible lengths. The code below first zero-extends the value to 64
...@@ -7667,6 +7670,20 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu) ...@@ -7667,6 +7670,20 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu)
return kvm_skip_emulated_instruction(vcpu); return kvm_skip_emulated_instruction(vcpu);
} }
switch (field) {
#define SHADOW_FIELD_RW(x) case x:
#include "vmx_shadow_fields.h"
/*
* The fields that can be updated by L1 without a vmexit are
* always updated in the vmcs02, the others go down the slow
* path of prepare_vmcs02.
*/
break;
default:
vmx->nested.dirty_vmcs12 = true;
break;
}
nested_vmx_succeed(vcpu); nested_vmx_succeed(vcpu);
return kvm_skip_emulated_instruction(vcpu); return kvm_skip_emulated_instruction(vcpu);
} }
...@@ -7681,6 +7698,7 @@ static void set_current_vmptr(struct vcpu_vmx *vmx, gpa_t vmptr) ...@@ -7681,6 +7698,7 @@ static void set_current_vmptr(struct vcpu_vmx *vmx, gpa_t vmptr)
__pa(vmx->vmcs01.shadow_vmcs)); __pa(vmx->vmcs01.shadow_vmcs));
vmx->nested.sync_shadow_vmcs = true; vmx->nested.sync_shadow_vmcs = true;
} }
vmx->nested.dirty_vmcs12 = true;
} }
/* Emulate the VMPTRLD instruction */ /* Emulate the VMPTRLD instruction */
...@@ -10297,6 +10315,11 @@ static int nested_vmx_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, bool ne ...@@ -10297,6 +10315,11 @@ static int nested_vmx_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, bool ne
return 0; return 0;
} }
static void prepare_vmcs02_full(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
bool from_vmentry)
{
}
/* /*
* prepare_vmcs02 is called when the L1 guest hypervisor runs its nested * prepare_vmcs02 is called when the L1 guest hypervisor runs its nested
* L2 guest. L1 has a vmcs for L2 (vmcs12), and this function "merges" it * L2 guest. L1 has a vmcs for L2 (vmcs12), and this function "merges" it
...@@ -10592,7 +10615,6 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, ...@@ -10592,7 +10615,6 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid); vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid);
vmx_flush_tlb(vcpu, true); vmx_flush_tlb(vcpu, true);
} }
} }
if (enable_pml) { if (enable_pml) {
...@@ -10641,6 +10663,11 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, ...@@ -10641,6 +10663,11 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12,
/* Note: modifies VM_ENTRY/EXIT_CONTROLS and GUEST/HOST_IA32_EFER */ /* Note: modifies VM_ENTRY/EXIT_CONTROLS and GUEST/HOST_IA32_EFER */
vmx_set_efer(vcpu, vcpu->arch.efer); vmx_set_efer(vcpu, vcpu->arch.efer);
if (vmx->nested.dirty_vmcs12) {
prepare_vmcs02_full(vcpu, vmcs12, from_vmentry);
vmx->nested.dirty_vmcs12 = false;
}
/* Shadow page tables on either EPT or shadow page tables. */ /* Shadow page tables on either EPT or shadow page tables. */
if (nested_vmx_load_cr3(vcpu, vmcs12->guest_cr3, nested_cpu_has_ept(vmcs12), if (nested_vmx_load_cr3(vcpu, vmcs12->guest_cr3, nested_cpu_has_ept(vmcs12),
entry_failure_code)) entry_failure_code))
......
...@@ -17,6 +17,12 @@ ...@@ -17,6 +17,12 @@
* (e.g. force a sync if VM_INSTRUCTION_ERROR is modified * (e.g. force a sync if VM_INSTRUCTION_ERROR is modified
* by nested_vmx_failValid) * by nested_vmx_failValid)
* *
* When adding or removing fields here, note that shadowed
* fields must always be synced by prepare_vmcs02, not just
* prepare_vmcs02_full.
*/
/*
* Keeping the fields ordered by size is an attempt at improving * Keeping the fields ordered by size is an attempt at improving
* branch prediction in vmcs_read_any and vmcs_write_any. * branch prediction in vmcs_read_any and vmcs_write_any.
*/ */
......
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