aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/include/asm/nmi.h22
-rw-r--r--arch/x86/kernel/nmi.c83
-rw-r--r--drivers/watchdog/hpwdt.c46
-rw-r--r--kernel/hung_task.c4
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);
27enum { 27enum {
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
36typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *); 38typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *);
37 39
38int register_nmi_handler(unsigned int, nmi_handler_t, unsigned long, 40struct 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
57int __register_nmi_handler(unsigned int, struct nmiaction *);
40 58
41void unregister_nmi_handler(unsigned int, const char *); 59void 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
35struct nmiaction {
36 struct list_head list;
37 nmi_handler_t handler;
38 unsigned int flags;
39 char *name;
40};
41
42struct nmi_desc { 34struct 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
110static int __setup_nmi(unsigned int type, struct nmiaction *action) 110int __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}
141EXPORT_SYMBOL(__register_nmi_handler);
136 142
137static struct nmiaction *__free_nmi(unsigned int type, const char *name) 143void 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
163int 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
189fail_setup_nmi:
190 kfree(action->name);
191fail_action_name:
192 kfree(action);
193fail_action:
194
195 return retval;
196}
197EXPORT_SYMBOL_GPL(register_nmi_handler);
198
199void 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
210EXPORT_SYMBOL_GPL(unregister_nmi_handler); 167EXPORT_SYMBOL_GPL(unregister_nmi_handler);
211 168
212static notrace __kprobes void 169static notrace __kprobes void
213pci_serr_error(unsigned char reason, struct pt_regs *regs) 170pci_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
148static unsigned int hpwdt_nmi_decoding; 148static unsigned int hpwdt_nmi_decoding;
149static unsigned int allow_kdump; 149static unsigned int allow_kdump;
150static unsigned int priority; /* hpwdt at end of die_notify list */
151static unsigned int is_icru; 150static unsigned int is_icru;
152static DEFINE_SPINLOCK(rom_lock); 151static DEFINE_SPINLOCK(rom_lock);
153static void *cru_rom_addr; 152static 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
743error2:
744 unregister_nmi_handler(NMI_SERR, "hpwdt");
745error1:
746 unregister_nmi_handler(NMI_UNKNOWN, "hpwdt");
747error:
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
750static void hpwdt_exit_nmi_decoding(void) 756static 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
882module_param(allow_kdump, int, 0); 888module_param(allow_kdump, int, 0);
883MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs"); 889MODULE_PARM_DESC(allow_kdump, "Start a kernel dump after NMI occurs");
884
885module_param(priority, int, 0);
886MODULE_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
890module_init(hpwdt_init); 892module_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/*