[PATCH 1/2] printk: Add printk_deferred_{enter, exit}

From: Chengming Zhou
Date: Sun Sep 27 2020 - 12:11:43 EST


We use printk_deferred in scheduler code when rq lock held because calling
printk can cause deadlock. It's a nice solution but a little hard to use
when we want to mark the printk_deferred danger areas.
Currently, the use of WARN_ON or WARN_ON_ONCE in scheduler with rq lock
held can cause deadlock when warn condition met. If we can mark the
printk_deferred area when rq lock held using pintk_deferred_{enter, exit},
all console output in that area will be deferred.

Signed-off-by: Chengming Zhou <zhouchengming@xxxxxxxxxxxxx>
Signed-off-by: MuChun Song <songmuchun@xxxxxxxxxxxxx>
---
include/linux/printk.h | 8 ++++++++
kernel/printk/internal.h | 3 ++-
kernel/printk/printk_safe.c | 18 ++++++++++++++++++
3 files changed, 28 insertions(+), 1 deletion(-)

diff --git a/include/linux/printk.h b/include/linux/printk.h
index 34c1a7be3e01..96ab252f529a 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -147,6 +147,14 @@ static inline __printf(1, 2) __cold
void early_printk(const char *s, ...) { }
#endif

+#ifdef CONFIG_PRINTK
+extern void printk_deferred_enter(void);
+extern void printk_deferred_exit(void);
+#else
+static inline void printk_deferred_enter(void) { }
+static inline void printk_deferred_exit(void) { }
+#endif
+
#ifdef CONFIG_PRINTK_NMI
extern void printk_nmi_enter(void);
extern void printk_nmi_exit(void);
diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h
index 660f9a6bf73a..b2ede46b7460 100644
--- a/kernel/printk/internal.h
+++ b/kernel/printk/internal.h
@@ -6,7 +6,8 @@

#ifdef CONFIG_PRINTK

-#define PRINTK_SAFE_CONTEXT_MASK 0x007ffffff
+#define PRINTK_SAFE_CONTEXT_MASK 0x003ffffff
+#define PRINTK_DEFERRED_CONTEXT_MASK 0x004000000
#define PRINTK_NMI_DIRECT_CONTEXT_MASK 0x008000000
#define PRINTK_NMI_CONTEXT_MASK 0xff0000000

diff --git a/kernel/printk/printk_safe.c b/kernel/printk/printk_safe.c
index 50aeae770434..f77c6880f59f 100644
--- a/kernel/printk/printk_safe.c
+++ b/kernel/printk/printk_safe.c
@@ -335,6 +335,16 @@ static __printf(1, 0) int vprintk_nmi(const char *fmt, va_list args)

#endif /* CONFIG_PRINTK_NMI */

+void printk_deferred_enter(void)
+{
+ __this_cpu_or(printk_context, PRINTK_DEFERRED_CONTEXT_MASK);
+}
+
+void printk_deferred_exit(void)
+{
+ __this_cpu_and(printk_context, ~PRINTK_DEFERRED_CONTEXT_MASK);
+}
+
/*
* Lock-less printk(), to avoid deadlocks should the printk() recurse
* into itself. It uses a per-CPU buffer to store the message, just like
@@ -385,6 +395,14 @@ __printf(1, 0) int vprintk_func(const char *fmt, va_list args)
if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
return vprintk_nmi(fmt, args);

+ if (this_cpu_read(printk_context) & PRINTK_DEFERRED_CONTEXT_MASK) {
+ int len;
+
+ len = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
+ defer_console_output();
+ return len;
+ }
+
/* Use extra buffer to prevent a recursion deadlock in safe mode. */
if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
return vprintk_safe(fmt, args);
--
2.11.0