KVM: x86: avoid useless set of KVM_REQ_EVENT after emulation
authorPaolo Bonzini <pbonzini@redhat.com>
Thu, 27 Mar 2014 10:29:28 +0000 (11:29 +0100)
committerPaolo Bonzini <pbonzini@redhat.com>
Fri, 11 Jul 2014 07:13:57 +0000 (09:13 +0200)
Despite the provisions to emulate up to 130 consecutive instructions, in
practice KVM will emulate just one before exiting handle_invalid_guest_state,
because x86_emulate_instruction always sets KVM_REQ_EVENT.

However, we only need to do this if an interrupt could be injected,
which happens a) if an interrupt shadow bit (STI or MOV SS) has gone
away; b) if the interrupt flag has just been set (other instructions
than STI can set it without enabling an interrupt shadow).

This cuts another 700-900 cycles from the cost of emulating an
instruction (measured on a Sandy Bridge Xeon: 1650-2600 cycles
before the patch on kvm-unit-tests, 925-1700 afterwards).

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/x86.c

index a56126e6bd75fe264f07c666861405dd285436d2..cd9316786dca6a2792481076985e83bbc8ca13bf 100644 (file)
@@ -87,6 +87,7 @@ static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE);
 
 static void update_cr8_intercept(struct kvm_vcpu *vcpu);
 static void process_nmi(struct kvm_vcpu *vcpu);
+static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags);
 
 struct kvm_x86_ops *kvm_x86_ops;
 EXPORT_SYMBOL_GPL(kvm_x86_ops);
@@ -4868,8 +4869,11 @@ static void toggle_interruptibility(struct kvm_vcpu *vcpu, u32 mask)
         */
        if (int_shadow & mask)
                mask = 0;
-       if (unlikely(int_shadow || mask))
+       if (unlikely(int_shadow || mask)) {
                kvm_x86_ops->set_interrupt_shadow(vcpu, mask);
+               if (!mask)
+                       kvm_make_request(KVM_REQ_EVENT, vcpu);
+       }
 }
 
 static void inject_emulated_exception(struct kvm_vcpu *vcpu)
@@ -5095,20 +5099,18 @@ static int kvm_vcpu_check_hw_bp(unsigned long addr, u32 type, u32 dr7,
        return dr6;
 }
 
-static void kvm_vcpu_check_singlestep(struct kvm_vcpu *vcpu, int *r)
+static void kvm_vcpu_check_singlestep(struct kvm_vcpu *vcpu, unsigned long rflags, int *r)
 {
        struct kvm_run *kvm_run = vcpu->run;
 
        /*
-        * Use the "raw" value to see if TF was passed to the processor.
-        * Note that the new value of the flags has not been saved yet.
+        * rflags is the old, "raw" value of the flags.  The new value has
+        * not been saved yet.
         *
         * This is correct even for TF set by the guest, because "the
         * processor will not generate this exception after the instruction
         * that sets the TF flag".
         */
-       unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
-
        if (unlikely(rflags & X86_EFLAGS_TF)) {
                if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP) {
                        kvm_run->debug.arch.dr6 = DR6_BS | DR6_FIXED_1;
@@ -5275,13 +5277,22 @@ restart:
                r = EMULATE_DONE;
 
        if (writeback) {
+               unsigned long rflags = kvm_x86_ops->get_rflags(vcpu);
                toggle_interruptibility(vcpu, ctxt->interruptibility);
-               kvm_make_request(KVM_REQ_EVENT, vcpu);
                vcpu->arch.emulate_regs_need_sync_to_vcpu = false;
                kvm_rip_write(vcpu, ctxt->eip);
                if (r == EMULATE_DONE)
-                       kvm_vcpu_check_singlestep(vcpu, &r);
-               kvm_set_rflags(vcpu, ctxt->eflags);
+                       kvm_vcpu_check_singlestep(vcpu, rflags, &r);
+               __kvm_set_rflags(vcpu, ctxt->eflags);
+
+               /*
+                * For STI, interrupts are shadowed; so KVM_REQ_EVENT will
+                * do nothing, and it will be requested again as soon as
+                * the shadow expires.  But we still need to check here,
+                * because POPF has no interrupt shadow.
+                */
+               if (unlikely((ctxt->eflags & ~rflags) & X86_EFLAGS_IF))
+                       kvm_make_request(KVM_REQ_EVENT, vcpu);
        } else
                vcpu->arch.emulate_regs_need_sync_to_vcpu = true;
 
@@ -7406,12 +7417,17 @@ unsigned long kvm_get_rflags(struct kvm_vcpu *vcpu)
 }
 EXPORT_SYMBOL_GPL(kvm_get_rflags);
 
-void kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
+static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
 {
        if (vcpu->guest_debug & KVM_GUESTDBG_SINGLESTEP &&
            kvm_is_linear_rip(vcpu, vcpu->arch.singlestep_rip))
                rflags |= X86_EFLAGS_TF;
        kvm_x86_ops->set_rflags(vcpu, rflags);
+}
+
+void kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags)
+{
+       __kvm_set_rflags(vcpu, rflags);
        kvm_make_request(KVM_REQ_EVENT, vcpu);
 }
 EXPORT_SYMBOL_GPL(kvm_set_rflags);