[PATCH v6 1/2] printk: Factor out buffering and irq work queuing in printk_deferred

From: Byungchul Park
Date: Fri Mar 11 2016 - 05:37:25 EST


printk_deferred() is a function for seperating between buffering a
message and deferring the actual printing. This patch factors out these
since these are also useful to others, where printk() cannot perform
the actual printing at the moment when printk() is called.

For example, spin_dump() must not perform the actual printing if
console_sem's spinlock has already been held at the moment. In that
case, spinlock debug system must do printk() without the actual printing,
otherwise a deadlock occures.

Signed-off-by: Byungchul Park <byungchul.park@xxxxxxx>
---
include/linux/printk.h | 11 +++++++++++
kernel/printk/printk.c | 17 ++++++++++++++---
2 files changed, 25 insertions(+), 3 deletions(-)

diff --git a/include/linux/printk.h b/include/linux/printk.h
index 9729565..8a522d7 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -169,6 +169,8 @@ void __init setup_log_buf(int early);
__printf(1, 2) void dump_stack_set_arch_desc(const char *fmt, ...);
void dump_stack_print_info(const char *log_lvl);
void show_regs_print_info(const char *log_lvl);
+int vprintk_deferred(const char *fmt, va_list args);
+void printk_pending_output(void);
#else
static inline __printf(1, 0)
int vprintk(const char *s, va_list args)
@@ -228,6 +230,15 @@ static inline void dump_stack_print_info(const char *log_lvl)
static inline void show_regs_print_info(const char *log_lvl)
{
}
+
+static inline int vprintk_deferred(const char *fmt, va_list args)
+{
+ return 0;
+}
+
+static inline void printk_pending_output(void)
+{
+}
#endif

extern asmlinkage void dump_stack(void) __cold;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 2ce8826..e82fae4 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1867,6 +1867,11 @@ int vprintk_default(const char *fmt, va_list args)
}
EXPORT_SYMBOL_GPL(vprintk_default);

+int vprintk_deferred(const char *fmt, va_list args)
+{
+ return vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
+}
+
/*
* This allows printk to be diverted to another function per cpu.
* This is useful for calling printk functions from within NMI
@@ -2710,18 +2715,24 @@ void wake_up_klogd(void)
preempt_enable();
}

+void printk_pending_output(void)
+{
+ __this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
+ irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
+}
+
int printk_deferred(const char *fmt, ...)
{
va_list args;
int r;

preempt_disable();
+
va_start(args, fmt);
- r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
+ r = vprintk_deferred(fmt, args);
va_end(args);
+ printk_pending_output();

- __this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
- irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
preempt_enable();

return r;
--
1.9.1