KVM: hlt emulation should take in-kernel APIC/PIT timers into account
Marcelo Tosatti [Fri, 11 Apr 2008 17:53:26 +0000 (14:53 -0300)]
Timers that fire between guest hlt and vcpu_block's add_wait_queue() are
ignored, possibly resulting in hangs.

Also make sure that atomic_inc and waitqueue_active tests happen in the
specified order, otherwise the following race is open:

CPU0                                        CPU1
                                            if (waitqueue_active(wq))
add_wait_queue()
if (!atomic_read(pit_timer->pending))
    schedule()
                                            atomic_inc(pit_timer->pending)

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>

arch/ia64/kvm/kvm-ia64.c
arch/s390/kvm/interrupt.c
arch/x86/kvm/i8254.c
arch/x86/kvm/irq.c
arch/x86/kvm/irq.h
arch/x86/kvm/lapic.c
include/linux/kvm_host.h
virt/kvm/kvm_main.c

index 9c56b64..ca1cfb1 100644 (file)
@@ -1778,6 +1778,11 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
        return 0;
 }
 
+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
+
 gfn_t unalias_gfn(struct kvm *kvm, gfn_t gfn)
 {
        return gfn;
index f62588c..fcd1ed8 100644 (file)
@@ -325,6 +325,11 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
        return rc;
 }
 
+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+       return 0;
+}
+
 int kvm_s390_handle_wait(struct kvm_vcpu *vcpu)
 {
        u64 now, sltime;
index 9f118e2..ed1af80 100644 (file)
@@ -212,6 +212,16 @@ int __pit_timer_fn(struct kvm_kpit_state *ps)
        return (pt->period == 0 ? 0 : 1);
 }
 
+int pit_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+       struct kvm_pit *pit = vcpu->kvm->arch.vpit;
+
+       if (pit && vcpu->vcpu_id == 0)
+               return atomic_read(&pit->pit_state.pit_timer.pending);
+
+       return 0;
+}
+
 static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
 {
        struct kvm_kpit_state *ps;
index dbfe21c..ce1f583 100644 (file)
 #include "i8254.h"
 
 /*
+ * check if there are pending timer events
+ * to be processed.
+ */
+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+       int ret;
+
+       ret = pit_has_pending_timer(vcpu);
+       ret |= apic_has_pending_timer(vcpu);
+
+       return ret;
+}
+EXPORT_SYMBOL(kvm_cpu_has_pending_timer);
+
+/*
  * check if there is pending interrupt without
  * intack.
  */
index fa5ed5d..1802134 100644 (file)
@@ -85,4 +85,7 @@ void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu);
 void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu);
 void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu);
 
+int pit_has_pending_timer(struct kvm_vcpu *vcpu);
+int apic_has_pending_timer(struct kvm_vcpu *vcpu);
+
 #endif
index 31280df..debf582 100644 (file)
@@ -952,6 +952,16 @@ static int __apic_timer_fn(struct kvm_lapic *apic)
        return result;
 }
 
+int apic_has_pending_timer(struct kvm_vcpu *vcpu)
+{
+       struct kvm_lapic *lapic = vcpu->arch.apic;
+
+       if (lapic)
+               return atomic_read(&lapic->timer.pending);
+
+       return 0;
+}
+
 static int __inject_apic_timer_irq(struct kvm_lapic *apic)
 {
        int vector;
index bd0c2d2..0bc4003 100644 (file)
@@ -269,6 +269,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm);
 
 int kvm_cpu_get_interrupt(struct kvm_vcpu *v);
 int kvm_cpu_has_interrupt(struct kvm_vcpu *v);
+int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu);
 void kvm_vcpu_kick(struct kvm_vcpu *vcpu);
 
 static inline void kvm_guest_enter(void)
index d5911d9..47cbc6e 100644 (file)
@@ -765,6 +765,7 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu)
         * We will block until either an interrupt or a signal wakes us up
         */
        while (!kvm_cpu_has_interrupt(vcpu)
+              && !kvm_cpu_has_pending_timer(vcpu)
               && !signal_pending(current)
               && !kvm_arch_vcpu_runnable(vcpu)) {
                set_current_state(TASK_INTERRUPTIBLE);