aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Zijlstra <peterz@infradead.org>2014-02-03 12:02:09 -0500
committerIngo Molnar <mingo@kernel.org>2014-02-09 07:17:22 -0500
commite90c78535283dd0ded3bf2e484890d14dba2d527 (patch)
tree349fe1e8b7d7dc6ea7c78a0d1403b7ca74c2f387
parent6a02ad66b2c44155d529f430d4fa5c6c66321077 (diff)
x86/nmi: Push duration printk() to irq context
Calling printk() from NMI context is bad (TM), so move it to IRQ context. In doing so we slightly change (probably wreck) the debugfs nmi_longest_ns thingy, in that it doesn't update to reflect the longest, nor does writing to it reset the count. Signed-off-by: Peter Zijlstra <peterz@infradead.org> Cc: Don Zickus <dzickus@redhat.com> Cc: Dave Hansen <dave.hansen@linux.intel.com> Link: http://lkml.kernel.org/n/tip-rdw0au56a5ymis1u8p48c12d@git.kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/include/asm/nmi.h3
-rw-r--r--arch/x86/kernel/nmi.c37
2 files changed, 27 insertions, 13 deletions
diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h
index 86f9301903c8..5f2fc4441b11 100644
--- a/arch/x86/include/asm/nmi.h
+++ b/arch/x86/include/asm/nmi.h
@@ -1,6 +1,7 @@
1#ifndef _ASM_X86_NMI_H 1#ifndef _ASM_X86_NMI_H
2#define _ASM_X86_NMI_H 2#define _ASM_X86_NMI_H
3 3
4#include <linux/irq_work.h>
4#include <linux/pm.h> 5#include <linux/pm.h>
5#include <asm/irq.h> 6#include <asm/irq.h>
6#include <asm/io.h> 7#include <asm/io.h>
@@ -38,6 +39,8 @@ typedef int (*nmi_handler_t)(unsigned int, struct pt_regs *);
38struct nmiaction { 39struct nmiaction {
39 struct list_head list; 40 struct list_head list;
40 nmi_handler_t handler; 41 nmi_handler_t handler;
42 u64 max_duration;
43 struct irq_work irq_work;
41 unsigned long flags; 44 unsigned long flags;
42 const char *name; 45 const char *name;
43}; 46};
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c
index 6fcb49ce50a1..b4872b999a71 100644
--- a/arch/x86/kernel/nmi.c
+++ b/arch/x86/kernel/nmi.c
@@ -87,6 +87,7 @@ __setup("unknown_nmi_panic", setup_unknown_nmi_panic);
87#define nmi_to_desc(type) (&nmi_desc[type]) 87#define nmi_to_desc(type) (&nmi_desc[type])
88 88
89static u64 nmi_longest_ns = 1 * NSEC_PER_MSEC; 89static u64 nmi_longest_ns = 1 * NSEC_PER_MSEC;
90
90static int __init nmi_warning_debugfs(void) 91static int __init nmi_warning_debugfs(void)
91{ 92{
92 debugfs_create_u64("nmi_longest_ns", 0644, 93 debugfs_create_u64("nmi_longest_ns", 0644,
@@ -95,6 +96,20 @@ static int __init nmi_warning_debugfs(void)
95} 96}
96fs_initcall(nmi_warning_debugfs); 97fs_initcall(nmi_warning_debugfs);
97 98
99static void nmi_max_handler(struct irq_work *w)
100{
101 struct nmiaction *a = container_of(w, struct nmiaction, irq_work);
102 int remainder_ns, decimal_msecs;
103 u64 whole_msecs = ACCESS_ONCE(a->max_duration);
104
105 remainder_ns = do_div(whole_msecs, (1000 * 1000));
106 decimal_msecs = remainder_ns / 1000;
107
108 printk_ratelimited(KERN_INFO
109 "INFO: NMI handler (%ps) took too long to run: %lld.%03d msecs\n",
110 a->handler, whole_msecs, decimal_msecs);
111}
112
98static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b) 113static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b)
99{ 114{
100 struct nmi_desc *desc = nmi_to_desc(type); 115 struct nmi_desc *desc = nmi_to_desc(type);
@@ -110,26 +125,20 @@ static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2
110 * to handle those situations. 125 * to handle those situations.
111 */ 126 */
112 list_for_each_entry_rcu(a, &desc->head, list) { 127 list_for_each_entry_rcu(a, &desc->head, list) {
113 u64 before, delta, whole_msecs; 128 int thishandled;
114 int remainder_ns, decimal_msecs, thishandled; 129 u64 delta;
115 130
116 before = sched_clock(); 131 delta = sched_clock();
117 thishandled = a->handler(type, regs); 132 thishandled = a->handler(type, regs);
118 handled += thishandled; 133 handled += thishandled;
119 delta = sched_clock() - before; 134 delta = sched_clock() - delta;
120 trace_nmi_handler(a->handler, (int)delta, thishandled); 135 trace_nmi_handler(a->handler, (int)delta, thishandled);
121 136
122 if (delta < nmi_longest_ns) 137 if (delta < nmi_longest_ns || delta < a->max_duration)
123 continue; 138 continue;
124 139
125 nmi_longest_ns = delta; 140 a->max_duration = delta;
126 whole_msecs = delta; 141 irq_work_queue(&a->irq_work);
127 remainder_ns = do_div(whole_msecs, (1000 * 1000));
128 decimal_msecs = remainder_ns / 1000;
129 printk_ratelimited(KERN_INFO
130 "INFO: NMI handler (%ps) took too long to run: "
131 "%lld.%03d msecs\n", a->handler, whole_msecs,
132 decimal_msecs);
133 } 142 }
134 143
135 rcu_read_unlock(); 144 rcu_read_unlock();
@@ -146,6 +155,8 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action)
146 if (!action->handler) 155 if (!action->handler)
147 return -EINVAL; 156 return -EINVAL;
148 157
158 init_irq_work(&action->irq_work, nmi_max_handler);
159
149 spin_lock_irqsave(&desc->lock, flags); 160 spin_lock_irqsave(&desc->lock, flags);
150 161
151 /* 162 /*