[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-changelog] Pull nmi/traps changes from Linux 2.6.14.



# HG changeset patch
# User kaf24@xxxxxxxxxxxxxxxxxxxx
# Node ID db500b8cb79acbce4a03cee0b10b0445a9ea1558
# Parent  06b80b837c92b5d6f6a50a2a569313293659d8f5

Pull nmi/traps changes from Linux 2.6.14.

Signed-off-by: Keir Fraser <keir@xxxxxxxxxxxxx>

diff -r 06b80b837c92 -r db500b8cb79a xen/arch/x86/nmi.c
--- a/xen/arch/x86/nmi.c        Tue Dec  6 16:40:50 2005
+++ b/xen/arch/x86/nmi.c        Tue Dec  6 16:48:57 2005
@@ -9,7 +9,8 @@
  *  Mikael Pettersson  : AMD K7 support for local APIC NMI watchdog.
  *  Mikael Pettersson  : Power Management for local APIC NMI watchdog.
  *  Mikael Pettersson  : Pentium 4 support for local APIC NMI watchdog.
- *  Keir Fraser         : Pentium 4 Hyperthreading support
+ *  Pavel Machek and
+ *  Mikael Pettersson  : PM converted to driver model. Disable/enable API.
  */
 
 #include <xen/config.h>
@@ -27,6 +28,7 @@
 #include <asm/msr.h>
 #include <asm/mpspec.h>
 #include <asm/debugger.h>
+#include <asm/div64.h>
 
 unsigned int nmi_watchdog = NMI_NONE;
 static unsigned int nmi_hz = HZ;
@@ -34,6 +36,28 @@
 static unsigned int nmi_p4_cccr_val;
 static struct ac_timer nmi_timer[NR_CPUS];
 static unsigned int nmi_timer_ticks[NR_CPUS];
+
+/*
+ * lapic_nmi_owner tracks the ownership of the lapic NMI hardware:
+ * - it may be reserved by some other driver, or not
+ * - when not reserved by some other driver, it may be used for
+ *   the NMI watchdog, or not
+ *
+ * This is maintained separately from nmi_active because the NMI
+ * watchdog may also be driven from the I/O APIC timer.
+ */
+static DEFINE_SPINLOCK(lapic_nmi_owner_lock);
+static unsigned int lapic_nmi_owner;
+#define LAPIC_NMI_WATCHDOG     (1<<0)
+#define LAPIC_NMI_RESERVED     (1<<1)
+
+/* nmi_active:
+ * +1: the lapic NMI watchdog is active, but can be disabled
+ *  0: the lapic NMI watchdog has not been set up, and cannot
+ *     be enabled
+ * -1: the lapic NMI watchdog is disabled, but can be enabled
+ */
+int nmi_active;
 
 #define K7_EVNTSEL_ENABLE      (1 << 22)
 #define K7_EVNTSEL_INT         (1 << 20)
@@ -111,8 +135,73 @@
     set_ac_timer(&nmi_timer[cpu], NOW() + MILLISECS(1000));
 }
 
-static inline void nmi_pm_init(void) { }
-#define __pminit       __init
+static void disable_lapic_nmi_watchdog(void)
+{
+    if (nmi_active <= 0)
+        return;
+    switch (boot_cpu_data.x86_vendor) {
+    case X86_VENDOR_AMD:
+        wrmsr(MSR_K7_EVNTSEL0, 0, 0);
+        break;
+    case X86_VENDOR_INTEL:
+        switch (boot_cpu_data.x86) {
+        case 6:
+            if (boot_cpu_data.x86_model > 0xd)
+                break;
+
+            wrmsr(MSR_P6_EVNTSEL0, 0, 0);
+            break;
+        case 15:
+            if (boot_cpu_data.x86_model > 0x4)
+                break;
+
+            wrmsr(MSR_P4_IQ_CCCR0, 0, 0);
+            wrmsr(MSR_P4_CRU_ESCR0, 0, 0);
+            break;
+        }
+        break;
+    }
+    nmi_active = -1;
+    /* tell do_nmi() and others that we're not active any more */
+    nmi_watchdog = 0;
+}
+
+static void enable_lapic_nmi_watchdog(void)
+{
+    if (nmi_active < 0) {
+        nmi_watchdog = NMI_LOCAL_APIC;
+        setup_apic_nmi_watchdog();
+    }
+}
+
+int reserve_lapic_nmi(void)
+{
+    unsigned int old_owner;
+
+    spin_lock(&lapic_nmi_owner_lock);
+    old_owner = lapic_nmi_owner;
+    lapic_nmi_owner |= LAPIC_NMI_RESERVED;
+    spin_unlock(&lapic_nmi_owner_lock);
+    if (old_owner & LAPIC_NMI_RESERVED)
+        return -EBUSY;
+    if (old_owner & LAPIC_NMI_WATCHDOG)
+        disable_lapic_nmi_watchdog();
+    return 0;
+}
+
+void release_lapic_nmi(void)
+{
+    unsigned int new_owner;
+
+    spin_lock(&lapic_nmi_owner_lock);
+    new_owner = lapic_nmi_owner & ~LAPIC_NMI_RESERVED;
+    lapic_nmi_owner = new_owner;
+    spin_unlock(&lapic_nmi_owner_lock);
+    if (new_owner & LAPIC_NMI_WATCHDOG)
+        enable_lapic_nmi_watchdog();
+}
+
+#define __pminit __init
 
 /*
  * Activate the NMI watchdog via the local APIC.
@@ -122,8 +211,19 @@
 static void __pminit clear_msr_range(unsigned int base, unsigned int n)
 {
     unsigned int i;
-    for ( i = 0; i < n; i++ )
+
+    for (i = 0; i < n; i++)
         wrmsr(base+i, 0, 0);
+}
+
+static inline void write_watchdog_counter(const char *descr)
+{
+    u64 count = (u64)cpu_khz * 1000;
+
+    do_div(count, nmi_hz);
+    if(descr)
+        Dprintk("setting %s to -0x%08Lx\n", descr, count);
+    wrmsrl(nmi_perfctr_msr, 0 - count);
 }
 
 static void __pminit setup_k7_watchdog(void)
@@ -141,8 +241,7 @@
         | K7_NMI_EVENT;
 
     wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);
-    Dprintk("setting K7_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000));
-    wrmsr(MSR_K7_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1);
+    write_watchdog_counter("K7_PERFCTR0");
     apic_write(APIC_LVTPC, APIC_DM_NMI);
     evntsel |= K7_EVNTSEL_ENABLE;
     wrmsr(MSR_K7_EVNTSEL0, evntsel, 0);
@@ -163,8 +262,7 @@
         | P6_NMI_EVENT;
 
     wrmsr(MSR_P6_EVNTSEL0, evntsel, 0);
-    Dprintk("setting P6_PERFCTR0 to %08lx\n", -(cpu_khz/nmi_hz*1000));
-    wrmsr(MSR_P6_PERFCTR0, -(cpu_khz/nmi_hz*1000), 0);
+    write_watchdog_counter("P6_PERFCTR0");
     apic_write(APIC_LVTPC, APIC_DM_NMI);
     evntsel |= P6_EVNTSEL0_ENABLE;
     wrmsr(MSR_P6_EVNTSEL0, evntsel, 0);
@@ -187,7 +285,13 @@
         clear_msr_range(0x3F1, 2);
     /* MSR 0x3F0 seems to have a default value of 0xFC00, but current
        docs doesn't fully define it, so leave it alone for now. */
-    clear_msr_range(0x3A0, 31);
+    if (boot_cpu_data.x86_model >= 0x3) {
+        /* MSR_P4_IQ_ESCR0/1 (0x3ba/0x3bb) removed */
+        clear_msr_range(0x3A0, 26);
+        clear_msr_range(0x3BC, 3);
+    } else {
+        clear_msr_range(0x3A0, 31);
+    }
     clear_msr_range(0x3C0, 6);
     clear_msr_range(0x3C8, 6);
     clear_msr_range(0x3E0, 2);
@@ -196,11 +300,9 @@
         
     wrmsr(MSR_P4_CRU_ESCR0, P4_NMI_CRU_ESCR0, 0);
     wrmsr(MSR_P4_IQ_CCCR0, P4_NMI_IQ_CCCR0 & ~P4_CCCR_ENABLE, 0);
-    Dprintk("setting P4_IQ_PERFCTR0 to 0x%08lx\n", -(cpu_khz/nmi_hz*1000));
-    wrmsr(MSR_P4_IQ_PERFCTR0, -(cpu_khz/nmi_hz*1000), -1);
+    write_watchdog_counter("P4_IQ_COUNTER0");
     apic_write(APIC_LVTPC, APIC_DM_NMI);
     wrmsr(MSR_P4_IQ_CCCR0, nmi_p4_cccr_val, 0);
-
     return 1;
 }
 
@@ -220,9 +322,15 @@
     case X86_VENDOR_INTEL:
         switch (boot_cpu_data.x86) {
         case 6:
+            if (boot_cpu_data.x86_model > 0xd)
+                return;
+
             setup_p6_watchdog();
             break;
         case 15:
+            if (boot_cpu_data.x86_model > 0x4)
+                return;
+
             if (!setup_p4_watchdog())
                 return;
             break;
@@ -234,11 +342,11 @@
         return;
     }
 
+    lapic_nmi_owner = LAPIC_NMI_WATCHDOG;
+    nmi_active = 1;
+
     init_ac_timer(&nmi_timer[cpu], nmi_timer_fn, NULL, cpu);
-
-    nmi_pm_init();
-}
-
+}
 
 static unsigned int
 last_irq_sums [NR_CPUS],
@@ -329,6 +437,6 @@
              */
             apic_write(APIC_LVTPC, APIC_DM_NMI);
         }
-        wrmsr(nmi_perfctr_msr, -(cpu_khz/nmi_hz*1000), -1);
-    }
-}
+        write_watchdog_counter(NULL);
+    }
+}
diff -r 06b80b837c92 -r db500b8cb79a xen/arch/x86/traps.c
--- a/xen/arch/x86/traps.c      Tue Dec  6 16:40:50 2005
+++ b/xen/arch/x86/traps.c      Tue Dec  6 16:48:57 2005
@@ -54,6 +54,7 @@
 #include <asm/debugger.h>
 #include <asm/msr.h>
 #include <asm/x86_emulate.h>
+#include <asm/nmi.h>
 
 /*
  * opt_nmi: one of 'ignore', 'dom0', or 'fatal'.
@@ -1131,10 +1132,8 @@
     printk("Do you have a strange power saving mode enabled?\n");
 }
 
-asmlinkage void do_nmi(struct cpu_user_regs *regs, unsigned long reason)
-{
-    ++nmi_count(smp_processor_id());
-
+static void default_do_nmi(struct cpu_user_regs *regs, unsigned long reason)
+{
     if ( nmi_watchdog )
         nmi_watchdog_tick(regs);
 
@@ -1144,6 +1143,33 @@
         io_check_error(regs);
     else if ( !nmi_watchdog )
         unknown_nmi_error((unsigned char)(reason&0xff));
+}
+
+static int dummy_nmi_callback(struct cpu_user_regs *regs, int cpu)
+{
+       return 0;
+}
+ 
+static nmi_callback_t nmi_callback = dummy_nmi_callback;
+ 
+asmlinkage void do_nmi(struct cpu_user_regs *regs, unsigned long reason)
+{
+    unsigned int cpu = smp_processor_id();
+
+    ++nmi_count(cpu);
+
+       if ( !nmi_callback(regs, cpu) )
+               default_do_nmi(regs, reason);
+}
+
+void set_nmi_callback(nmi_callback_t callback)
+{
+    nmi_callback = callback;
+}
+
+void unset_nmi_callback(void)
+{
+       nmi_callback = dummy_nmi_callback;
 }
 
 asmlinkage int math_state_restore(struct cpu_user_regs *regs)
diff -r 06b80b837c92 -r db500b8cb79a xen/include/asm-x86/msr.h
--- a/xen/include/asm-x86/msr.h Tue Dec  6 16:40:50 2005
+++ b/xen/include/asm-x86/msr.h Tue Dec  6 16:48:57 2005
@@ -1,5 +1,7 @@
 #ifndef __ASM_MSR_H
 #define __ASM_MSR_H
+
+#ifndef __ASSEMBLY__
 
 #define rdmsr(msr,val1,val2) \
      __asm__ __volatile__("rdmsr" \
@@ -18,7 +20,13 @@
                          : /* no outputs */ \
                          : "c" (msr), "a" (val1), "d" (val2))
 
-#define wrmsrl(msr,val) wrmsr(msr,(__u32)((__u64)(val)),((__u64)(val))>>32) 
+static inline void wrmsrl(unsigned int msr, __u64 val)
+{
+        __u32 lo, hi;
+        lo = (__u32)val;
+        hi = (__u32)(val >> 32);
+        wrmsr(msr, lo, hi);
+}
 
 #define rdmsr_user(msr,val1,val2) ({\
     int _rc; \
@@ -73,6 +81,8 @@
      __asm__ __volatile__("rdpmc" \
                          : "=a" (low), "=d" (high) \
                          : "c" (counter))
+
+#endif /* !__ASSEMBLY__ */
 
 /* symbolic names for some interesting MSRs */
 /* Intel defined MSRs. */
diff -r 06b80b837c92 -r db500b8cb79a xen/include/asm-x86/nmi.h
--- /dev/null   Tue Dec  6 16:40:50 2005
+++ b/xen/include/asm-x86/nmi.h Tue Dec  6 16:48:57 2005
@@ -0,0 +1,24 @@
+
+#ifndef ASM_NMI_H
+#define ASM_NMI_H
+
+struct cpu_user_regs;
+ 
+typedef int (*nmi_callback_t)(struct cpu_user_regs *regs, int cpu);
+ 
+/** 
+ * set_nmi_callback
+ *
+ * Set a handler for an NMI. Only one handler may be
+ * set. Return 1 if the NMI was handled.
+ */
+void set_nmi_callback(nmi_callback_t callback);
+ 
+/** 
+ * unset_nmi_callback
+ *
+ * Remove the handler previously set.
+ */
+void unset_nmi_callback(void);
+ 
+#endif /* ASM_NMI_H */

_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-changelog


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.