| From f2c1f5c3d0134cb42b1d6ee61d2d5ed113e4033c Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sat, 11 Jan 2020 20:54:27 +0800 |
| Subject: x86/nmi: Remove irq_work from the long duration NMI handler |
| |
| From: Changbin Du <changbin.du@gmail.com> |
| |
| [ Upstream commit 248ed51048c40d36728e70914e38bffd7821da57 ] |
| |
| First, printk() is NMI-context safe now since the safe printk() has been |
| implemented and it already has an irq_work to make NMI-context safe. |
| |
| Second, this NMI irq_work actually does not work if a NMI handler causes |
| panic by watchdog timeout. It has no chance to run in such case, while |
| the safe printk() will flush its per-cpu buffers before panicking. |
| |
| While at it, repurpose the irq_work callback into a function which |
| concentrates the NMI duration checking and makes the code easier to |
| follow. |
| |
| [ bp: Massage. ] |
| |
| Signed-off-by: Changbin Du <changbin.du@gmail.com> |
| Signed-off-by: Borislav Petkov <bp@suse.de> |
| Acked-by: Thomas Gleixner <tglx@linutronix.de> |
| Link: https://lkml.kernel.org/r/20200111125427.15662-1-changbin.du@gmail.com |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| arch/x86/include/asm/nmi.h | 1 - |
| arch/x86/kernel/nmi.c | 20 +++++++++----------- |
| 2 files changed, 9 insertions(+), 12 deletions(-) |
| |
| diff --git a/arch/x86/include/asm/nmi.h b/arch/x86/include/asm/nmi.h |
| index 75ded1d13d98d..9d5d949e662e1 100644 |
| --- a/arch/x86/include/asm/nmi.h |
| +++ b/arch/x86/include/asm/nmi.h |
| @@ -41,7 +41,6 @@ struct nmiaction { |
| struct list_head list; |
| nmi_handler_t handler; |
| u64 max_duration; |
| - struct irq_work irq_work; |
| unsigned long flags; |
| const char *name; |
| }; |
| diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c |
| index e676a9916c498..54c21d6abd5ac 100644 |
| --- a/arch/x86/kernel/nmi.c |
| +++ b/arch/x86/kernel/nmi.c |
| @@ -104,18 +104,22 @@ static int __init nmi_warning_debugfs(void) |
| } |
| fs_initcall(nmi_warning_debugfs); |
| |
| -static void nmi_max_handler(struct irq_work *w) |
| +static void nmi_check_duration(struct nmiaction *action, u64 duration) |
| { |
| - struct nmiaction *a = container_of(w, struct nmiaction, irq_work); |
| + u64 whole_msecs = READ_ONCE(action->max_duration); |
| int remainder_ns, decimal_msecs; |
| - u64 whole_msecs = READ_ONCE(a->max_duration); |
| + |
| + if (duration < nmi_longest_ns || duration < action->max_duration) |
| + return; |
| + |
| + action->max_duration = 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); |
| + action->handler, whole_msecs, decimal_msecs); |
| } |
| |
| static int nmi_handle(unsigned int type, struct pt_regs *regs) |
| @@ -142,11 +146,7 @@ static int nmi_handle(unsigned int type, struct pt_regs *regs) |
| delta = sched_clock() - delta; |
| trace_nmi_handler(a->handler, (int)delta, thishandled); |
| |
| - if (delta < nmi_longest_ns || delta < a->max_duration) |
| - continue; |
| - |
| - a->max_duration = delta; |
| - irq_work_queue(&a->irq_work); |
| + nmi_check_duration(a, delta); |
| } |
| |
| rcu_read_unlock(); |
| @@ -164,8 +164,6 @@ int __register_nmi_handler(unsigned int type, struct nmiaction *action) |
| if (!action->handler) |
| return -EINVAL; |
| |
| - init_irq_work(&action->irq_work, nmi_max_handler); |
| - |
| raw_spin_lock_irqsave(&desc->lock, flags); |
| |
| /* |
| -- |
| 2.20.1 |
| |