diff options
Diffstat (limited to 'arch/x86/kernel/nmi.c')
| -rw-r--r-- | arch/x86/kernel/nmi.c | 55 | 
1 files changed, 36 insertions, 19 deletions
diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index ba77ebc2c35..c3e985d1751 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);  #define nmi_to_desc(type) (&nmi_desc[type])  static u64 nmi_longest_ns = 1 * NSEC_PER_MSEC; +  static int __init nmi_warning_debugfs(void)  {  	debugfs_create_u64("nmi_longest_ns", 0644, @@ -95,7 +96,21 @@ static int __init nmi_warning_debugfs(void)  }  fs_initcall(nmi_warning_debugfs); -static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b) +static void nmi_max_handler(struct irq_work *w) +{ +	struct nmiaction *a = container_of(w, struct nmiaction, irq_work); +	int remainder_ns, decimal_msecs; +	u64 whole_msecs = ACCESS_ONCE(a->max_duration); + +	remainder_ns = do_div(whole_msecs, (1000 * 1000)); +	decimal_msecs = remainder_ns / 1000; + +	printk_ratelimited(KERN_INFO +		"INFO: NMI handler (%ps) took too long to run: %lld.%03d msecs\n", +		a->handler, whole_msecs, decimal_msecs); +} + +static int nmi_handle(unsigned int type, struct pt_regs *regs, bool b2b)  {  	struct nmi_desc *desc = nmi_to_desc(type);  	struct nmiaction *a; @@ -110,26 +125,20 @@ static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2  	 * to handle those situations.  	 */  	list_for_each_entry_rcu(a, &desc->head, list) { -		u64 before, delta, whole_msecs; -		int remainder_ns, decimal_msecs, thishandled; +		int thishandled; +		u64 delta; -		before = local_clock(); +		delta = sched_clock();  		thishandled = a->handler(type, regs);  		handled += thishandled; -		delta = local_clock() - before; +		delta = sched_clock() - delta;  		trace_nmi_handler(a->handler, (int)delta, thishandled); -		if (delta < nmi_longest_ns) +		if (delta < nmi_longest_ns || delta < a->max_duration)  			continue; -		nmi_longest_ns = delta; -		whole_msecs = delta; -		remainder_ns = do_div(whole_msecs, (1000 * 1000)); -		decimal_msecs = remainder_ns / 1000; -		printk_ratelimited(KERN_INFO -			"INFO: NMI handler (%ps) took too long to run: " -			"%lld.%03d msecs\n", a->handler, whole_msecs, -			decimal_msecs); +		a->max_duration = delta; +		irq_work_queue(&a->irq_work);  	}  	rcu_read_unlock(); @@ -137,6 +146,7 @@ static int __kprobes nmi_handle(unsigned int type, struct pt_regs *regs, bool b2  	/* return total number of NMI events handled */  	return handled;  } +NOKPROBE_SYMBOL(nmi_handle);  int __register_nmi_handler(unsigned int type, struct nmiaction *action)  { @@ -146,6 +156,8 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action)  	if (!action->handler)  		return -EINVAL; +	init_irq_work(&action->irq_work, nmi_max_handler); +  	spin_lock_irqsave(&desc->lock, flags);  	/* @@ -197,7 +209,7 @@ void unregister_nmi_handler(unsigned int type, const char *name)  }  EXPORT_SYMBOL_GPL(unregister_nmi_handler); -static __kprobes void +static void  pci_serr_error(unsigned char reason, struct pt_regs *regs)  {  	/* check to see if anyone registered against these types of errors */ @@ -227,8 +239,9 @@ pci_serr_error(unsigned char reason, struct pt_regs *regs)  	reason = (reason & NMI_REASON_CLEAR_MASK) | NMI_REASON_CLEAR_SERR;  	outb(reason, NMI_REASON_PORT);  } +NOKPROBE_SYMBOL(pci_serr_error); -static __kprobes void +static void  io_check_error(unsigned char reason, struct pt_regs *regs)  {  	unsigned long i; @@ -258,8 +271,9 @@ io_check_error(unsigned char reason, struct pt_regs *regs)  	reason &= ~NMI_REASON_CLEAR_IOCHK;  	outb(reason, NMI_REASON_PORT);  } +NOKPROBE_SYMBOL(io_check_error); -static __kprobes void +static void  unknown_nmi_error(unsigned char reason, struct pt_regs *regs)  {  	int handled; @@ -287,11 +301,12 @@ unknown_nmi_error(unsigned char reason, struct pt_regs *regs)  	pr_emerg("Dazed and confused, but trying to continue\n");  } +NOKPROBE_SYMBOL(unknown_nmi_error);  static DEFINE_PER_CPU(bool, swallow_nmi);  static DEFINE_PER_CPU(unsigned long, last_nmi_rip); -static __kprobes void default_do_nmi(struct pt_regs *regs) +static void default_do_nmi(struct pt_regs *regs)  {  	unsigned char reason = 0;  	int handled; @@ -390,6 +405,7 @@ static __kprobes void default_do_nmi(struct pt_regs *regs)  	else  		unknown_nmi_error(reason, regs);  } +NOKPROBE_SYMBOL(default_do_nmi);  /*   * NMIs can hit breakpoints which will cause it to lose its @@ -509,7 +525,7 @@ static inline void nmi_nesting_postprocess(void)  }  #endif -dotraplinkage notrace __kprobes void +dotraplinkage notrace void  do_nmi(struct pt_regs *regs, long error_code)  {  	nmi_nesting_preprocess(regs); @@ -526,6 +542,7 @@ do_nmi(struct pt_regs *regs, long error_code)  	/* On i386, may loop back to preprocess */  	nmi_nesting_postprocess();  } +NOKPROBE_SYMBOL(do_nmi);  void stop_nmi(void)  {  | 
