Allow multiple NMI handlers to register against NMIs
Subsystems (2)
PATCH 1/1 Allow multiple NMI handlers to register against NMIs
This patch has been posted to LKML a few times. I think it's ready for the mm series, but it needs to be looked over. This requires the previous nmicmos patch that I just posted for x86_64 and is in the mm kernel right now for i386.
-Corey
This patch allows multiple NMI handlers to dynamically bind and unbind to the NMI. The IPMI watchdog driver has the concept of a pretimeout where the hardware can issue an NMI a period of time before the actual watchdog reset. This lets a panic occur instead of a blind reset on a watchdog timeout. If used carefully, multiple users may use the NMI as long as they can correctly identify themselves as the watchdog source. This is possible with nmi_watchdog=2 and oprofile, but not with nmi_watchdog=1.
This is for both x86 and x86_64.
arch/i386/kernel/i386_ksyms.c +0 -3
@@ -167,9 +167,6 @@EXPORT_SYMBOL (rtc_lock); -EXPORT_SYMBOL_GPL (set_nmi_callback); -EXPORT_SYMBOL_GPL (unset_nmi_callback); -#undef memcmpextern int memcmp (const void *,const void *,__kernel_size_t );EXPORT_SYMBOL (memcmp);
arch/i386/kernel/irq.c +4 -0
@@ -204,6 +204,8 @@* /proc/interrupts printing: */ +extern void nmi_append_user_names (struct seq_file *p); +int show_interrupts (struct seq_file *p,void *v) {int i = *(loff_t *) v, j;
@@ -242,6 +244,8 @@seq_printf (p,"NMI: " );for_each_cpu (j)seq_printf (p,"%10u " ,nmi_count (j)); +seq_printf (p," " ); +nmi_append_user_names (p);seq_putc (p,'\n' );#ifdef CONFIG_X86_LOCAL_APIC seq_printf (p,"LOC: " );
arch/i386/kernel/nmi.c +129 -77
304 lines not on this page · raw message
arch/i386/kernel/traps.c +202 -60
326 lines not on this page · raw message
arch/i386/oprofile/nmi_int.c +26 -4
@@ -82,11 +82,23 @@#endif /* CONFIG_PM */ -static int nmi_callback (struct pt_regs * regs,int cpu) +static int nmi_callback (void * dev_id,struct pt_regs * regs,int cpu,int handled) { -return model->check_ctrs (regs, &cpu_msrs[cpu]); +if (model->check_ctrs (regs, &cpu_msrs[cpu])) +return NOTIFY_OK ; + +return NOTIFY_DONE ; } +static struct nmi_handler nmi_handler = +{ + .link =LIST_HEAD_INIT (nmi_handler.link), + .dev_name ="oprofile" , + .dev_id =NULL , + .handler = nmi_callback, + .priority =NMI_HANDLER_MAX_PRIORITY /* Highest possible priority */ +}; +static void nmi_cpu_save_registers (struct op_msrs * msrs) {
@@ -173,8 +185,12 @@ } +static void nmi_cpu_shutdown (void * dummy); +static int nmi_setup (void ) { +int rv; +if (!allocate_msrs ())return -ENOMEM ;
@@ -192,7 +208,13 @@*/ on_each_cpu (nmi_save_registers,NULL ,0 ,1 );on_each_cpu (nmi_cpu_setup,NULL ,0 ,1 ); -set_nmi_callback (nmi_callback); + rv =request_nmi (&nmi_handler); +if (rv) { +smp_call_function (nmi_cpu_shutdown,NULL ,0 ,1 ); +nmi_cpu_shutdown (0 ); +return rv; + } + nmi_enabled =1 ;return 0 ; }
@@ -243,7 +265,7 @@ { nmi_enabled =0 ;on_each_cpu (nmi_cpu_shutdown,NULL ,0 ,1 ); -unset_nmi_callback (); +release_nmi (&nmi_handler);release_lapic_nmi ();free_msrs (); }
arch/x86_64/kernel/nmi.c +88 -88
258 lines not on this page · raw message
drivers/acpi/osl.c +1 -1
@@ -36,7 +36,7 @@#include <linux/kmod.h> #include <linux/delay.h> #include <linux/workqueue.h> -#include <linux/nmi.h> +#include <linux/nmi_watchdog.h> #include <acpi/acpi.h> #include <asm/io.h> #include <acpi/acpi_bus.h>
include/asm-i386/apic.h +0 -1
@@ -117,7 +117,6 @@extern void release_lapic_nmi (void );extern void disable_timer_nmi_watchdog (void );extern void enable_timer_nmi_watchdog (void ); -extern void nmi_watchdog_tick (struct pt_regs * regs);extern int APIC_init_uniprocessor (void );extern void disable_APIC_timer (void );extern void enable_APIC_timer (void );
include/asm-i386/nmi.h +30 -14
54 lines not on this page · raw message
include/linux/nmi.h +7 -18
@@ -1,22 +1,11 @@/* -* linux/include/linux/nmi.h +* linux/include/linux/nmi.h +* +* (C) 2002 Corey Minyard <[email protected]> +* +* Include file for NMI handling. */ -#ifndef LINUX_NMI_H -#define LINUX_NMI_H - -#include <asm/irq.h> - -/** -* touch_nmi_watchdog - restart NMI watchdog timeout. -* -* If the architecture supports the NMI watchdog, touch_nmi_watchdog() -* may be used to reset the timeout - for code which intentionally -* disables interrupts for a long time. This call is stateless. -*/ -#ifdef ARCH_HAS_NMI_WATCHDOG -extern void touch_nmi_watchdog (void ); -#else -# define touch_nmi_watchdog () do { } while(0) -#endif +#if defined(__i386__) +#include <asm/nmi.h> #endif
include/linux/nmi_watchdog.h new file +22 -0
@@ -0,0 +1,22 @@ +/* +* linux/include/linux/nmi.h +*/ +#ifndef LINUX_NMI_WATCHDOG_H +#define LINUX_NMI_WATCHDOG_H + +#include <asm/irq.h> + +/** +* touch_nmi_watchdog - restart NMI watchdog timeout. +* +* If the architecture supports the NMI watchdog, touch_nmi_watchdog() +* may be used to reset the timeout - for code which intentionally +* disables interrupts for a long time. This call is stateless. +*/ +#ifdef ARCH_HAS_NMI_WATCHDOG +extern void touch_nmi_watchdog (void ); +#else +# define touch_nmi_watchdog () do { } while(0) +#endif + +#endif
kernel/panic.c +1 -1
@@ -17,7 +17,7 @@#include <linux/init.h> #include <linux/sysrq.h> #include <linux/interrupt.h> -#include <linux/nmi.h> +#include <linux/nmi_watchdog.h> #include <linux/kexec.h> int panic_timeout;
kernel/sched.c +1 -1
@@ -20,7 +20,7 @@#include <linux/mm.h> #include <linux/module.h> -#include <linux/nmi.h> +#include <linux/nmi_watchdog.h> #include <linux/init.h> #include <asm/uaccess.h> #include <linux/highmem.h>
kernel/sysctl.c +1 -3
@@ -68,8 +68,6 @@#if defined(CONFIG_X86_LOCAL_APIC ) && defined(CONFIG_X86 )int unknown_nmi_panic; -extern int proc_unknown_nmi_panic (ctl_table *,int ,struct file *, -void __user *,size_t *,loff_t *);#endif /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
@@ -621,7 +619,7 @@ .data = &unknown_nmi_panic, .maxlen = sizeof (int), .mode = 0644, - .proc_handler = &proc_unknown_nmi_panic, + .proc_handler = &proc_dointvec, },#endif #if defined(CONFIG_X86 )
arch/i386/oprofile/nmi_timer_int.c +18 -3
@@ -18,16 +18,31 @@#include <asm/apic.h> #include <asm/ptrace.h> -static int nmi_timer_callback (struct pt_regs * regs,int cpu) +static int nmi_timer_callback (void *dev_id,struct pt_regs * regs,int cpu,int handled) {oprofile_add_sample (regs,0 );return 1 ; } +static struct nmi_handler nmi_timer_handler = +{ + .link =LIST_HEAD_INIT (nmi_timer_handler.link), + .dev_name ="oprofile_timer" , + .dev_id =NULL , + .handler = nmi_timer_callback, + .priority =NMI_HANDLER_MAX_PRIORITY /* Highest possible priority */ +}; +static int timer_start (void ) { +int rv; +disable_timer_nmi_watchdog (); -set_nmi_callback (nmi_timer_callback); + rv =request_nmi (&nmi_timer_handler); +if (rv) { +enable_timer_nmi_watchdog (); +return rv; + }return 0 ; }
@@ -35,7 +50,7 @@static void timer_stop (void ) {enable_timer_nmi_watchdog (); -unset_nmi_callback (); +release_nmi (&nmi_timer_handler);synchronize_kernel (); }
include/asm-x86_64/apic.h +0 -1
@@ -93,7 +93,6 @@extern void release_lapic_nmi (void );extern void disable_timer_nmi_watchdog (void );extern void enable_timer_nmi_watchdog (void ); -extern void nmi_watchdog_tick (struct pt_regs * regs,unsigned reason);extern int APIC_init_uniprocessor (void );extern void disable_APIC_timer (void );extern void enable_APIC_timer (void );
arch/x86_64/kernel/x8664_ksyms.c +0 -3
@@ -129,9 +129,6 @@EXPORT_SYMBOL (rtc_lock); -EXPORT_SYMBOL_GPL (set_nmi_callback); -EXPORT_SYMBOL_GPL (unset_nmi_callback); -/* Export string functions. We normally rely on gcc builtin for most of these, but gcc sometimes decides not to inline them. */ #undef memcpy
arch/x86_64/kernel/traps.c +213 -41
303 lines not on this page · raw message
arch/x86_64/kernel/io_apic.c +1 -1
@@ -1670,7 +1670,7 @@ }printk (" failed.\n" ); -if (nmi_watchdog) { +if (nmi_watchdog ==NMI_IO_APIC ) {printk (KERN_WARNING "timer doesn't work through the IO-APIC - disabling NMI Watchdog!\n" ); nmi_watchdog =0 ; }
arch/x86_64/kernel/irq.c +4 -0
@@ -28,6 +28,8 @@* Generic, controller-independent functions: */ +extern void nmi_append_user_names (struct seq_file *p); +int show_interrupts (struct seq_file *p,void *v) {int i = *(loff_t *) v, j;
@@ -69,6 +71,8 @@for (j =0 ; j <NR_CPUS ; j++)if (cpu_online (j))seq_printf (p,"%10u " , cpu_pda[j].__nmi_count); +seq_printf (p," " ); +nmi_append_user_names (p);seq_putc (p,'\n' );#ifdef CONFIG_X86_LOCAL_APIC seq_printf (p,"LOC: " );
include/asm-x86_64/nmi.h +30 -14
56 lines not on this page · raw message