Commit 9f68cb57 authored by Petr Mladek's avatar Petr Mladek

Merge branch 'for-4.19-nmi' into for-linus

parents 554ec508 03fc7f9c
...@@ -148,9 +148,13 @@ void early_printk(const char *s, ...) { } ...@@ -148,9 +148,13 @@ void early_printk(const char *s, ...) { }
#ifdef CONFIG_PRINTK_NMI #ifdef CONFIG_PRINTK_NMI
extern void printk_nmi_enter(void); extern void printk_nmi_enter(void);
extern void printk_nmi_exit(void); extern void printk_nmi_exit(void);
extern void printk_nmi_direct_enter(void);
extern void printk_nmi_direct_exit(void);
#else #else
static inline void printk_nmi_enter(void) { } static inline void printk_nmi_enter(void) { }
static inline void printk_nmi_exit(void) { } static inline void printk_nmi_exit(void) { }
static inline void printk_nmi_direct_enter(void) { }
static inline void printk_nmi_direct_exit(void) { }
#endif /* PRINTK_NMI */ #endif /* PRINTK_NMI */
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
......
...@@ -19,11 +19,16 @@ ...@@ -19,11 +19,16 @@
#ifdef CONFIG_PRINTK #ifdef CONFIG_PRINTK
#define PRINTK_SAFE_CONTEXT_MASK 0x3fffffff #define PRINTK_SAFE_CONTEXT_MASK 0x3fffffff
#define PRINTK_NMI_DEFERRED_CONTEXT_MASK 0x40000000 #define PRINTK_NMI_DIRECT_CONTEXT_MASK 0x40000000
#define PRINTK_NMI_CONTEXT_MASK 0x80000000 #define PRINTK_NMI_CONTEXT_MASK 0x80000000
extern raw_spinlock_t logbuf_lock; extern raw_spinlock_t logbuf_lock;
__printf(5, 0)
int vprintk_store(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args);
__printf(1, 0) int vprintk_default(const char *fmt, va_list args); __printf(1, 0) int vprintk_default(const char *fmt, va_list args);
__printf(1, 0) int vprintk_deferred(const char *fmt, va_list args); __printf(1, 0) int vprintk_deferred(const char *fmt, va_list args);
__printf(1, 0) int vprintk_func(const char *fmt, va_list args); __printf(1, 0) int vprintk_func(const char *fmt, va_list args);
...@@ -54,6 +59,8 @@ void __printk_safe_exit(void); ...@@ -54,6 +59,8 @@ void __printk_safe_exit(void);
local_irq_enable(); \ local_irq_enable(); \
} while (0) } while (0)
void defer_console_output(void);
#else #else
__printf(1, 0) int vprintk_func(const char *fmt, va_list args) { return 0; } __printf(1, 0) int vprintk_func(const char *fmt, va_list args) { return 0; }
......
...@@ -1829,7 +1829,8 @@ static size_t log_output(int facility, int level, enum log_flags lflags, const c ...@@ -1829,7 +1829,8 @@ static size_t log_output(int facility, int level, enum log_flags lflags, const c
return log_store(facility, level, lflags, 0, dict, dictlen, text, text_len); return log_store(facility, level, lflags, 0, dict, dictlen, text, text_len);
} }
asmlinkage int vprintk_emit(int facility, int level, /* Must be called under logbuf_lock. */
int vprintk_store(int facility, int level,
const char *dict, size_t dictlen, const char *dict, size_t dictlen,
const char *fmt, va_list args) const char *fmt, va_list args)
{ {
...@@ -1837,20 +1838,7 @@ asmlinkage int vprintk_emit(int facility, int level, ...@@ -1837,20 +1838,7 @@ asmlinkage int vprintk_emit(int facility, int level,
char *text = textbuf; char *text = textbuf;
size_t text_len; size_t text_len;
enum log_flags lflags = 0; enum log_flags lflags = 0;
unsigned long flags;
int printed_len;
bool in_sched = false;
if (level == LOGLEVEL_SCHED) {
level = LOGLEVEL_DEFAULT;
in_sched = true;
}
boot_delay_msec(level);
printk_delay();
/* This stops the holder of console_sem just where we want him */
logbuf_lock_irqsave(flags);
/* /*
* The printf needs to come first; we need the syslog * The printf needs to come first; we need the syslog
* prefix which might be passed-in as a parameter. * prefix which might be passed-in as a parameter.
...@@ -1894,8 +1882,29 @@ asmlinkage int vprintk_emit(int facility, int level, ...@@ -1894,8 +1882,29 @@ asmlinkage int vprintk_emit(int facility, int level,
if (suppress_message_printing(level)) if (suppress_message_printing(level))
lflags |= LOG_NOCONS; lflags |= LOG_NOCONS;
printed_len = log_output(facility, level, lflags, dict, dictlen, text, text_len); return log_output(facility, level, lflags,
dict, dictlen, text, text_len);
}
asmlinkage int vprintk_emit(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args)
{
int printed_len;
bool in_sched = false;
unsigned long flags;
if (level == LOGLEVEL_SCHED) {
level = LOGLEVEL_DEFAULT;
in_sched = true;
}
boot_delay_msec(level);
printk_delay();
/* This stops the holder of console_sem just where we want him */
logbuf_lock_irqsave(flags);
printed_len = vprintk_store(facility, level, dict, dictlen, fmt, args);
logbuf_unlock_irqrestore(flags); logbuf_unlock_irqrestore(flags);
/* If called from the scheduler, we can not call up(). */ /* If called from the scheduler, we can not call up(). */
...@@ -2884,16 +2893,20 @@ void wake_up_klogd(void) ...@@ -2884,16 +2893,20 @@ void wake_up_klogd(void)
preempt_enable(); preempt_enable();
} }
int vprintk_deferred(const char *fmt, va_list args) void defer_console_output(void)
{ {
int r;
r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
preempt_disable(); preempt_disable();
__this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT); __this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
irq_work_queue(this_cpu_ptr(&wake_up_klogd_work)); irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
preempt_enable(); preempt_enable();
}
int vprintk_deferred(const char *fmt, va_list args)
{
int r;
r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
defer_console_output();
return r; return r;
} }
......
...@@ -308,24 +308,33 @@ static __printf(1, 0) int vprintk_nmi(const char *fmt, va_list args) ...@@ -308,24 +308,33 @@ static __printf(1, 0) int vprintk_nmi(const char *fmt, va_list args)
void printk_nmi_enter(void) void printk_nmi_enter(void)
{ {
/*
* The size of the extra per-CPU buffer is limited. Use it only when
* the main one is locked. If this CPU is not in the safe context,
* the lock must be taken on another CPU and we could wait for it.
*/
if ((this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK) &&
raw_spin_is_locked(&logbuf_lock)) {
this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK); this_cpu_or(printk_context, PRINTK_NMI_CONTEXT_MASK);
} else {
this_cpu_or(printk_context, PRINTK_NMI_DEFERRED_CONTEXT_MASK);
}
} }
void printk_nmi_exit(void) void printk_nmi_exit(void)
{ {
this_cpu_and(printk_context, this_cpu_and(printk_context, ~PRINTK_NMI_CONTEXT_MASK);
~(PRINTK_NMI_CONTEXT_MASK | }
PRINTK_NMI_DEFERRED_CONTEXT_MASK));
/*
* Marks a code that might produce many messages in NMI context
* and the risk of losing them is more critical than eventual
* reordering.
*
* It has effect only when called in NMI context. Then printk()
* will try to store the messages into the main logbuf directly
* and use the per-CPU buffers only as a fallback when the lock
* is not available.
*/
void printk_nmi_direct_enter(void)
{
if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
this_cpu_or(printk_context, PRINTK_NMI_DIRECT_CONTEXT_MASK);
}
void printk_nmi_direct_exit(void)
{
this_cpu_and(printk_context, ~PRINTK_NMI_DIRECT_CONTEXT_MASK);
} }
#else #else
...@@ -363,6 +372,20 @@ void __printk_safe_exit(void) ...@@ -363,6 +372,20 @@ void __printk_safe_exit(void)
__printf(1, 0) int vprintk_func(const char *fmt, va_list args) __printf(1, 0) int vprintk_func(const char *fmt, va_list args)
{ {
/*
* Try to use the main logbuf even in NMI. But avoid calling console
* drivers that might have their own locks.
*/
if ((this_cpu_read(printk_context) & PRINTK_NMI_DIRECT_CONTEXT_MASK) &&
raw_spin_trylock(&logbuf_lock)) {
int len;
len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, 0, fmt, args);
raw_spin_unlock(&logbuf_lock);
defer_console_output();
return len;
}
/* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */ /* Use extra buffer in NMI when logbuf_lock is taken or in safe mode. */
if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK) if (this_cpu_read(printk_context) & PRINTK_NMI_CONTEXT_MASK)
return vprintk_nmi(fmt, args); return vprintk_nmi(fmt, args);
...@@ -371,13 +394,6 @@ __printf(1, 0) int vprintk_func(const char *fmt, va_list args) ...@@ -371,13 +394,6 @@ __printf(1, 0) int vprintk_func(const char *fmt, va_list args)
if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK) if (this_cpu_read(printk_context) & PRINTK_SAFE_CONTEXT_MASK)
return vprintk_safe(fmt, args); return vprintk_safe(fmt, args);
/*
* Use the main logbuf when logbuf_lock is available in NMI.
* But avoid calling console drivers that might have their own locks.
*/
if (this_cpu_read(printk_context) & PRINTK_NMI_DEFERRED_CONTEXT_MASK)
return vprintk_deferred(fmt, args);
/* No obstacles. */ /* No obstacles. */
return vprintk_default(fmt, args); return vprintk_default(fmt, args);
} }
......
...@@ -8265,6 +8265,7 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) ...@@ -8265,6 +8265,7 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
tracing_off(); tracing_off();
local_irq_save(flags); local_irq_save(flags);
printk_nmi_direct_enter();
/* Simulate the iterator */ /* Simulate the iterator */
trace_init_global_iter(&iter); trace_init_global_iter(&iter);
...@@ -8345,6 +8346,7 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) ...@@ -8345,6 +8346,7 @@ void ftrace_dump(enum ftrace_dump_mode oops_dump_mode)
atomic_dec(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled); atomic_dec(&per_cpu_ptr(iter.trace_buffer->data, cpu)->disabled);
} }
atomic_dec(&dump_running); atomic_dec(&dump_running);
printk_nmi_direct_exit();
local_irq_restore(flags); local_irq_restore(flags);
} }
EXPORT_SYMBOL_GPL(ftrace_dump); EXPORT_SYMBOL_GPL(ftrace_dump);
......
...@@ -87,11 +87,9 @@ void nmi_trigger_cpumask_backtrace(const cpumask_t *mask, ...@@ -87,11 +87,9 @@ void nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
bool nmi_cpu_backtrace(struct pt_regs *regs) bool nmi_cpu_backtrace(struct pt_regs *regs)
{ {
static arch_spinlock_t lock = __ARCH_SPIN_LOCK_UNLOCKED;
int cpu = smp_processor_id(); int cpu = smp_processor_id();
if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) { if (cpumask_test_cpu(cpu, to_cpumask(backtrace_mask))) {
arch_spin_lock(&lock);
if (regs && cpu_in_idle(instruction_pointer(regs))) { if (regs && cpu_in_idle(instruction_pointer(regs))) {
pr_warn("NMI backtrace for cpu %d skipped: idling at %pS\n", pr_warn("NMI backtrace for cpu %d skipped: idling at %pS\n",
cpu, (void *)instruction_pointer(regs)); cpu, (void *)instruction_pointer(regs));
...@@ -102,7 +100,6 @@ bool nmi_cpu_backtrace(struct pt_regs *regs) ...@@ -102,7 +100,6 @@ bool nmi_cpu_backtrace(struct pt_regs *regs)
else else
dump_stack(); dump_stack();
} }
arch_spin_unlock(&lock);
cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask)); cpumask_clear_cpu(cpu, to_cpumask(backtrace_mask));
return true; return true;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment