KVM: x86: Add KVM exit for IOAPIC EOIs
authorSteve Rutherford <srutherford@google.com>
Thu, 30 Jul 2015 06:21:41 +0000 (23:21 -0700)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 1 Oct 2015 13:06:27 +0000 (15:06 +0200)
Adds KVM_EXIT_IOAPIC_EOI which allows the kernel to EOI
level-triggered IOAPIC interrupts.

Uses a per VCPU exit bitmap to decide whether or not the IOAPIC needs
to be informed (which is identical to the EOI_EXIT_BITMAP field used
by modern x86 processors, but can also be used to elide kvm IOAPIC EOI
exits on older processors).

[Note: A prototype using ResampleFDs found that decoupling the EOI
from the VCPU's thread made it possible for the VCPU to not see a
recent EOI after reentering the guest. This does not match real
hardware.]

Compile tested for Intel x86.

Signed-off-by: Steve Rutherford <srutherford@google.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Documentation/virtual/kvm/api.txt
arch/x86/include/asm/kvm_host.h
arch/x86/kvm/lapic.c
arch/x86/kvm/x86.c
include/linux/kvm_host.h
include/uapi/linux/kvm.h

index 43e0816d0de135a6df8b93927d1a87b39ed357d4..0d14bf5db534765ea88a900ae085cb5ef1a75004 100644 (file)
@@ -3309,6 +3309,18 @@ Valid values for 'type' are:
    to ignore the request, or to gather VM memory core dump and/or
    reset/shutdown of the VM.
 
+               /* KVM_EXIT_IOAPIC_EOI */
+               struct {
+                       __u8 vector;
+               } eoi;
+
+Indicates that the VCPU's in-kernel local APIC received an EOI for a
+level-triggered IOAPIC interrupt.  This exit only triggers when the
+IOAPIC is implemented in userspace (i.e. KVM_CAP_SPLIT_IRQCHIP is enabled);
+the userspace IOAPIC should process the EOI and retrigger the interrupt if
+it is still asserted.  Vector is the LAPIC interrupt vector for which the
+EOI was received.
+
                /* Fix the size of the union. */
                char padding[256];
        };
index befcf555bddc16014eb460ca12eb2d1b60e91cb5..af09fa1d1be75a4d8d8280b3b5d250d4a0d89a15 100644 (file)
@@ -574,6 +574,8 @@ struct kvm_vcpu_arch {
        struct {
                bool pv_unhalted;
        } pv;
+
+       int pending_ioapic_eoi;
 };
 
 struct kvm_lpage_info {
index e05946c36b8738e6488d0570c69c2c7b7d5b5041..ef70f6f3a37a6f5bfb43b0f5efd84f085413d62b 100644 (file)
@@ -877,15 +877,25 @@ static bool kvm_ioapic_handles_vector(struct kvm_lapic *apic, int vector)
 
 static void kvm_ioapic_send_eoi(struct kvm_lapic *apic, int vector)
 {
-       if (kvm_ioapic_handles_vector(apic, vector)) {
-               int trigger_mode;
-               if (apic_test_vector(vector, apic->regs + APIC_TMR))
-                       trigger_mode = IOAPIC_LEVEL_TRIG;
-               else
-                       trigger_mode = IOAPIC_EDGE_TRIG;
+       int trigger_mode;
+
+       /* Eoi the ioapic only if the ioapic doesn't own the vector. */
+       if (!kvm_ioapic_handles_vector(apic, vector))
+               return;
 
-               kvm_ioapic_update_eoi(apic->vcpu, vector, trigger_mode);
+       /* Request a KVM exit to inform the userspace IOAPIC. */
+       if (irqchip_split(apic->vcpu->kvm)) {
+               apic->vcpu->arch.pending_ioapic_eoi = vector;
+               kvm_make_request(KVM_REQ_IOAPIC_EOI_EXIT, apic->vcpu);
+               return;
        }
+
+       if (apic_test_vector(vector, apic->regs + APIC_TMR))
+               trigger_mode = IOAPIC_LEVEL_TRIG;
+       else
+               trigger_mode = IOAPIC_EDGE_TRIG;
+
+       kvm_ioapic_update_eoi(apic->vcpu, vector, trigger_mode);
 }
 
 static int apic_set_eoi(struct kvm_lapic *apic)
index f720774a479792e3c97e26ba440bd68ac5f05ec2..27429daa054ec88eeacd2b58cb7123255e0d2af6 100644 (file)
@@ -6271,6 +6271,17 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
                        kvm_pmu_handle_event(vcpu);
                if (kvm_check_request(KVM_REQ_PMI, vcpu))
                        kvm_pmu_deliver_pmi(vcpu);
+               if (kvm_check_request(KVM_REQ_IOAPIC_EOI_EXIT, vcpu)) {
+                       BUG_ON(vcpu->arch.pending_ioapic_eoi > 255);
+                       if (test_bit(vcpu->arch.pending_ioapic_eoi,
+                                    (void *) vcpu->arch.eoi_exit_bitmap)) {
+                               vcpu->run->exit_reason = KVM_EXIT_IOAPIC_EOI;
+                               vcpu->run->eoi.vector =
+                                               vcpu->arch.pending_ioapic_eoi;
+                               r = 0;
+                               goto out;
+                       }
+               }
                if (kvm_check_request(KVM_REQ_SCAN_IOAPIC, vcpu))
                        vcpu_scan_ioapic(vcpu);
                if (kvm_check_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu))
index 354f147647ab59b038dcdc970697d2e3ea0847b2..3b33215ed447090c3fa6fcd314d675f64a84caea 100644 (file)
@@ -140,6 +140,7 @@ static inline bool is_error_page(struct page *page)
 #define KVM_REQ_APIC_PAGE_RELOAD  25
 #define KVM_REQ_SMI               26
 #define KVM_REQ_HV_CRASH          27
+#define KVM_REQ_IOAPIC_EOI_EXIT   28
 
 #define KVM_USERSPACE_IRQ_SOURCE_ID            0
 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID       1
@@ -1146,4 +1147,3 @@ static inline void kvm_vcpu_set_dy_eligible(struct kvm_vcpu *vcpu, bool val)
 }
 #endif /* CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT */
 #endif
-
index ed00f8fc9ea2d6b59916b9586c3e3db1d454db6f..12e3afbf0f47e028a9e327f9514367fe572f377e 100644 (file)
@@ -183,6 +183,7 @@ struct kvm_s390_skeys {
 #define KVM_EXIT_EPR              23
 #define KVM_EXIT_SYSTEM_EVENT     24
 #define KVM_EXIT_S390_STSI        25
+#define KVM_EXIT_IOAPIC_EOI       26
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -333,6 +334,10 @@ struct kvm_run {
                        __u8 sel1;
                        __u16 sel2;
                } s390_stsi;
+               /* KVM_EXIT_IOAPIC_EOI */
+               struct {
+                       __u8 vector;
+               } eoi;
                /* Fix the size of the union. */
                char padding[256];
        };