diff options
Diffstat (limited to 'kernel/printk.c')
-rw-r--r-- | kernel/printk.c | 102 |
1 files changed, 88 insertions, 14 deletions
diff --git a/kernel/printk.c b/kernel/printk.c index 7982a0a841ea..9eabbbbae463 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -21,6 +21,7 @@ #include <linux/tty.h> #include <linux/tty_driver.h> #include <linux/console.h> +#include <linux/sysrq.h> #include <linux/init.h> #include <linux/jiffies.h> #include <linux/nmi.h> @@ -44,13 +45,6 @@ #include <asm/uaccess.h> -/* - * Architectures can override it: - */ -void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...) -{ -} - #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) /* printk's without a loglevel use this.. */ @@ -511,6 +505,7 @@ static void __call_console_drivers(unsigned start, unsigned end) { struct console *con; + migrate_disable(); for_each_console(con) { if (exclusive_console && con != exclusive_console) continue; @@ -519,7 +514,61 @@ static void __call_console_drivers(unsigned start, unsigned end) (con->flags & CON_ANYTIME))) con->write(con, &LOG_BUF(start), end - start); } + migrate_enable(); +} + +#ifdef CONFIG_EARLY_PRINTK +struct console *early_console; + +static void early_vprintk(const char *fmt, va_list ap) +{ + char buf[512]; + int n = vscnprintf(buf, sizeof(buf), fmt, ap); + if (early_console) + early_console->write(early_console, buf, n); +} + +asmlinkage void early_printk(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + early_vprintk(fmt, ap); + va_end(ap); +} + +/* + * This is independent of any log levels - a global + * kill switch that turns off all of printk. + * + * Used by the NMI watchdog if early-printk is enabled. + */ +static int __read_mostly printk_killswitch; + +static int __init force_early_printk_setup(char *str) +{ + printk_killswitch = 1; + return 0; +} +early_param("force_early_printk", force_early_printk_setup); + +void printk_kill(void) +{ + printk_killswitch = 1; +} + +static int forced_early_printk(const char *fmt, va_list ap) +{ + if (!printk_killswitch) + return 0; + early_vprintk(fmt, ap); + return 1; +} +#else +static inline int forced_early_printk(const char *fmt, va_list ap) +{ + return 0; } +#endif static int __read_mostly ignore_loglevel; @@ -781,12 +830,18 @@ static inline int can_use_console(unsigned int cpu) * interrupts disabled. It should return with 'lockbuf_lock' * released but interrupts still disabled. */ -static int console_trylock_for_printk(unsigned int cpu) +static int console_trylock_for_printk(unsigned int cpu, unsigned long flags) __releases(&logbuf_lock) { int retval = 0, wake = 0; +#ifdef CONFIG_PREEMPT_RT_FULL + int lock = (!early_boot_irqs_disabled && !irqs_disabled_flags(flags) && + !preempt_count()) || sysrq_in_progress; +#else + int lock = 1; +#endif - if (console_trylock()) { + if (lock && console_trylock()) { retval = 1; /* @@ -837,10 +892,17 @@ asmlinkage int vprintk(const char *fmt, va_list args) size_t plen; char special; + /* + * Fall back to early_printk if a debugging subsystem has + * killed printk output + */ + if (unlikely(forced_early_printk(fmt, args))) + return 1; + boot_delay_msec(); printk_delay(); - preempt_disable(); + migrate_disable(); /* This stops the holder of console_sem just where we want him */ raw_local_irq_save(flags); this_cpu = smp_processor_id(); @@ -957,14 +1019,21 @@ asmlinkage int vprintk(const char *fmt, va_list args) * will release 'logbuf_lock' regardless of whether it * actually gets the semaphore or not. */ - if (console_trylock_for_printk(this_cpu)) + if (console_trylock_for_printk(this_cpu, flags)) { +#ifndef CONFIG_PREEMPT_RT_FULL console_unlock(); +#else + raw_local_irq_restore(flags); + console_unlock(); + raw_local_irq_save(flags); +#endif + } lockdep_on(); out_restore_irqs: raw_local_irq_restore(flags); - preempt_enable(); + migrate_enable(); return printed_len; } EXPORT_SYMBOL(printk); @@ -1221,8 +1290,8 @@ void printk_tick(void) int printk_needs_cpu(int cpu) { - if (cpu_is_offline(cpu)) - printk_tick(); + if (unlikely(cpu_is_offline(cpu))) + __this_cpu_write(printk_pending, 0); return __this_cpu_read(printk_pending); } @@ -1268,11 +1337,16 @@ again: _con_start = con_start; _log_end = log_end; con_start = log_end; /* Flush */ +#ifndef CONFIG_PREEMPT_RT_FULL raw_spin_unlock(&logbuf_lock); stop_critical_timings(); /* don't trace print latency */ call_console_drivers(_con_start, _log_end); start_critical_timings(); local_irq_restore(flags); +#else + raw_spin_unlock_irqrestore(&logbuf_lock, flags); + call_console_drivers(_con_start, _log_end); +#endif } console_locked = 0; |