diff options
Diffstat (limited to 'arch/x86/kernel/nmi.c')
-rw-r--r-- | arch/x86/kernel/nmi.c | 36 |
1 files changed, 34 insertions, 2 deletions
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index 60308053fdb2..0920212e6159 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/kprobes.h> | 14 | #include <linux/kprobes.h> |
15 | #include <linux/kdebug.h> | 15 | #include <linux/kdebug.h> |
16 | #include <linux/nmi.h> | 16 | #include <linux/nmi.h> |
17 | #include <linux/debugfs.h> | ||
17 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
18 | #include <linux/hardirq.h> | 19 | #include <linux/hardirq.h> |
19 | #include <linux/slab.h> | 20 | #include <linux/slab.h> |
@@ -29,6 +30,9 @@ | |||
29 | #include <asm/nmi.h> | 30 | #include <asm/nmi.h> |
30 | #include <asm/x86_init.h> | 31 | #include <asm/x86_init.h> |
31 | 32 | ||
33 | #define CREATE_TRACE_POINTS | ||
34 | #include <trace/events/nmi.h> | ||
35 | |||
32 | struct nmi_desc { | 36 | struct nmi_desc { |
33 | spinlock_t lock; | 37 | spinlock_t lock; |
34 | struct list_head head; | 38 | struct list_head head; |
@@ -82,6 +86,15 @@ __setup("unknown_nmi_panic", setup_unknown_nmi_panic); | |||
82 | 86 | ||
83 | #define nmi_to_desc(type) (&nmi_desc[type]) | 87 | #define nmi_to_desc(type) (&nmi_desc[type]) |
84 | 88 | ||
89 | static u64 nmi_longest_ns = 1 * NSEC_PER_MSEC; | ||
90 | static int __init nmi_warning_debugfs(void) | ||
91 | { | ||
92 | debugfs_create_u64("nmi_longest_ns", 0644, | ||
93 | arch_debugfs_dir, &nmi_longest_ns); | ||
94 | return 0; | ||
95 | } | ||
96 | fs_initcall(nmi_warning_debugfs); | ||
97 | |||
85 | static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b) | 98 | static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b) |
86 | { | 99 | { |
87 | struct nmi_desc *desc = nmi_to_desc(type); | 100 | struct nmi_desc *desc = nmi_to_desc(type); |
@@ -96,8 +109,27 @@ static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2 | |||
96 | * can be latched at any given time. Walk the whole list | 109 | * can be latched at any given time. Walk the whole list |
97 | * to handle those situations. | 110 | * to handle those situations. |
98 | */ | 111 | */ |
99 | list_for_each_entry_rcu(a, &desc->head, list) | 112 | list_for_each_entry_rcu(a, &desc->head, list) { |
100 | handled += a->handler(type, regs); | 113 | u64 before, delta, whole_msecs; |
114 | int decimal_msecs, thishandled; | ||
115 | |||
116 | before = local_clock(); | ||
117 | thishandled = a->handler(type, regs); | ||
118 | handled += thishandled; | ||
119 | delta = local_clock() - before; | ||
120 | trace_nmi_handler(a->handler, (int)delta, thishandled); | ||
121 | |||
122 | if (delta < nmi_longest_ns) | ||
123 | continue; | ||
124 | |||
125 | nmi_longest_ns = delta; | ||
126 | whole_msecs = do_div(delta, (1000 * 1000)); | ||
127 | decimal_msecs = do_div(delta, 1000) % 1000; | ||
128 | printk_ratelimited(KERN_INFO | ||
129 | "INFO: NMI handler (%ps) took too long to run: " | ||
130 | "%lld.%03d msecs\n", a->handler, whole_msecs, | ||
131 | decimal_msecs); | ||
132 | } | ||
101 | 133 | ||
102 | rcu_read_unlock(); | 134 | rcu_read_unlock(); |
103 | 135 | ||