1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * printk_safe.c - Safe printk for printk-deadlock-prone contexts
4  */
5 
6 #include <linux/preempt.h>
7 #include <linux/kdb.h>
8 #include <linux/smp.h>
9 #include <linux/cpumask.h>
10 #include <linux/printk.h>
11 #include <linux/kprobes.h>
12 
13 #include "internal.h"
14 
15 static DEFINE_PER_CPU(int, printk_context);
16 
17 /* Can be preempted by NMI. */
__printk_safe_enter(void)18 void __printk_safe_enter(void)
19 {
20 	this_cpu_inc(printk_context);
21 }
22 
23 /* Can be preempted by NMI. */
__printk_safe_exit(void)24 void __printk_safe_exit(void)
25 {
26 	this_cpu_dec(printk_context);
27 }
28 
__printk_deferred_enter(void)29 void __printk_deferred_enter(void)
30 {
31 	cant_migrate();
32 	__printk_safe_enter();
33 }
34 
__printk_deferred_exit(void)35 void __printk_deferred_exit(void)
36 {
37 	cant_migrate();
38 	__printk_safe_exit();
39 }
40 
is_printk_legacy_deferred(void)41 bool is_printk_legacy_deferred(void)
42 {
43 	/*
44 	 * The per-CPU variable @printk_context can be read safely in any
45 	 * context. CPU migration is always disabled when set.
46 	 */
47 	return (force_legacy_kthread() ||
48 		this_cpu_read(printk_context) ||
49 		in_nmi());
50 }
51 
vprintk(const char * fmt,va_list args)52 asmlinkage int vprintk(const char *fmt, va_list args)
53 {
54 #ifdef CONFIG_KGDB_KDB
55 	/* Allow to pass printk() to kdb but avoid a recursion. */
56 	if (unlikely(kdb_trap_printk && kdb_printf_cpu < 0))
57 		return vkdb_printf(KDB_MSGSRC_PRINTK, fmt, args);
58 #endif
59 
60 	/*
61 	 * Use the main logbuf even in NMI. But avoid calling console
62 	 * drivers that might have their own locks.
63 	 */
64 	if (is_printk_legacy_deferred())
65 		return vprintk_deferred(fmt, args);
66 
67 	/* No obstacles. */
68 	return vprintk_default(fmt, args);
69 }
70 EXPORT_SYMBOL(vprintk);
71