KVM: x86: Enable NMI Watchdog via in-kernel PIT source
authorJan Kiszka <jan.kiszka@siemens.com>
Fri, 26 Sep 2008 07:30:52 +0000 (09:30 +0200)
committerAvi Kivity <avi@redhat.com>
Wed, 31 Dec 2008 14:51:41 +0000 (16:51 +0200)
LINT0 of the LAPIC can be used to route PIT events as NMI watchdog ticks
into the guest. This patch aligns the in-kernel irqchip emulation with
the user space irqchip with already supports this feature. The trick is
to route PIT interrupts to all LAPIC's LVT0 lines.

Rebased and slightly polished patch originally posted by Sheng Yang.

Signed-off-by: Jan Kiszka <jan.kiszka@siemens.com>
Signed-off-by: Sheng Yang <sheng.yang@intel.com>
Signed-off-by: Avi Kivity <avi@redhat.com>
arch/x86/kvm/i8254.c
arch/x86/kvm/irq.h
arch/x86/kvm/lapic.c

index 59ebd37ad79e25b658217e02dd5b57ab38f18c21..580cc1d01c7dc9790bf9a155ad8b11677c63d0ef 100644 (file)
@@ -603,10 +603,25 @@ void kvm_free_pit(struct kvm *kvm)
 
 static void __inject_pit_timer_intr(struct kvm *kvm)
 {
+       struct kvm_vcpu *vcpu;
+       int i;
+
        mutex_lock(&kvm->lock);
        kvm_set_irq(kvm, kvm->arch.vpit->irq_source_id, 0, 1);
        kvm_set_irq(kvm, kvm->arch.vpit->irq_source_id, 0, 0);
        mutex_unlock(&kvm->lock);
+
+       /*
+        * Provides NMI watchdog support in IOAPIC mode.
+        * The route is: PIT -> PIC -> LVT0 in NMI mode,
+        * timer IRQs will continue to flow through the IOAPIC.
+        */
+       for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+               vcpu = kvm->vcpus[i];
+               if (!vcpu)
+                       continue;
+               kvm_apic_local_deliver(vcpu, APIC_LVT0);
+       }
 }
 
 void kvm_inject_pit_timer_irqs(struct kvm_vcpu *vcpu)
index f17c8f5bbf31b8bae7b1d2a0d770bd5a968a7a6a..71e37a530cf73d20a7ab4eb73a4ff98dba14d634 100644 (file)
@@ -87,6 +87,7 @@ void kvm_pic_reset(struct kvm_kpic_state *s);
 void kvm_timer_intr_post(struct kvm_vcpu *vcpu, int vec);
 void kvm_inject_pending_timer_irqs(struct kvm_vcpu *vcpu);
 void kvm_inject_apic_timer_irqs(struct kvm_vcpu *vcpu);
+int kvm_apic_local_deliver(struct kvm_vcpu *vcpu, int lvt_type);
 void __kvm_migrate_apic_timer(struct kvm_vcpu *vcpu);
 void __kvm_migrate_pit_timer(struct kvm_vcpu *vcpu);
 void __kvm_migrate_timers(struct kvm_vcpu *vcpu);
index 0fc3cab48943da8a3c513c7c753a4792c6743198..206cc11a1c9776728118563de9978165498ec50f 100644 (file)
@@ -380,6 +380,14 @@ static int __apic_accept_irq(struct kvm_lapic *apic, int delivery_mode,
                }
                break;
 
+       case APIC_DM_EXTINT:
+               /*
+                * Should only be called by kvm_apic_local_deliver() with LVT0,
+                * before NMI watchdog was enabled. Already handled by
+                * kvm_apic_accept_pic_intr().
+                */
+               break;
+
        default:
                printk(KERN_ERR "TODO: unsupported delivery mode %x\n",
                       delivery_mode);
@@ -743,10 +751,13 @@ static void apic_mmio_write(struct kvm_io_device *this,
                apic_set_reg(apic, APIC_ICR2, val & 0xff000000);
                break;
 
+       case APIC_LVT0:
+               if (val == APIC_DM_NMI)
+                       apic_debug("Receive NMI setting on APIC_LVT0 "
+                               "for cpu %d\n", apic->vcpu->vcpu_id);
        case APIC_LVTT:
        case APIC_LVTTHMR:
        case APIC_LVTPC:
-       case APIC_LVT0:
        case APIC_LVT1:
        case APIC_LVTERR:
                /* TODO: Check vector */
@@ -961,12 +972,25 @@ int apic_has_pending_timer(struct kvm_vcpu *vcpu)
        return 0;
 }
 
-static int __inject_apic_timer_irq(struct kvm_lapic *apic)
+int kvm_apic_local_deliver(struct kvm_vcpu *vcpu, int lvt_type)
 {
-       int vector;
+       struct kvm_lapic *apic = vcpu->arch.apic;
+       int vector, mode, trig_mode;
+       u32 reg;
+
+       if (apic && apic_enabled(apic)) {
+               reg = apic_get_reg(apic, lvt_type);
+               vector = reg & APIC_VECTOR_MASK;
+               mode = reg & APIC_MODE_MASK;
+               trig_mode = reg & APIC_LVT_LEVEL_TRIGGER;
+               return __apic_accept_irq(apic, mode, vector, 1, trig_mode);
+       }
+       return 0;
+}
 
-       vector = apic_lvt_vector(apic, APIC_LVTT);
-       return __apic_accept_irq(apic, APIC_DM_FIXED, vector, 1, 0);
+static inline int __inject_apic_timer_irq(struct kvm_lapic *apic)
+{
+       return kvm_apic_local_deliver(apic->vcpu, APIC_LVTT);
 }
 
 static enum hrtimer_restart apic_timer_fn(struct hrtimer *data)