Skip to content

Commit eed1fc8

Browse files
committed
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/pmladek/printk
Pull printk updates from Petr Mladek: - Store printk() messages into the main log buffer directly even in NMI when the lock is available. It is the best effort to print even large chunk of text. It is handy, for example, when all ftrace messages are printed during the system panic in NMI. - Add missing annotations to calm down compiler warnings * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/pmladek/printk: printk: add __printf attributes to internal functions printk: Use the main logbuf in NMI when logbuf_lock is available
2 parents 1996454 + a5707ee commit eed1fc8

File tree

4 files changed

+50
-14
lines changed

4 files changed

+50
-14
lines changed

kernel/printk/internal.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818

1919
#ifdef CONFIG_PRINTK
2020

21-
#define PRINTK_SAFE_CONTEXT_MASK 0x7fffffff
22-
#define PRINTK_NMI_CONTEXT_MASK 0x80000000
21+
#define PRINTK_SAFE_CONTEXT_MASK 0x3fffffff
22+
#define PRINTK_NMI_DEFERRED_CONTEXT_MASK 0x40000000
23+
#define PRINTK_NMI_CONTEXT_MASK 0x80000000
2324

2425
extern raw_spinlock_t logbuf_lock;
2526

2627
__printf(1, 0) int vprintk_default(const char *fmt, va_list args);
28+
__printf(1, 0) int vprintk_deferred(const char *fmt, va_list args);
2729
__printf(1, 0) int vprintk_func(const char *fmt, va_list args);
2830
void __printk_safe_enter(void);
2931
void __printk_safe_exit(void);

kernel/printk/printk.c

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2720,23 +2720,32 @@ void wake_up_klogd(void)
27202720
preempt_enable();
27212721
}
27222722

2723-
int printk_deferred(const char *fmt, ...)
2723+
int vprintk_deferred(const char *fmt, va_list args)
27242724
{
2725-
va_list args;
27262725
int r;
27272726

2728-
preempt_disable();
2729-
va_start(args, fmt);
27302727
r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
2731-
va_end(args);
27322728

2729+
preempt_disable();
27332730
__this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
27342731
irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
27352732
preempt_enable();
27362733

27372734
return r;
27382735
}
27392736

2737+
int printk_deferred(const char *fmt, ...)
2738+
{
2739+
va_list args;
2740+
int r;
2741+
2742+
va_start(args, fmt);
2743+
r = vprintk_deferred(fmt, args);
2744+
va_end(args);
2745+
2746+
return r;
2747+
}
2748+
27402749
/*
27412750
* printk rate limiting, lifted from the networking subsystem.
27422751
*

kernel/printk/printk_safe.c

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,8 @@ static void queue_flush_work(struct printk_safe_seq_buf *s)
8080
* happen, printk_safe_log_store() will notice the buffer->len mismatch
8181
* and repeat the write.
8282
*/
83-
static int printk_safe_log_store(struct printk_safe_seq_buf *s,
84-
const char *fmt, va_list args)
83+
static __printf(2, 0) int printk_safe_log_store(struct printk_safe_seq_buf *s,
84+
const char *fmt, va_list args)
8585
{
8686
int add;
8787
size_t len;
@@ -299,7 +299,7 @@ void printk_safe_flush_on_panic(void)
299299
* one writer running. But the buffer might get flushed from another
300300
* CPU, so we need to be careful.
301301
*/
302-
static int vprintk_nmi(const char *fmt, va_list args)
302+
static __printf(1, 0) int vprintk_nmi(const char *fmt, va_list args)
303303
{
304304
struct printk_safe_seq_buf *s = this_cpu_ptr(&nmi_print_seq);
305305

@@ -308,17 +308,29 @@ static int vprintk_nmi(const char *fmt, va_list args)
308308

309309
void printk_nmi_enter(void)
310310
{
311-
this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
311+
/*
312+
* The size of the extra per-CPU buffer is limited. Use it only when
313+
* the main one is locked. If this CPU is not in the safe context,
314+
* the lock must be taken on another CPU and we could wait for it.
315+
*/
316+
if ((this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK) &&
317+
raw_spin_is_locked(&logbuf_lock)) {
318+
this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
319+
} else {
320+
this_cpu_or(printk_context, PRINTK_NMI_DEFERRED_CONTEXT_MASK);
321+
}
312322
}
313323

314324
void printk_nmi_exit(void)
315325
{
316-
this_cpu_and(printk_context, ~PRINTK_NMI_CONTEXT_MASK);
326+
this_cpu_and(printk_context,
327+
~(PRINTK_NMI_CONTEXT_MASK |
328+
PRINTK_NMI_DEFERRED_CONTEXT_MASK));
317329
}
318330

319331
#else
320332

321-
static int vprintk_nmi(const char *fmt, va_list args)
333+
static __printf(1, 0) int vprintk_nmi(const char *fmt, va_list args)
322334
{
323335
return 0;
324336
}
@@ -330,7 +342,7 @@ static int vprintk_nmi(const char *fmt, va_list args)
330342
* into itself. It uses a per-CPU buffer to store the message, just like
331343
* NMI.
332344
*/
333-
static int vprintk_safe(const char *fmt, va_list args)
345+
static __printf(1, 0) int vprintk_safe(const char *fmt, va_list args)
334346
{
335347
struct printk_safe_seq_buf *s = this_cpu_ptr(&safe_print_seq);
336348

@@ -351,12 +363,22 @@ void __printk_safe_exit(void)
351363

352364
__printf(1, 0) int vprintk_func(const char *fmt, va_list args)
353365
{
366+
/* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */
354367
if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
355368
return vprintk_nmi(fmt, args);
356369

370+
/* Use extra buffer to prevent a recursion deadlock in safe mode. */
357371
if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
358372
return vprintk_safe(fmt, args);
359373

374+
/*
375+
* Use the main logbuf when logbuf_lock is available in NMI.
376+
* But avoid calling console drivers that might have their own locks.
377+
*/
378+
if (this_cpu_read(printk_context) & PRINTK_NMI_DEFERRED_CONTEXT_MASK)
379+
return vprintk_deferred(fmt, args);
380+
381+
/* No obstacles. */
360382
return vprintk_default(fmt, args);
361383
}
362384

lib/nmi_backtrace.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,11 @@ void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
8686

8787
bool nmi_cpu_backtrace(struct pt_regs *regs)
8888
{
89+
static arch_spinlock_t lock = __ARCH_SPIN_LOCK_UNLOCKED;
8990
int cpu = smp_processor_id();
9091

9192
if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) {
93+
arch_spin_lock(&lock);
9294
if (regs && cpu_in_idle(instruction_pointer(regs))) {
9395
pr_warn("NMI backtrace for cpu %d skipped: idling at pc %#lx\n",
9496
cpu, instruction_pointer(regs));
@@ -99,6 +101,7 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
99101
else
100102
dump_stack();
101103
}
104+
arch_spin_unlock(&lock);
102105
cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
103106
return true;
104107
}

0 commit comments

Comments
 (0)