KVM: hlt emulation should take in-kernel APIC/PIT timers into account
authorMarcelo Tosatti <mtosatti@redhat.com>
Fri, 11 Apr 2008 17:53:26 +0000 (14:53 -0300)
committerAvi Kivity <avi@qumranet.com>
Sun, 27 Apr 2008 09:04:11 +0000 (12:04 +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 9c56b6429cb6dddd30eb74640685530de029b0a9..ca1cfb124d4ff480e1a1b35fe1309ce13ff1999e 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 f62588cb75f89a980617d8b637d5a7cee21fef85..fcd1ed8015c1c2d3b4f4dbdca4f202ffc2dd281a 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 9f118e2f350d656d423530c3a8b5a1c5a55d0b7f..ed1af80432b32cb6a18f9543c568b4df05b8a6c2 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 dbfe21c99c487380f926a8497b9a20bef61def35..ce1f583459b1f4875db0d3eeea72d728aa0a9873 100644 (file)
 #include "irq.h"
 #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 fa5ed5d59b5da9fa83529b42e09301f25f97f87a..1802134b836fc352529f59e6fd7b82b81ef28e95 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 31280df7d2e31a70d0eccfe521434a8cc1968826..debf58211bdd2999eb9afe8ad57fe285abd90f71 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 bd0c2d2d840f38e4cb63cd27fe867df159ece047..0bc400387cae405fcc0ce6048d918db39cd72924 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 d5911d9895c3d6867d8e289dc520b8a6a6a52c62..47cbc6e3fafdffb3690e3eeaafbf94f9f8604fc9 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);