diff options
-rw-r--r-- | arch/x86/include/asm/nmi.h | 22 | ||||
-rw-r--r-- | arch/x86/kernel/nmi.c | 83 | ||||
-rw-r--r-- | drivers/watchdog/hpwdt.c | 46 | ||||
-rw-r--r-- | kernel/hung_task.c | 4 |
4 files changed, 71 insertions, 84 deletions
diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h index fd3f9f18cf3f..0e3793b821ef 100644 --- a/arch/x86/include/asm/nmi.h +++ b/arch/x86/include/asm/nmi.h | |||
@@ -27,6 +27,8 @@ void arch_trigger_all_cpu_backtrace(void); | |||
27 | enum { | 27 | enum { |
28 | NMI_LOCAL=0, | 28 | NMI_LOCAL=0, |
29 | NMI_UNKNOWN, | 29 | NMI_UNKNOWN, |
30 | NMI_SERR, | ||
31 | NMI_IO_CHECK, | ||
30 | NMI_MAX | 32 | NMI_MAX |
31 | }; | 33 | }; |
32 | 34 | ||
@@ -35,8 +37,24 @@ enum { | |||
35 | 37 | ||
36 | typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); | 38 | typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); |
37 | 39 | ||
38 | int register_nmi_handler(unsigned int, nmi_handler_t, unsigned long, | 40 | struct nmiaction { |
39 | const char *); | 41 | struct list_head list; |
42 | nmi_handler_t handler; | ||
43 | unsigned long flags; | ||
44 | const char *name; | ||
45 | }; | ||
46 | |||
47 | #define register_nmi_handler(t, fn, fg, n) \ | ||
48 | ({ \ | ||
49 | static struct nmiaction fn##_na = { \ | ||
50 | .handler = (fn), \ | ||
51 | .name = (n), \ | ||
52 | .flags = (fg), \ | ||
53 | }; \ | ||
54 | __register_nmi_handler((t), &fn##_na); \ | ||
55 | }) | ||
56 | |||
57 | int __register_nmi_handler(unsigned int, struct nmiaction *); | ||
40 | 58 | ||
41 | void unregister_nmi_handler(unsigned int, const char *); | 59 | void unregister_nmi_handler(unsigned int, const char *); |
42 | 60 | ||
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index 47acaf319165..585be4bd71a5 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c | |||
@@ -31,14 +31,6 @@ | |||
31 | #include <asm/nmi.h> | 31 | #include <asm/nmi.h> |
32 | #include <asm/x86_init.h> | 32 | #include <asm/x86_init.h> |
33 | 33 | ||
34 | #define NMI_MAX_NAMELEN 16 | ||
35 | struct nmiaction { | ||
36 | struct list_head list; | ||
37 | nmi_handler_t handler; | ||
38 | unsigned int flags; | ||
39 | char *name; | ||
40 | }; | ||
41 | |||
42 | struct nmi_desc { | 34 | struct nmi_desc { |
43 | spinlock_t lock; | 35 | spinlock_t lock; |
44 | struct list_head head; | 36 | struct list_head head; |
@@ -54,6 +46,14 @@ static struct nmi_desc nmi_desc[NMI_MAX] = | |||
54 | .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock), | 46 | .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock), |
55 | .head = LIST_HEAD_INIT(nmi_desc[1].head), | 47 | .head = LIST_HEAD_INIT(nmi_desc[1].head), |
56 | }, | 48 | }, |
49 | { | ||
50 | .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[2].lock), | ||
51 | .head = LIST_HEAD_INIT(nmi_desc[2].head), | ||
52 | }, | ||
53 | { | ||
54 | .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[3].lock), | ||
55 | .head = LIST_HEAD_INIT(nmi_desc[3].head), | ||
56 | }, | ||
57 | 57 | ||
58 | }; | 58 | }; |
59 | 59 | ||
@@ -107,11 +107,14 @@ static int notrace __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, | |||
107 | return handled; | 107 | return handled; |
108 | } | 108 | } |
109 | 109 | ||
110 | static int __setup_nmi(unsigned int type, struct nmiaction *action) | 110 | int __register_nmi_handler(unsigned int type, struct nmiaction *action) |
111 | { | 111 | { |
112 | struct nmi_desc *desc = nmi_to_desc(type); | 112 | struct nmi_desc *desc = nmi_to_desc(type); |
113 | unsigned long flags; | 113 | unsigned long flags; |
114 | 114 | ||
115 | if (!action->handler) | ||
116 | return -EINVAL; | ||
117 | |||
115 | spin_lock_irqsave(&desc->lock, flags); | 118 | spin_lock_irqsave(&desc->lock, flags); |
116 | 119 | ||
117 | /* | 120 | /* |
@@ -120,6 +123,8 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action) | |||
120 | * to manage expectations | 123 | * to manage expectations |
121 | */ | 124 | */ |
122 | WARN_ON_ONCE(type == NMI_UNKNOWN && !list_empty(&desc->head)); | 125 | WARN_ON_ONCE(type == NMI_UNKNOWN && !list_empty(&desc->head)); |
126 | WARN_ON_ONCE(type == NMI_SERR && !list_empty(&desc->head)); | ||
127 | WARN_ON_ONCE(type == NMI_IO_CHECK && !list_empty(&desc->head)); | ||
123 | 128 | ||
124 | /* | 129 | /* |
125 | * some handlers need to be executed first otherwise a fake | 130 | * some handlers need to be executed first otherwise a fake |
@@ -133,8 +138,9 @@ static int __setup_nmi(unsigned int type, struct nmiaction *action) | |||
133 | spin_unlock_irqrestore(&desc->lock, flags); | 138 | spin_unlock_irqrestore(&desc->lock, flags); |
134 | return 0; | 139 | return 0; |
135 | } | 140 | } |
141 | EXPORT_SYMBOL(__register_nmi_handler); | ||
136 | 142 | ||
137 | static struct nmiaction *__free_nmi(unsigned int type, const char *name) | 143 | void unregister_nmi_handler(unsigned int type, const char *name) |
138 | { | 144 | { |
139 | struct nmi_desc *desc = nmi_to_desc(type); | 145 | struct nmi_desc *desc = nmi_to_desc(type); |
140 | struct nmiaction *n; | 146 | struct nmiaction *n; |
@@ -157,61 +163,16 @@ static struct nmiaction *__free_nmi(unsigned int type, const char *name) | |||
157 | 163 | ||
158 | spin_unlock_irqrestore(&desc->lock, flags); | 164 | spin_unlock_irqrestore(&desc->lock, flags); |
159 | synchronize_rcu(); | 165 | synchronize_rcu(); |
160 | return (n); | ||
161 | } | 166 | } |
162 | |||
163 | int register_nmi_handler(unsigned int type, nmi_handler_t handler, | ||
164 | unsigned long nmiflags, const char *devname) | ||
165 | { | ||
166 | struct nmiaction *action; | ||
167 | int retval = -ENOMEM; | ||
168 | |||
169 | if (!handler) | ||
170 | return -EINVAL; | ||
171 | |||
172 | action = kzalloc(sizeof(struct nmiaction), GFP_KERNEL); | ||
173 | if (!action) | ||
174 | goto fail_action; | ||
175 | |||
176 | action->handler = handler; | ||
177 | action->flags = nmiflags; | ||
178 | action->name = kstrndup(devname, NMI_MAX_NAMELEN, GFP_KERNEL); | ||
179 | if (!action->name) | ||
180 | goto fail_action_name; | ||
181 | |||
182 | retval = __setup_nmi(type, action); | ||
183 | |||
184 | if (retval) | ||
185 | goto fail_setup_nmi; | ||
186 | |||
187 | return retval; | ||
188 | |||
189 | fail_setup_nmi: | ||
190 | kfree(action->name); | ||
191 | fail_action_name: | ||
192 | kfree(action); | ||
193 | fail_action: | ||
194 | |||
195 | return retval; | ||
196 | } | ||
197 | EXPORT_SYMBOL_GPL(register_nmi_handler); | ||
198 | |||
199 | void unregister_nmi_handler(unsigned int type, const char *name) | ||
200 | { | ||
201 | struct nmiaction *a; | ||
202 | |||
203 | a = __free_nmi(type, name); | ||
204 | if (a) { | ||
205 | kfree(a->name); | ||
206 | kfree(a); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | EXPORT_SYMBOL_GPL(unregister_nmi_handler); | 167 | EXPORT_SYMBOL_GPL(unregister_nmi_handler); |
211 | 168 | ||
212 | static notrace __kprobes void | 169 | static notrace __kprobes void |
213 | pci_serr_error(unsigned char reason, struct pt_regs *regs) | 170 | pci_serr_error(unsigned char reason, struct pt_regs *regs) |
214 | { | 171 | { |
172 | /* check to see if anyone registered against these types of errors */ | ||
173 | if (nmi_handle(NMI_SERR, regs, false)) | ||
174 | return; | ||
175 | |||
215 | pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n", | 176 | pr_emerg("NMI: PCI system error (SERR) for reason %02x on CPU %d.\n", |
216 | reason, smp_processor_id()); | 177 | reason, smp_processor_id()); |
217 | 178 | ||
@@ -241,6 +202,10 @@ io_check_error(unsigned char reason, struct pt_regs *regs) | |||
241 | { | 202 | { |
242 | unsigned long i; | 203 | unsigned long i; |
243 | 204 | ||
205 | /* check to see if anyone registered against these types of errors */ | ||
206 | if (nmi_handle(NMI_IO_CHECK, regs, false)) | ||
207 | return; | ||
208 | |||
244 | pr_emerg( | 209 | pr_emerg( |
245 | "NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n", | 210 | "NMI: IOCK error (debug interrupt?) for reason %02x on CPU %d.\n", |
246 | reason, smp_processor_id()); | 211 | reason, smp_processor_id()); |
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 9f13b897fd64..23885f2d56a0 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c | |||
@@ -147,7 +147,6 @@ struct cmn_registers { | |||
147 | 147 | ||
148 | static unsigned int hpwdt_nmi_decoding; | 148 | static unsigned int hpwdt_nmi_decoding; |
149 | static unsigned int allow_kdump; | 149 | static unsigned int allow_kdump; |
150 | static unsigned int priority; /* hpwdt at end of die_notify list */ | ||
151 | static unsigned int is_icru; | 150 | static unsigned int is_icru; |
152 | static DEFINE_SPINLOCK(rom_lock); | 151 | static DEFINE_SPINLOCK(rom_lock); |
153 | static void *cru_rom_addr; | 152 | static void *cru_rom_addr; |
@@ -723,28 +722,35 @@ static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) | |||
723 | } | 722 | } |
724 | 723 | ||
725 | /* | 724 | /* |
726 | * If the priority is set to 1, then we will be put first on the | 725 | * Only one function can register for NMI_UNKNOWN |
727 | * die notify list to handle a critical NMI. The default is to | ||
728 | * be last so other users of the NMI signal can function. | ||
729 | */ | 726 | */ |
730 | retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, | 727 | retval = register_nmi_handler(NMI_UNKNOWN, hpwdt_pretimeout, 0, "hpwdt"); |
731 | (priority) ? NMI_FLAG_FIRST : 0, | 728 | if (retval) |
732 | "hpwdt"); | 729 | goto error; |
733 | if (retval != 0) { | 730 | retval = register_nmi_handler(NMI_SERR, hpwdt_pretimeout, 0, "hpwdt"); |
734 | dev_warn(&dev->dev, | 731 | if (retval) |
735 | "Unable to register a die notifier (err=%d).\n", | 732 | goto error1; |
736 | retval); | 733 | retval = register_nmi_handler(NMI_IO_CHECK, hpwdt_pretimeout, 0, "hpwdt"); |
737 | if (cru_rom_addr) | 734 | if (retval) |
738 | iounmap(cru_rom_addr); | 735 | goto error2; |
739 | } | ||
740 | 736 | ||
741 | dev_info(&dev->dev, | 737 | dev_info(&dev->dev, |
742 | "HP Watchdog Timer Driver: NMI decoding initialized" | 738 | "HP Watchdog Timer Driver: NMI decoding initialized" |
743 | ", allow kernel dump: %s (default = 0/OFF)" | 739 | ", allow kernel dump: %s (default = 0/OFF)\n", |
744 | ", priority: %s (default = 0/LAST).\n", | 740 | (allow_kdump == 0) ? "OFF" : "ON"); |
745 | (allow_kdump == 0) ? "OFF" : "ON", | ||
746 | (priority == 0) ? "LAST" : "FIRST"); | ||
747 | return 0; | 741 | return 0; |
742 | |||
743 | error2: | ||
744 | unregister_nmi_handler(NMI_SERR, "hpwdt"); | ||
745 | error1: | ||
746 | unregister_nmi_handler(NMI_UNKNOWN, "hpwdt"); | ||
747 | error: | ||
748 | dev_warn(&dev->dev, | ||
749 | "Unable to register a die notifier (err=%d).\n", | ||
750 | retval); | ||
751 | if (cru_rom_addr) | ||
752 | iounmap(cru_rom_addr); | ||
753 | return retval; | ||
748 | } | 754 | } |
749 | 755 | ||
750 | static void hpwdt_exit_nmi_decoding(void) | 756 | static void hpwdt_exit_nmi_decoding(void) |
@@ -881,10 +887,6 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" | |||
881 | #ifdef CONFIG_HPWDT_NMI_DECODING | 887 | #ifdef CONFIG_HPWDT_NMI_DECODING |
882 | module_param(allow_kdump, int, 0); | 888 | module_param(allow_kdump, int, 0); |
883 | MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); | 889 | MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); |
884 | |||
885 | module_param(priority, int, 0); | ||
886 | MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" | ||
887 | " (default = 0/Last)\n"); | ||
888 | #endif /* !CONFIG_HPWDT_NMI_DECODING */ | 890 | #endif /* !CONFIG_HPWDT_NMI_DECODING */ |
889 | 891 | ||
890 | module_init(hpwdt_init); | 892 | module_init(hpwdt_init); |
diff --git a/kernel/hung_task.c b/kernel/hung_task.c index c21449f85a2a..6df614912b9d 100644 --- a/kernel/hung_task.c +++ b/kernel/hung_task.c | |||
@@ -108,8 +108,10 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout) | |||
108 | 108 | ||
109 | touch_nmi_watchdog(); | 109 | touch_nmi_watchdog(); |
110 | 110 | ||
111 | if (sysctl_hung_task_panic) | 111 | if (sysctl_hung_task_panic) { |
112 | trigger_all_cpu_backtrace(); | ||
112 | panic("hung_task: blocked tasks"); | 113 | panic("hung_task: blocked tasks"); |
114 | } | ||
113 | } | 115 | } |
114 | 116 | ||
115 | /* | 117 | /* |