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

"Linux Kernel Mailing List" <[email protected]> Sat, 10 Feb 2018 22:00:22 +0000 (UTC)
Newsgroups gmane.linux.kernel.commits.head
Message-ID <[email protected]>
Web:        https://git.kernel.org/torvalds/c/74a497fae754bd970ad0573c26d0a3c5e784fa0c
Commit:     74a497fae754bd970ad0573c26d0a3c5e784fa0c
Parent:     c9e9deae76b8fbbd48e7357247689016bb0d6242
Refname:    refs/heads/master
Author:     Paolo Bonzini <[email protected]>
AuthorDate: Wed Dec 20 13:55:39 2017 +0100
Committer:  Radim Krčmář <[email protected]>
CommitDate: Tue Jan 16 16:50:13 2018 +0100

    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: Paolo Bonzini <[email protected]>
    Signed-off-by: Radim Krčmář <[email protected]>
---
 arch/x86/kvm/vmx.c               | 29 ++++++++++++++++++++++++++++-
 arch/x86/kvm/vmx_shadow_fields.h |  6 ++++++
 2 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 4530b2ba63b9..1cc787f63972 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -436,6 +436,7 @@ struct nested_vmx {
 	 * data hold by vmcs12
 	 */
 	bool sync_shadow_vmcs;
+	bool dirty_vmcs12;
 
 	bool change_vmcs01_virtual_x2apic_mode;
 	/* L2 must run next, and mustn't decide to exit to L1. */
@@ -7623,8 +7624,10 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu)
 {
 	unsigned long field;
 	gva_t gva;
+	struct vcpu_vmx *vmx = to_vmx(vcpu);
 	unsigned long exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
 	u32 vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO);
+
 	/* 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
 	 * possible lengths. The code below first zero-extends the value to 64
@@ -7667,6 +7670,20 @@ static int handle_vmwrite(struct kvm_vcpu *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);
 	return kvm_skip_emulated_instruction(vcpu);
 }
@@ -7681,6 +7698,7 @@ static void set_current_vmptr(struct vcpu_vmx *vmx, gpa_t vmptr)
 			     __pa(vmx->vmcs01.shadow_vmcs));
 		vmx->nested.sync_shadow_vmcs = true;
 	}
+	vmx->nested.dirty_vmcs12 = true;
 }
 
 /* Emulate the VMPTRLD instruction */
@@ -10297,6 +10315,11 @@ static int nested_vmx_load_cr3(struct kvm_vcpu *vcpu, unsigned long cr3, bool ne
 	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
  * 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,
 			vmcs_write16(VIRTUAL_PROCESSOR_ID, vmx->vpid);
 			vmx_flush_tlb(vcpu, true);
 		}
-
 	}
 
 	if (enable_pml) {
@@ -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 */
 	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. */
 	if (nested_vmx_load_cr3(vcpu, vmcs12->guest_cr3, nested_cpu_has_ept(vmcs12),
 				entry_failure_code))
diff --git a/arch/x86/kvm/vmx_shadow_fields.h b/arch/x86/kvm/vmx_shadow_fields.h
index 31d7a15338ac..cd0c75f6d037 100644
--- a/arch/x86/kvm/vmx_shadow_fields.h
+++ b/arch/x86/kvm/vmx_shadow_fields.h
@@ -17,6 +17,12 @@
  * (e.g. force a sync if VM_INSTRUCTION_ERROR is modified
  * 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
  * branch prediction in vmcs_read_any and vmcs_write_any.
  */
--
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html