]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] oprofile - NMI hook
authorJohn Levon <levon@movementarian.org>
Tue, 15 Oct 2002 11:30:44 +0000 (04:30 -0700)
committerLinus Torvalds <torvalds@home.transmeta.com>
Tue, 15 Oct 2002 11:30:44 +0000 (04:30 -0700)
This provides a simple api to let oprofile hook into the NMI interrupt
for the perfctr profiler.

arch/i386/kernel/i386_ksyms.c
arch/i386/kernel/nmi.c
arch/i386/kernel/traps.c
include/asm-i386/nmi.h [new file with mode: 0644]

index 9314e0b9f880513921d144611b6740c36268a778..f9dc46f34e6d22c8e9994deb60e5d0a4daacacaa 100644 (file)
@@ -29,6 +29,7 @@
 #include <asm/pgtable.h>
 #include <asm/pgalloc.h>
 #include <asm/tlbflush.h>
+#include <asm/nmi.h>
 
 extern void dump_thread(struct pt_regs *, struct user *);
 extern spinlock_t rtc_lock;
@@ -151,6 +152,10 @@ EXPORT_SYMBOL(smp_call_function);
 EXPORT_SYMBOL(flush_tlb_page);
 #endif
 
+#if defined(CONFIG_X86_LOCAL_APIC) && defined(CONFIG_PM)
+EXPORT_SYMBOL_GPL(set_nmi_pm_callback);
+EXPORT_SYMBOL_GPL(unset_nmi_pm_callback);
+#endif
 #ifdef CONFIG_X86_IO_APIC
 EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector);
 #endif
@@ -169,6 +174,8 @@ EXPORT_SYMBOL(rtc_lock);
 
 EXPORT_SYMBOL_GPL(register_profile_notifier);
 EXPORT_SYMBOL_GPL(unregister_profile_notifier);
+EXPORT_SYMBOL_GPL(set_nmi_callback);
+EXPORT_SYMBOL_GPL(unset_nmi_callback);
  
 #undef memcpy
 #undef memset
index fb2026daf3f10e414e33c0b5c5fbfe27944534fd..fbae2c8deeaf5247d6da6d58ce2567cd4fb1301d 100644 (file)
@@ -175,6 +175,18 @@ static int nmi_pm_callback(struct pm_dev *dev, pm_request_t rqst, void *data)
        return 0;
 }
 
+struct pm_dev * set_nmi_pm_callback(pm_callback callback)
+{
+       apic_pm_unregister(nmi_pmdev);
+       return apic_pm_register(PM_SYS_DEV, 0, callback);
+}
+
+void unset_nmi_pm_callback(struct pm_dev * dev)
+{
+       apic_pm_unregister(dev);
+       nmi_pmdev = apic_pm_register(PM_SYS_DEV, 0, nmi_pm_callback);
+}
 static void nmi_pm_init(void)
 {
        if (!nmi_pmdev)
index 68ed7969fe6da972404f6ba4dcb8648a34ad0660..3eeb2c41814b61cb0a7b9f0402dc24df5c395b06 100644 (file)
@@ -40,6 +40,7 @@
 #include <asm/debugreg.h>
 #include <asm/desc.h>
 #include <asm/i387.h>
+#include <asm/nmi.h>
 
 #include <asm/smp.h>
 #include <asm/pgalloc.h>
@@ -478,17 +479,16 @@ static void unknown_nmi_error(unsigned char reason, struct pt_regs * regs)
                return;
        }
 #endif
-       printk("Uhhuh. NMI received for unknown reason %02x.\n", reason);
+       printk("Uhhuh. NMI received for unknown reason %02x on CPU %d.\n",
+               reason, smp_processor_id());
        printk("Dazed and confused, but trying to continue\n");
        printk("Do you have a strange power saving mode enabled?\n");
 }
 
-asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
+static void default_do_nmi(struct pt_regs * regs)
 {
        unsigned char reason = inb(0x61);
-
-       ++nmi_count(smp_processor_id());
-
        if (!(reason & 0xc0)) {
 #if CONFIG_X86_LOCAL_APIC
                /*
@@ -517,6 +517,33 @@ asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
        inb(0x71);              /* dummy */
 }
 
+static int dummy_nmi_callback(struct pt_regs * regs, int cpu)
+{
+       return 0;
+}
+static nmi_callback_t nmi_callback = dummy_nmi_callback;
+asmlinkage void do_nmi(struct pt_regs * regs, long error_code)
+{
+       int cpu = smp_processor_id();
+
+       ++nmi_count(cpu);
+
+       if (!nmi_callback(regs, cpu))
+               default_do_nmi(regs);
+}
+
+void set_nmi_callback(nmi_callback_t callback)
+{
+       nmi_callback = callback;
+}
+
+void unset_nmi_callback(void)
+{
+       nmi_callback = dummy_nmi_callback;
+}
+
 /*
  * Our handling of the processor debug registers is non-trivial.
  * We do not clear them on entry and exit from the kernel. Therefore
diff --git a/include/asm-i386/nmi.h b/include/asm-i386/nmi.h
new file mode 100644 (file)
index 0000000..d20f0fb
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *  linux/include/asm-i386/nmi.h
+ */
+#ifndef ASM_NMI_H
+#define ASM_NMI_H
+
+#include <linux/pm.h>
+struct pt_regs;
+typedef int (*nmi_callback_t)(struct pt_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);
+#ifdef CONFIG_PM
+/** Replace the PM callback routine for NMI. */
+struct pm_dev * set_nmi_pm_callback(pm_callback callback);
+
+/** Unset the PM callback routine back to the default. */
+void unset_nmi_pm_callback(struct pm_dev * dev);
+
+#else
+
+static inline struct pm_dev * set_nmi_pm_callback(pm_callback callback)
+{
+       return 0;
+} 
+static inline void unset_nmi_pm_callback(struct pm_dev * dev)
+{
+}
+
+#endif /* CONFIG_PM */
+#endif /* ASM_NMI_H */