aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorDon Zickus <dzickus@redhat.com>2011-09-30 15:06:20 -0400
committerIngo Molnar <mingo@elte.hu>2011-10-10 00:56:52 -0400
commitc9126b2ee8adb9235941cedbf558d39a9e65642d (patch)
tree9ed4fbec583b1fad741055e3f18070d513d6d763 /arch
parent1d48922c14b6363f6d5febb12464d804bb5cc53f (diff)
x86, nmi: Create new NMI handler routines
The NMI handlers used to rely on the notifier infrastructure. This worked great until we wanted to support handling multiple events better. One of the key ideas to the nmi handling is to process _all_ the handlers for each NMI. The reason behind this switch is because NMIs are edge triggered. If enough NMIs are triggered, then they could be lost because the cpu can only latch at most one NMI (besides the one currently being processed). In order to deal with this we have decided to process all the NMI handlers for each NMI. This allows the handlers to determine if they recieved an event or not (the ones that can not determine this will be left to fend for themselves on the unknown NMI list). As a result of this change it is now possible to have an extra NMI that was destined to be received for an already processed event. Because the event was processed in the previous NMI, this NMI gets dropped and becomes an 'unknown' NMI. This of course will cause printks that scare people. However, we prefer to have extra NMIs as opposed to losing NMIs and as such are have developed a basic mechanism to catch most of them. That will be a later patch. To accomplish this idea, I unhooked the nmi handlers from the notifier routines and created a new mechanism loosely based on doIRQ. The reason for this is the notifier routines have a couple of shortcomings. One we could't guarantee all future NMI handlers used NOTIFY_OK instead of NOTIFY_STOP. Second, we couldn't keep track of the number of events being handled in each routine (most only handle one, perf can handle more than one). Third, I wanted to eventually display which nmi handlers are registered in the system in /proc/interrupts to help see who is generating NMIs. The patch below just implements the new infrastructure but doesn't wire it up yet (that is the next patch). Its design is based on doIRQ structs and the atomic notifier routines. So the rcu stuff in the patch isn't entirely untested (as the notifier routines have soaked it) but it should be double checked in case I copied the code wrong. Signed-off-by: Don Zickus <dzickus@redhat.com> Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl> Link: http://lkml.kernel.org/r/1317409584-23662-3-git-send-email-dzickus@redhat.com Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/include/asm/nmi.h18
-rw-r--r--arch/x86/kernel/nmi.c153
2 files changed, 171 insertions, 0 deletions
diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h
index 4886a68f267e..480b69b75756 100644
--- a/arch/x86/include/asm/nmi.h
+++ b/arch/x86/include/asm/nmi.h
@@ -42,6 +42,24 @@ void arch_trigger_all_cpu_backtrace(void);
42#define NMI_LOCAL_NORMAL_PRIOR (NMI_LOCAL_BIT | NMI_NORMAL_PRIOR) 42#define NMI_LOCAL_NORMAL_PRIOR (NMI_LOCAL_BIT | NMI_NORMAL_PRIOR)
43#define NMI_LOCAL_LOW_PRIOR (NMI_LOCAL_BIT | NMI_LOW_PRIOR) 43#define NMI_LOCAL_LOW_PRIOR (NMI_LOCAL_BIT | NMI_LOW_PRIOR)
44 44
45#define NMI_FLAG_FIRST 1
46
47enum {
48 NMI_LOCAL=0,
49 NMI_UNKNOWN,
50 NMI_MAX
51};
52
53#define NMI_DONE 0
54#define NMI_HANDLED 1
55
56typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *);
57
58int register_nmi_handler(unsigned int, nmi_handler_t, unsigned long,
59 const char *);
60
61void unregister_nmi_handler(unsigned int, const char *);
62
45void stop_nmi(void); 63void stop_nmi(void);
46void restart_nmi(void); 64void restart_nmi(void);
47 65
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index 68d758aca8cd..327748d4f6b0 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -13,6 +13,9 @@
13#include <linux/kprobes.h> 13#include <linux/kprobes.h>
14#include <linux/kdebug.h> 14#include <linux/kdebug.h>
15#include <linux/nmi.h> 15#include <linux/nmi.h>
16#include <linux/delay.h>
17#include <linux/hardirq.h>
18#include <linux/slab.h>
16 19
17#if defined(CONFIG_EDAC) 20#if defined(CONFIG_EDAC)
18#include <linux/edac.h> 21#include <linux/edac.h>
@@ -21,6 +24,33 @@
21#include <linux/atomic.h> 24#include <linux/atomic.h>
22#include <asm/traps.h> 25#include <asm/traps.h>
23#include <asm/mach_traps.h> 26#include <asm/mach_traps.h>
27#include <asm/nmi.h>
28
29#define NMI_MAX_NAMELEN 16
30struct nmiaction {
31 struct list_head list;
32 nmi_handler_t handler;
33 unsigned int flags;
34 char *name;
35};
36
37struct nmi_desc {
38 spinlock_t lock;
39 struct list_head head;
40};
41
42static struct nmi_desc nmi_desc[NMI_MAX] =
43{
44 {
45 .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[0].lock),
46 .head = LIST_HEAD_INIT(nmi_desc[0].head),
47 },
48 {
49 .lock = __SPIN_LOCK_UNLOCKED(&nmi_desc[1].lock),
50 .head = LIST_HEAD_INIT(nmi_desc[1].head),
51 },
52
53};
24 54
25static int ignore_nmis; 55static int ignore_nmis;
26 56
@@ -38,6 +68,129 @@ static int __init setup_unknown_nmi_panic(char *str)
38} 68}
39__setup("unknown_nmi_panic", setup_unknown_nmi_panic); 69__setup("unknown_nmi_panic", setup_unknown_nmi_panic);
40 70
71#define nmi_to_desc(type) (&nmi_desc[type])
72
73static int notrace __kprobes nmi_handle(unsigned int type, struct pt_regs *regs)
74{
75 struct nmi_desc *desc = nmi_to_desc(type);
76 struct nmiaction *a;
77 int handled=0;
78
79 rcu_read_lock();
80
81 /*
82 * NMIs are edge-triggered, which means if you have enough
83 * of them concurrently, you can lose some because only one
84 * can be latched at any given time. Walk the whole list
85 * to handle those situations.
86 */
87 list_for_each_entry_rcu(a, &desc->head, list) {
88
89 handled += a->handler(type, regs);
90
91 }
92
93 rcu_read_unlock();
94
95 /* return total number of NMI events handled */
96 return handled;
97}
98
99static int __setup_nmi(unsigned int type, struct nmiaction *action)
100{
101 struct nmi_desc *desc = nmi_to_desc(type);
102 unsigned long flags;
103
104 spin_lock_irqsave(&desc->lock, flags);
105
106 /*
107 * some handlers need to be executed first otherwise a fake
108 * event confuses some handlers (kdump uses this flag)
109 */
110 if (action->flags & NMI_FLAG_FIRST)
111 list_add_rcu(&action->list, &desc->head);
112 else
113 list_add_tail_rcu(&action->list, &desc->head);
114
115 spin_unlock_irqrestore(&desc->lock, flags);
116 return 0;
117}
118
119static struct nmiaction *__free_nmi(unsigned int type, const char *name)
120{
121 struct nmi_desc *desc = nmi_to_desc(type);
122 struct nmiaction *n;
123 unsigned long flags;
124
125 spin_lock_irqsave(&desc->lock, flags);
126
127 list_for_each_entry_rcu(n, &desc->head, list) {
128 /*
129 * the name passed in to describe the nmi handler
130 * is used as the lookup key
131 */
132 if (!strcmp(n->name, name)) {
133 WARN(in_nmi(),
134 "Trying to free NMI (%s) from NMI context!\n", n->name);
135 list_del_rcu(&n->list);
136 break;
137 }
138 }
139
140 spin_unlock_irqrestore(&desc->lock, flags);
141 synchronize_rcu();
142 return (n);
143}
144
145int register_nmi_handler(unsigned int type, nmi_handler_t handler,
146 unsigned long nmiflags, const char *devname)
147{
148 struct nmiaction *action;
149 int retval = -ENOMEM;
150
151 if (!handler)
152 return -EINVAL;
153
154 action = kzalloc(sizeof(struct nmiaction), GFP_KERNEL);
155 if (!action)
156 goto fail_action;
157
158 action->handler = handler;
159 action->flags = nmiflags;
160 action->name = kstrndup(devname, NMI_MAX_NAMELEN, GFP_KERNEL);
161 if (!action->name)
162 goto fail_action_name;
163
164 retval = __setup_nmi(type, action);
165
166 if (retval)
167 goto fail_setup_nmi;
168
169 return retval;
170
171fail_setup_nmi:
172 kfree(action->name);
173fail_action_name:
174 kfree(action);
175fail_action:
176
177 return retval;
178}
179EXPORT_SYMBOL_GPL(register_nmi_handler);
180
181void unregister_nmi_handler(unsigned int type, const char *name)
182{
183 struct nmiaction *a;
184
185 a = __free_nmi(type, name);
186 if (a) {
187 kfree(a->name);
188 kfree(a);
189 }
190}
191
192EXPORT_SYMBOL_GPL(unregister_nmi_handler);
193
41static notrace __kprobes void 194static notrace __kprobes void
42pci_serr_error(unsigned char reason, struct pt_regs *regs) 195pci_serr_error(unsigned char reason, struct pt_regs *regs)
43{ 196{