Skip to content

Commit 42a0bb3

Browse files
pmladektorvalds
authored andcommitted
printk/nmi: generic solution for safe printk in NMI
printk() takes some locks and could not be used a safe way in NMI context. The chance of a deadlock is real especially when printing stacks from all CPUs. This particular problem has been addressed on x86 by the commit a9edc88 ("x86/nmi: Perform a safe NMI stack trace on all CPUs"). The patchset brings two big advantages. First, it makes the NMI backtraces safe on all architectures for free. Second, it makes all NMI messages almost safe on all architectures (the temporary buffer is limited. We still should keep the number of messages in NMI context at minimum). Note that there already are several messages printed in NMI context: WARN_ON(in_nmi()), BUG_ON(in_nmi()), anything being printed out from MCE handlers. These are not easy to avoid. This patch reuses most of the code and makes it generic. It is useful for all messages and architectures that support NMI. The alternative printk_func is set when entering and is reseted when leaving NMI context. It queues IRQ work to copy the messages into the main ring buffer in a safe context. __printk_nmi_flush() copies all available messages and reset the buffer. Then we could use a simple cmpxchg operations to get synchronized with writers. There is also used a spinlock to get synchronized with other flushers. We do not longer use seq_buf because it depends on external lock. It would be hard to make all supported operations safe for a lockless use. It would be confusing and error prone to make only some operations safe. The code is put into separate printk/nmi.c as suggested by Steven Rostedt. It needs a per-CPU buffer and is compiled only on architectures that call nmi_enter(). This is achieved by the new HAVE_NMI Kconfig flag. The are MN10300 and Xtensa architectures. We need to clean up NMI handling there first. Let's do it separately. The patch is heavily based on the draft from Peter Zijlstra, see https://lkml.org/lkml/2015/6/10/327 [[email protected]: printk-nmi: use %zu format string for size_t] [[email protected]: min_t->min - all types are size_t here] Signed-off-by: Petr Mladek <[email protected]> Suggested-by: Peter Zijlstra <[email protected]> Suggested-by: Steven Rostedt <[email protected]> Cc: Jan Kara <[email protected]> Acked-by: Russell King <[email protected]> [arm part] Cc: Daniel Thompson <[email protected]> Cc: Jiri Kosina <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: Ralf Baechle <[email protected]> Cc: Benjamin Herrenschmidt <[email protected]> Cc: Martin Schwidefsky <[email protected]> Cc: David Miller <[email protected]> Cc: Daniel Thompson <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 2eeed7e commit 42a0bb3

File tree

24 files changed

+306
-107
lines changed

24 files changed

+306
-107
lines changed

arch/Kconfig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,11 @@ config HAVE_OPTPROBES
187187
config HAVE_KPROBES_ON_FTRACE
188188
bool
189189

190+
config HAVE_NMI
191+
bool
192+
190193
config HAVE_NMI_WATCHDOG
194+
depends on HAVE_NMI
191195
bool
192196
#
193197
# An arch should select this if it provides all these things:

arch/arm/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ config ARM
6767
select HAVE_KRETPROBES if (HAVE_KPROBES)
6868
select HAVE_MEMBLOCK
6969
select HAVE_MOD_ARCH_SPECIFIC
70+
select HAVE_NMI
7071
select HAVE_OPROFILE if (HAVE_PERF_EVENTS)
7172
select HAVE_OPTPROBES if !THUMB2_KERNEL
7273
select HAVE_PERF_EVENTS

arch/arm/kernel/smp.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,9 +644,11 @@ void handle_IPI(int ipinr, struct pt_regs *regs)
644644
break;
645645

646646
case IPI_CPU_BACKTRACE:
647+
printk_nmi_enter();
647648
irq_enter();
648649
nmi_cpu_backtrace(regs);
649650
irq_exit();
651+
printk_nmi_exit();
650652
break;
651653

652654
default:

arch/avr32/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ config AVR32
1818
select GENERIC_CLOCKEVENTS
1919
select HAVE_MOD_ARCH_SPECIFIC
2020
select MODULES_USE_ELF_RELA
21+
select HAVE_NMI
2122
help
2223
AVR32 is a high-performance 32-bit RISC microprocessor core,
2324
designed for cost-sensitive embedded applications, with particular

arch/blackfin/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ config BLACKFIN
4040
select HAVE_MOD_ARCH_SPECIFIC
4141
select MODULES_USE_ELF_RELA
4242
select HAVE_DEBUG_STACKOVERFLOW
43+
select HAVE_NMI
4344

4445
config GENERIC_CSUM
4546
def_bool y

arch/cris/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ config CRIS
7070
select GENERIC_CLOCKEVENTS if ETRAX_ARCH_V32
7171
select GENERIC_SCHED_CLOCK if ETRAX_ARCH_V32
7272
select HAVE_DEBUG_BUGVERBOSE if ETRAX_ARCH_V32
73+
select HAVE_NMI
7374

7475
config HZ
7576
int

arch/mips/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ config MIPS
4848
select GENERIC_SCHED_CLOCK if !CAVIUM_OCTEON_SOC
4949
select GENERIC_CMOS_UPDATE
5050
select HAVE_MOD_ARCH_SPECIFIC
51+
select HAVE_NMI
5152
select VIRT_TO_BUS
5253
select MODULES_USE_ELF_REL if MODULES
5354
select MODULES_USE_ELF_RELA if MODULES && 64BIT

arch/powerpc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ config PPC
155155
select NO_BOOTMEM
156156
select HAVE_GENERIC_RCU_GUP
157157
select HAVE_PERF_EVENTS_NMI if PPC64
158+
select HAVE_NMI if PERF_EVENTS
158159
select EDAC_SUPPORT
159160
select EDAC_ATOMIC_SCRUB
160161
select ARCH_HAS_DMA_SET_COHERENT_MASK

arch/s390/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ config S390
166166
select TTY
167167
select VIRT_CPU_ACCOUNTING
168168
select VIRT_TO_BUS
169+
select HAVE_NMI
169170

170171

171172
config SCHED_OMIT_FRAME_POINTER

arch/sh/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ config SUPERH
4444
select OLD_SIGSUSPEND
4545
select OLD_SIGACTION
4646
select HAVE_ARCH_AUDITSYSCALL
47+
select HAVE_NMI
4748
help
4849
The SuperH is a RISC processor targeted for use in embedded systems
4950
and consumer electronics; it was also used in the Sega Dreamcast

arch/sparc/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ config SPARC64
7979
select NO_BOOTMEM
8080
select HAVE_ARCH_AUDITSYSCALL
8181
select ARCH_SUPPORTS_ATOMIC_RMW
82+
select HAVE_NMI
8283

8384
config ARCH_DEFCONFIG
8485
string

arch/tile/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ config TILE
3030
select HAVE_DEBUG_STACKOVERFLOW
3131
select ARCH_WANT_FRAME_POINTERS
3232
select HAVE_CONTEXT_TRACKING
33+
select HAVE_NMI if USE_PMC
3334
select EDAC_SUPPORT
3435
select GENERIC_STRNCPY_FROM_USER
3536
select GENERIC_STRNLEN_USER

arch/x86/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ config X86
131131
select HAVE_MEMBLOCK
132132
select HAVE_MEMBLOCK_NODE_MAP
133133
select HAVE_MIXED_BREAKPOINTS_REGS
134+
select HAVE_NMI
134135
select HAVE_OPROFILE
135136
select HAVE_OPTPROBES
136137
select HAVE_PCSPKR_PLATFORM

arch/x86/kernel/apic/hw_nmi.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#include <linux/nmi.h>
1919
#include <linux/module.h>
2020
#include <linux/delay.h>
21-
#include <linux/seq_buf.h>
2221

2322
#ifdef CONFIG_HARDLOCKUP_DETECTOR
2423
u64 hw_nmi_get_sample_period(int watchdog_thresh)

include/linux/hardirq.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ extern void irq_exit(void);
6161

6262
#define nmi_enter() \
6363
do { \
64+
printk_nmi_enter(); \
6465
lockdep_off(); \
6566
ftrace_nmi_enter(); \
6667
BUG_ON(in_nmi()); \
@@ -77,6 +78,7 @@ extern void irq_exit(void);
7778
preempt_count_sub(NMI_OFFSET + HARDIRQ_OFFSET); \
7879
ftrace_nmi_exit(); \
7980
lockdep_on(); \
81+
printk_nmi_exit(); \
8082
} while (0)
8183

8284
#endif /* LINUX_HARDIRQ_H */

include/linux/percpu.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,4 @@ extern phys_addr_t per_cpu_ptr_to_phys(void *addr);
129129
(typeof(type) __percpu *)__alloc_percpu(sizeof(type), \
130130
__alignof__(type))
131131

132-
/* To avoid include hell, as printk can not declare this, we declare it here */
133-
DECLARE_PER_CPU(printk_func_t, printk_func);
134-
135132
#endif /* __LINUX_PERCPU_H */

include/linux/printk.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,17 @@ static inline __printf(1, 2) __cold
122122
void early_printk(const char *s, ...) { }
123123
#endif
124124

125-
typedef __printf(1, 0) int (*printk_func_t)(const char *fmt, va_list args);
125+
#ifdef CONFIG_PRINTK_NMI
126+
extern void printk_nmi_init(void);
127+
extern void printk_nmi_enter(void);
128+
extern void printk_nmi_exit(void);
129+
extern void printk_nmi_flush(void);
130+
#else
131+
static inline void printk_nmi_init(void) { }
132+
static inline void printk_nmi_enter(void) { }
133+
static inline void printk_nmi_exit(void) { }
134+
static inline void printk_nmi_flush(void) { }
135+
#endif /* PRINTK_NMI */
126136

127137
#ifdef CONFIG_PRINTK
128138
asmlinkage __printf(5, 0)

init/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,6 +1454,11 @@ config PRINTK
14541454
very difficult to diagnose system problems, saying N here is
14551455
strongly discouraged.
14561456

1457+
config PRINTK_NMI
1458+
def_bool y
1459+
depends on PRINTK
1460+
depends on HAVE_NMI
1461+
14571462
config BUG
14581463
bool "BUG() support" if EXPERT
14591464
default y

init/main.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ asmlinkage __visible void __init start_kernel(void)
569569
timekeeping_init();
570570
time_init();
571571
sched_clock_postinit();
572+
printk_nmi_init();
572573
perf_event_init();
573574
profile_init();
574575
call_function_init();

kernel/printk/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
obj-y = printk.o
2+
obj-$(CONFIG_PRINTK_NMI) += nmi.o
23
obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o

kernel/printk/internal.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* internal.h - printk internal definitions
3+
*
4+
* This program is free software; you can redistribute it and/or
5+
* modify it under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation; either version 2
7+
* of the License, or (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
#include <linux/percpu.h>
18+
19+
typedef __printf(1, 0) int (*printk_func_t)(const char *fmt, va_list args);
20+
21+
int __printf(1, 0) vprintk_default(const char *fmt, va_list args);
22+
23+
#ifdef CONFIG_PRINTK_NMI
24+
25+
/*
26+
* printk() could not take logbuf_lock in NMI context. Instead,
27+
* it temporary stores the strings into a per-CPU buffer.
28+
* The alternative implementation is chosen transparently
29+
* via per-CPU variable.
30+
*/
31+
DECLARE_PER_CPU(printk_func_t, printk_func);
32+
static inline __printf(1, 0) int vprintk_func(const char *fmt, va_list args)
33+
{
34+
return this_cpu_read(printk_func)(fmt, args);
35+
}
36+
37+
#else /* CONFIG_PRINTK_NMI */
38+
39+
static inline __printf(1, 0) int vprintk_func(const char *fmt, va_list args)
40+
{
41+
return vprintk_default(fmt, args);
42+
}
43+
44+
#endif /* CONFIG_PRINTK_NMI */

0 commit comments

Comments
 (0)