Commit e0346c53 authored by John Levon's avatar John Levon Committed by Linus Torvalds

[PATCH] oprofile - NMI hook

This provides a simple api to let oprofile hook into the NMI interrupt
for the perfctr profiler.
parent 120790b8
......@@ -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
......
......@@ -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)
......
......@@ -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
......
/*
* 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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment