Merge branch 'rework/printk_safe-removal' into for-linus
authorPetr Mladek <pmladek@suse.com>
Mon, 30 Aug 2021 14:36:10 +0000 (16:36 +0200)
committerPetr Mladek <pmladek@suse.com>
Mon, 30 Aug 2021 14:36:10 +0000 (16:36 +0200)
1  2 
arch/powerpc/kernel/traps.c
arch/powerpc/kernel/watchdog.c
arch/powerpc/kexec/crash.c
include/linux/printk.h
init/Kconfig
kernel/kexec_core.c
kernel/panic.c
kernel/printk/internal.h
kernel/printk/printk.c
kernel/trace/trace.c

@@@ -67,7 -67,6 +67,7 @@@
  #include <asm/kprobes.h>
  #include <asm/stacktrace.h>
  #include <asm/nmi.h>
 +#include <asm/disassemble.h>
  
  #if defined(CONFIG_DEBUGGER) || defined(CONFIG_KEXEC_CORE)
  int (*__debugger)(struct pt_regs *regs) __read_mostly;
@@@ -171,7 -170,6 +171,6 @@@ extern void panic_flush_kmsg_start(void
  
  extern void panic_flush_kmsg_end(void)
  {
-       printk_safe_flush_on_panic();
        kmsg_dump(KMSG_DUMP_PANIC);
        bust_spinlocks(0);
        debug_locks_off();
@@@ -428,7 -426,7 +427,7 @@@ void hv_nmi_check_nonrecoverable(struc
        return;
  
  nonrecoverable:
 -      regs->msr &= ~MSR_RI;
 +      regs_set_return_msr(regs, regs->msr & ~MSR_RI);
  #endif
  }
  DEFINE_INTERRUPT_HANDLER_NMI(system_reset_exception)
@@@ -538,11 -536,11 +537,11 @@@ static inline int check_io_access(struc
                 * For the debug message, we look at the preceding
                 * load or store.
                 */
 -              if (*nip == PPC_INST_NOP)
 +              if (*nip == PPC_RAW_NOP())
                        nip -= 2;
 -              else if (*nip == PPC_INST_ISYNC)
 +              else if (*nip == PPC_RAW_ISYNC())
                        --nip;
 -              if (*nip == PPC_INST_SYNC || (*nip >> 26) == OP_TRAP) {
 +              if (*nip == PPC_RAW_SYNC() || get_op(*nip) == OP_TRAP) {
                        unsigned int rb;
  
                        --nip;
                        printk(KERN_DEBUG "%s bad port %lx at %p\n",
                               (*nip & 0x100)? "OUT to": "IN from",
                               regs->gpr[rb] - _IO_BASE, nip);
 -                      regs->msr |= MSR_RI;
 -                      regs->nip = extable_fixup(entry);
 +                      regs_set_return_msr(regs, regs->msr | MSR_RI);
 +                      regs_set_return_ip(regs, extable_fixup(entry));
                        return 1;
                }
        }
  #define REASON_BOUNDARY               SRR1_BOUNDARY
  
  #define single_stepping(regs) ((regs)->msr & MSR_SE)
 -#define clear_single_step(regs)       ((regs)->msr &= ~MSR_SE)
 -#define clear_br_trace(regs)  ((regs)->msr &= ~MSR_BE)
 +#define clear_single_step(regs)       (regs_set_return_msr((regs), (regs)->msr & ~MSR_SE))
 +#define clear_br_trace(regs)  (regs_set_return_msr((regs), (regs)->msr & ~MSR_BE))
  #endif
  
  #define inst_length(reason)   (((reason) & REASON_PREFIXED) ? 8 : 4)
@@@ -1032,7 -1030,7 +1031,7 @@@ static void p9_hmi_special_emu(struct p
  #endif /* !__LITTLE_ENDIAN__ */
  
        /* Go to next instruction */
 -      regs->nip += 4;
 +      regs_add_return_ip(regs, 4);
  }
  #endif /* CONFIG_VSX */
  
@@@ -1477,7 -1475,7 +1476,7 @@@ static void do_program_check(struct pt_
  
                if (!(regs->msr & MSR_PR) &&  /* not user-mode */
                    report_bug(bugaddr, regs) == BUG_TRAP_TYPE_WARN) {
 -                      regs->nip += 4;
 +                      regs_add_return_ip(regs, 4);
                        return;
                }
                _exception(SIGTRAP, regs, TRAP_BRKPT, regs->nip);
        if (reason & (REASON_ILLEGAL | REASON_PRIVILEGED)) {
                switch (emulate_instruction(regs)) {
                case 0:
 -                      regs->nip += 4;
 +                      regs_add_return_ip(regs, 4);
                        emulate_single_step(regs);
                        return;
                case -EFAULT:
@@@ -1567,7 -1565,7 +1566,7 @@@ DEFINE_INTERRUPT_HANDLER(program_check_
   */
  DEFINE_INTERRUPT_HANDLER(emulation_assist_interrupt)
  {
 -      regs->msr |= REASON_ILLEGAL;
 +      regs_set_return_msr(regs, regs->msr | REASON_ILLEGAL);
        do_program_check(regs);
  }
  
@@@ -1594,7 -1592,7 +1593,7 @@@ DEFINE_INTERRUPT_HANDLER(alignment_exce
  
        if (fixed == 1) {
                /* skip over emulated instruction */
 -              regs->nip += inst_length(reason);
 +              regs_add_return_ip(regs, inst_length(reason));
                emulate_single_step(regs);
                return;
        }
@@@ -1660,7 -1658,7 +1659,7 @@@ static void tm_unavailable(struct pt_re
  #ifdef CONFIG_PPC_TRANSACTIONAL_MEM
        if (user_mode(regs)) {
                current->thread.load_tm++;
 -              regs->msr |= MSR_TM;
 +              regs_set_return_msr(regs, regs->msr | MSR_TM);
                tm_enable();
                tm_restore_sprs(&current->thread);
                return;
@@@ -1752,7 -1750,7 +1751,7 @@@ DEFINE_INTERRUPT_HANDLER(facility_unava
                                pr_err("DSCR based mfspr emulation failed\n");
                                return;
                        }
 -                      regs->nip += 4;
 +                      regs_add_return_ip(regs, 4);
                        emulate_single_step(regs);
                }
                return;
@@@ -1949,7 -1947,7 +1948,7 @@@ static void handle_debug(struct pt_reg
         */
        if (DBCR_ACTIVE_EVENTS(current->thread.debug.dbcr0,
                               current->thread.debug.dbcr1))
 -              regs->msr |= MSR_DE;
 +              regs_set_return_msr(regs, regs->msr | MSR_DE);
        else
                /* Make sure the IDM flag is off */
                current->thread.debug.dbcr0 &= ~DBCR0_IDM;
@@@ -1970,7 -1968,7 +1969,7 @@@ DEFINE_INTERRUPT_HANDLER(DebugException
         * instead of stopping here when hitting a BT
         */
        if (debug_status & DBSR_BT) {
 -              regs->msr &= ~MSR_DE;
 +              regs_set_return_msr(regs, regs->msr & ~MSR_DE);
  
                /* Disable BT */
                mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_BT);
                if (user_mode(regs)) {
                        current->thread.debug.dbcr0 &= ~DBCR0_BT;
                        current->thread.debug.dbcr0 |= DBCR0_IDM | DBCR0_IC;
 -                      regs->msr |= MSR_DE;
 +                      regs_set_return_msr(regs, regs->msr | MSR_DE);
                        return;
                }
  
                if (debugger_sstep(regs))
                        return;
        } else if (debug_status & DBSR_IC) {    /* Instruction complete */
 -              regs->msr &= ~MSR_DE;
 +              regs_set_return_msr(regs, regs->msr & ~MSR_DE);
  
                /* Disable instruction completion */
                mtspr(SPRN_DBCR0, mfspr(SPRN_DBCR0) & ~DBCR0_IC);
                        current->thread.debug.dbcr0 &= ~DBCR0_IC;
                        if (DBCR_ACTIVE_EVENTS(current->thread.debug.dbcr0,
                                               current->thread.debug.dbcr1))
 -                              regs->msr |= MSR_DE;
 +                              regs_set_return_msr(regs, regs->msr | MSR_DE);
                        else
                                /* Make sure the IDM bit is off */
                                current->thread.debug.dbcr0 &= ~DBCR0_IDM;
@@@ -2045,7 -2043,7 +2044,7 @@@ DEFINE_INTERRUPT_HANDLER(altivec_assist
        PPC_WARN_EMULATED(altivec, regs);
        err = emulate_altivec(regs);
        if (err == 0) {
 -              regs->nip += 4;         /* skip emulated instruction */
 +              regs_add_return_ip(regs, 4); /* skip emulated instruction */
                emulate_single_step(regs);
                return;
        }
@@@ -2110,7 -2108,7 +2109,7 @@@ DEFINE_INTERRUPT_HANDLER(SPEFloatingPoi
  
        err = do_spe_mathemu(regs);
        if (err == 0) {
 -              regs->nip += 4;         /* skip emulated instruction */
 +              regs_add_return_ip(regs, 4); /* skip emulated instruction */
                emulate_single_step(regs);
                return;
        }
@@@ -2141,10 -2139,10 +2140,10 @@@ DEFINE_INTERRUPT_HANDLER(SPEFloatingPoi
                giveup_spe(current);
        preempt_enable();
  
 -      regs->nip -= 4;
 +      regs_add_return_ip(regs, -4);
        err = speround_handler(regs);
        if (err == 0) {
 -              regs->nip += 4;         /* skip emulated instruction */
 +              regs_add_return_ip(regs, 4); /* skip emulated instruction */
                emulate_single_step(regs);
                return;
        }
@@@ -24,7 -24,6 +24,7 @@@
  #include <linux/kdebug.h>
  #include <linux/sched/debug.h>
  #include <linux/delay.h>
 +#include <linux/processor.h>
  #include <linux/smp.h>
  
  #include <asm/interrupt.h>
@@@ -184,11 -183,6 +184,6 @@@ static void watchdog_smp_panic(int cpu
  
        wd_smp_unlock(&flags);
  
-       printk_safe_flush();
-       /*
-        * printk_safe_flush() seems to require another print
-        * before anything actually goes out to console.
-        */
        if (sysctl_hardlockup_all_cpu_backtrace)
                trigger_allbutself_cpu_backtrace();
  
@@@ -105,8 -105,8 +105,8 @@@ void crash_ipi_callback(struct pt_regs 
  static void crash_kexec_prepare_cpus(int cpu)
  {
        unsigned int msecs;
 -      unsigned int ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */
 -      int tries = 0;
 +      volatile unsigned int ncpus = num_online_cpus() - 1;/* Excluding the panic cpu */
 +      volatile int tries = 0;
        int (*old_handler)(struct pt_regs *regs);
  
        printk(KERN_EMERG "Sending IPI to other CPUs\n");
@@@ -313,7 -313,7 +313,7 @@@ void default_machine_crash_shutdown(str
        int (*old_handler)(struct pt_regs *regs);
  
        /* Avoid hardlocking with irresponsive CPU holding logbuf_lock */
-       printk_nmi_enter();
+       printk_deferred_enter();
  
        /*
         * This function is only called after the system
diff --combined include/linux/printk.h
@@@ -8,7 -8,6 +8,7 @@@
  #include <linux/linkage.h>
  #include <linux/cache.h>
  #include <linux/ratelimit_types.h>
 +#include <linux/once_lite.h>
  
  extern const char linux_banner[];
  extern const char linux_proc_banner[];
@@@ -70,7 -69,16 +70,7 @@@ extern int console_printk[]
  #define minimum_console_loglevel (console_printk[2])
  #define default_console_loglevel (console_printk[3])
  
 -static inline void console_silent(void)
 -{
 -      console_loglevel = CONSOLE_LOGLEVEL_SILENT;
 -}
 -
 -static inline void console_verbose(void)
 -{
 -      if (console_loglevel)
 -              console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
 -}
 +extern void console_verbose(void);
  
  /* strlen("ratelimit") + 1 */
  #define DEVKMSG_STR_MAX_SIZE 10
@@@ -141,18 -149,6 +141,6 @@@ static inline __printf(1, 2) __col
  void early_printk(const char *s, ...) { }
  #endif
  
- #ifdef CONFIG_PRINTK_NMI
- extern void printk_nmi_enter(void);
- extern void printk_nmi_exit(void);
- extern void printk_nmi_direct_enter(void);
- extern void printk_nmi_direct_exit(void);
- #else
- static inline void printk_nmi_enter(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 */
  struct dev_printk_info;
  
  #ifdef CONFIG_PRINTK
@@@ -165,13 -161,23 +153,23 @@@ asmlinkage __printf(1, 0
  int vprintk(const char *fmt, va_list args);
  
  asmlinkage __printf(1, 2) __cold
 -int printk(const char *fmt, ...);
 +int _printk(const char *fmt, ...);
  
  /*
   * Special printk facility for scheduler/timekeeping use only, _DO_NOT_USE_ !
   */
 -__printf(1, 2) __cold int printk_deferred(const char *fmt, ...);
 +__printf(1, 2) __cold int _printk_deferred(const char *fmt, ...);
  
+ extern void __printk_safe_enter(void);
+ extern void __printk_safe_exit(void);
+ /*
+  * The printk_deferred_enter/exit macros are available only as a hack for
+  * some code paths that need to defer all printk console printing. Interrupts
+  * must be disabled for the deferred duration.
+  */
+ #define printk_deferred_enter __printk_safe_enter
+ #define printk_deferred_exit __printk_safe_exit
  /*
   * Please don't use printk_ratelimit(), because it shares ratelimiting state
   * with all other unrelated printk_ratelimit() callsites.  Instead use
@@@ -200,8 -206,6 +198,6 @@@ void dump_stack_print_info(const char *
  void show_regs_print_info(const char *log_lvl);
  extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold;
  extern asmlinkage void dump_stack(void) __cold;
- extern void printk_safe_flush(void);
- extern void printk_safe_flush_on_panic(void);
  #else
  static inline __printf(1, 0)
  int vprintk(const char *s, va_list args)
        return 0;
  }
  static inline __printf(1, 2) __cold
 -int printk(const char *s, ...)
 +int _printk(const char *s, ...)
  {
        return 0;
  }
  static inline __printf(1, 2) __cold
 -int printk_deferred(const char *s, ...)
 +int _printk_deferred(const char *s, ...)
  {
        return 0;
  }
+ static inline void printk_deferred_enter(void)
+ {
+ }
+ static inline void printk_deferred_exit(void)
+ {
+ }
  static inline int printk_ratelimit(void)
  {
        return 0;
@@@ -269,14 -282,6 +274,6 @@@ static inline void dump_stack_lvl(cons
  static inline void dump_stack(void)
  {
  }
- static inline void printk_safe_flush(void)
- {
- }
- static inline void printk_safe_flush_on_panic(void)
- {
- }
  #endif
  
  #ifdef CONFIG_SMP
@@@ -339,117 -344,6 +336,117 @@@ extern int kptr_restrict
  #define pr_fmt(fmt) fmt
  #endif
  
 +struct module;
 +
 +#ifdef CONFIG_PRINTK_INDEX
 +struct pi_entry {
 +      const char *fmt;
 +      const char *func;
 +      const char *file;
 +      unsigned int line;
 +
 +      /*
 +       * While printk and pr_* have the level stored in the string at compile
 +       * time, some subsystems dynamically add it at runtime through the
 +       * format string. For these dynamic cases, we allow the subsystem to
 +       * tell us the level at compile time.
 +       *
 +       * NULL indicates that the level, if any, is stored in fmt.
 +       */
 +      const char *level;
 +
 +      /*
 +       * The format string used by various subsystem specific printk()
 +       * wrappers to prefix the message.
 +       *
 +       * Note that the static prefix defined by the pr_fmt() macro is stored
 +       * directly in the message format (@fmt), not here.
 +       */
 +      const char *subsys_fmt_prefix;
 +} __packed;
 +
 +#define __printk_index_emit(_fmt, _level, _subsys_fmt_prefix)         \
 +      do {                                                            \
 +              if (__builtin_constant_p(_fmt) && __builtin_constant_p(_level)) { \
 +                      /*
 +                       * We check __builtin_constant_p multiple times here
 +                       * for the same input because GCC will produce an error
 +                       * if we try to assign a static variable to fmt if it
 +                       * is not a constant, even with the outer if statement.
 +                       */                                             \
 +                      static const struct pi_entry _entry             \
 +                      __used = {                                      \
 +                              .fmt = __builtin_constant_p(_fmt) ? (_fmt) : NULL, \
 +                              .func = __func__,                       \
 +                              .file = __FILE__,                       \
 +                              .line = __LINE__,                       \
 +                              .level = __builtin_constant_p(_level) ? (_level) : NULL, \
 +                              .subsys_fmt_prefix = _subsys_fmt_prefix,\
 +                      };                                              \
 +                      static const struct pi_entry *_entry_ptr        \
 +                      __used __section(".printk_index") = &_entry;    \
 +              }                                                       \
 +      } while (0)
 +
 +#else /* !CONFIG_PRINTK_INDEX */
 +#define __printk_index_emit(...) do {} while (0)
 +#endif /* CONFIG_PRINTK_INDEX */
 +
 +/*
 + * Some subsystems have their own custom printk that applies a va_format to a
 + * generic format, for example, to include a device number or other metadata
 + * alongside the format supplied by the caller.
 + *
 + * In order to store these in the way they would be emitted by the printk
 + * infrastructure, the subsystem provides us with the start, fixed string, and
 + * any subsequent text in the format string.
 + *
 + * We take a variable argument list as pr_fmt/dev_fmt/etc are sometimes passed
 + * as multiple arguments (eg: `"%s: ", "blah"`), and we must only take the
 + * first one.
 + *
 + * subsys_fmt_prefix must be known at compile time, or compilation will fail
 + * (since this is a mistake). If fmt or level is not known at compile time, no
 + * index entry will be made (since this can legitimately happen).
 + */
 +#define printk_index_subsys_emit(subsys_fmt_prefix, level, fmt, ...) \
 +      __printk_index_emit(fmt, level, subsys_fmt_prefix)
 +
 +#define printk_index_wrap(_p_func, _fmt, ...)                         \
 +      ({                                                              \
 +              __printk_index_emit(_fmt, NULL, NULL);                  \
 +              _p_func(_fmt, ##__VA_ARGS__);                           \
 +      })
 +
 +
 +/**
 + * printk - print a kernel message
 + * @fmt: format string
 + *
 + * This is printk(). It can be called from any context. We want it to work.
 + *
 + * If printk indexing is enabled, _printk() is called from printk_index_wrap.
 + * Otherwise, printk is simply #defined to _printk.
 + *
 + * We try to grab the console_lock. If we succeed, it's easy - we log the
 + * output and call the console drivers.  If we fail to get the semaphore, we
 + * place the output into the log buffer and return. The current holder of
 + * the console_sem will notice the new output in console_unlock(); and will
 + * send it to the consoles before releasing the lock.
 + *
 + * One effect of this deferred printing is that code which calls printk() and
 + * then changes console_loglevel may break. This is because console_loglevel
 + * is inspected when the actual printing occurs.
 + *
 + * See also:
 + * printf(3)
 + *
 + * See the vsnprintf() documentation for format string extensions over C99.
 + */
 +#define printk(fmt, ...) printk_index_wrap(_printk, fmt, ##__VA_ARGS__)
 +#define printk_deferred(fmt, ...)                                     \
 +      printk_index_wrap(_printk_deferred, fmt, ##__VA_ARGS__)
 +
  /**
   * pr_emerg - Print an emergency-level message
   * @fmt: format string
  
  #ifdef CONFIG_PRINTK
  #define printk_once(fmt, ...)                                 \
 -({                                                            \
 -      static bool __section(".data.once") __print_once;       \
 -      bool __ret_print_once = !__print_once;                  \
 -                                                              \
 -      if (!__print_once) {                                    \
 -              __print_once = true;                            \
 -              printk(fmt, ##__VA_ARGS__);                     \
 -      }                                                       \
 -      unlikely(__ret_print_once);                             \
 -})
 +      DO_ONCE_LITE(printk, fmt, ##__VA_ARGS__)
  #define printk_deferred_once(fmt, ...)                                \
 -({                                                            \
 -      static bool __section(".data.once") __print_once;       \
 -      bool __ret_print_once = !__print_once;                  \
 -                                                              \
 -      if (!__print_once) {                                    \
 -              __print_once = true;                            \
 -              printk_deferred(fmt, ##__VA_ARGS__);            \
 -      }                                                       \
 -      unlikely(__ret_print_once);                             \
 -})
 +      DO_ONCE_LITE(printk_deferred, fmt, ##__VA_ARGS__)
  #else
  #define printk_once(fmt, ...)                                 \
        no_printk(fmt, ##__VA_ARGS__)
diff --combined init/Kconfig
@@@ -83,9 -83,6 +83,9 @@@ config TOOLS_SUPPORT_REL
  config CC_HAS_ASM_INLINE
        def_bool $(success,echo 'void foo(void) { asm inline (""); }' | $(CC) -x c - -c -o /dev/null)
  
 +config CC_HAS_NO_PROFILE_FN_ATTR
 +      def_bool $(success,echo '__attribute__((no_profile_instrument_function)) int x();' | $(CC) -x c - -c -o /dev/null -Werror)
 +
  config CONSTRUCTORS
        bool
  
@@@ -775,20 -772,6 +775,20 @@@ config PRINTK_SAFE_LOG_BUF_SHIF
                     13 =>   8 KB for each CPU
                     12 =>   4 KB for each CPU
  
 +config PRINTK_INDEX
 +      bool "Printk indexing debugfs interface"
 +      depends on PRINTK && DEBUG_FS
 +      help
 +        Add support for indexing of all printk formats known at compile time
 +        at <debugfs>/printk/index/<module>.
 +
 +        This can be used as part of maintaining daemons which monitor
 +        /dev/kmsg, as it permits auditing the printk formats present in a
 +        kernel, allowing detection of cases where monitored printks are
 +        changed or no longer present.
 +
 +        There is no additional runtime cost to printk with this enabled.
 +
  #
  # Architectures with an unreliable sched_clock() should select this:
  #
@@@ -1523,11 -1506,6 +1523,6 @@@ config PRINT
          very difficult to diagnose system problems, saying N here is
          strongly discouraged.
  
- config PRINTK_NMI
-       def_bool y
-       depends on PRINTK
-       depends on HAVE_NMI
  config BUG
        bool "BUG() support" if EXPERT
        default y
@@@ -1861,7 -1839,6 +1856,7 @@@ config SLUB_DEBU
        default y
        bool "Enable SLUB debugging support" if EXPERT
        depends on SLUB && SYSFS
 +      select STACKDEPOT if STACKTRACE_SUPPORT
        help
          SLUB has extensive debug support features. Disabling these can
          result in significant savings in code size. This also disables
diff --combined kernel/kexec_core.c
@@@ -26,7 -26,6 +26,7 @@@
  #include <linux/suspend.h>
  #include <linux/device.h>
  #include <linux/freezer.h>
 +#include <linux/panic_notifier.h>
  #include <linux/pm.h>
  #include <linux/cpu.h>
  #include <linux/uaccess.h>
@@@ -979,7 -978,6 +979,6 @@@ void crash_kexec(struct pt_regs *regs
        old_cpu = atomic_cmpxchg(&panic_cpu, PANIC_CPU_INVALID, this_cpu);
        if (old_cpu == PANIC_CPU_INVALID) {
                /* This is the 1st CPU which comes here, so go ahead. */
-               printk_safe_flush_on_panic();
                __crash_kexec(regs);
  
                /*
diff --combined kernel/panic.c
@@@ -23,7 -23,6 +23,7 @@@
  #include <linux/reboot.h>
  #include <linux/delay.h>
  #include <linux/kexec.h>
 +#include <linux/panic_notifier.h>
  #include <linux/sched.h>
  #include <linux/sysrq.h>
  #include <linux/init.h>
@@@ -248,7 -247,6 +248,6 @@@ void panic(const char *fmt, ...
         * Bypass the panic_cpu check and call __crash_kexec directly.
         */
        if (!_crash_kexec_post_notifiers) {
-               printk_safe_flush_on_panic();
                __crash_kexec(NULL);
  
                /*
         */
        atomic_notifier_call_chain(&panic_notifier_list, 0, buf);
  
-       /* Call flush even twice. It tries harder with a single online CPU */
-       printk_safe_flush_on_panic();
        kmsg_dump(KMSG_DUMP_PANIC);
  
        /*
diff --combined kernel/printk/internal.h
@@@ -6,18 -6,6 +6,12 @@@
  
  #ifdef CONFIG_PRINTK
  
- #define PRINTK_SAFE_CONTEXT_MASK      0x007ffffff
- #define PRINTK_NMI_DIRECT_CONTEXT_MASK        0x008000000
- #define PRINTK_NMI_CONTEXT_MASK               0xff0000000
- #define PRINTK_NMI_CONTEXT_OFFSET     0x010000000
 +/* Flags for a single printk record. */
 +enum printk_info_flags {
 +      LOG_NEWLINE     = 2,    /* text ended with a newline */
 +      LOG_CONT        = 8,    /* text is a fragment of a continuation line */
 +};
 +
  __printf(4, 0)
  int vprintk_store(int facility, int level,
                  const struct dev_printk_info *dev_info,
  
  __printf(1, 0) int vprintk_default(const char *fmt, va_list args);
  __printf(1, 0) int vprintk_deferred(const char *fmt, va_list args);
- void __printk_safe_enter(void);
- void __printk_safe_exit(void);
  
- void printk_safe_init(void);
  bool printk_percpu_data_ready(void);
  
  #define printk_safe_enter_irqsave(flags)      \
                local_irq_restore(flags);       \
        } while (0)
  
- #define printk_safe_enter_irq()               \
-       do {                                    \
-               local_irq_disable();            \
-               __printk_safe_enter();          \
-       } while (0)
- #define printk_safe_exit_irq()                        \
-       do {                                    \
-               __printk_safe_exit();           \
-               local_irq_enable();             \
-       } while (0)
  void defer_console_output(void);
  
 +u16 printk_parse_prefix(const char *text, int *level,
 +                      enum printk_info_flags *flags);
  #else
  
  /*
@@@ -69,9 -40,5 +48,5 @@@
  #define printk_safe_enter_irqsave(flags) local_irq_save(flags)
  #define printk_safe_exit_irqrestore(flags) local_irq_restore(flags)
  
- #define printk_safe_enter_irq() local_irq_disable()
- #define printk_safe_exit_irq() local_irq_enable()
- static inline void printk_safe_init(void) { }
  static inline bool printk_percpu_data_ready(void) { return false; }
  #endif /* CONFIG_PRINTK */
diff --combined kernel/printk/printk.c
@@@ -350,8 -350,13 +350,8 @@@ static int console_msg_format = MSG_FOR
   * non-prinatable characters are escaped in the "\xff" notation.
   */
  
 -enum log_flags {
 -      LOG_NEWLINE     = 2,    /* text ended with a newline */
 -      LOG_CONT        = 8,    /* text is a fragment of a continuation line */
 -};
 -
  /* syslog_lock protects syslog_* variables and write access to clear_seq. */
- static DEFINE_RAW_SPINLOCK(syslog_lock);
+ static DEFINE_MUTEX(syslog_lock);
  
  #ifdef CONFIG_PRINTK
  DECLARE_WAIT_QUEUE_HEAD(log_wait);
@@@ -727,27 -732,22 +727,22 @@@ static ssize_t devkmsg_read(struct fil
        if (ret)
                return ret;
  
-       printk_safe_enter_irq();
        if (!prb_read_valid(prb, atomic64_read(&user->seq), r)) {
                if (file->f_flags & O_NONBLOCK) {
                        ret = -EAGAIN;
-                       printk_safe_exit_irq();
                        goto out;
                }
  
-               printk_safe_exit_irq();
                ret = wait_event_interruptible(log_wait,
                                prb_read_valid(prb, atomic64_read(&user->seq), r));
                if (ret)
                        goto out;
-               printk_safe_enter_irq();
        }
  
        if (r->info->seq != atomic64_read(&user->seq)) {
                /* our last seen message is gone, return error and reset */
                atomic64_set(&user->seq, r->info->seq);
                ret = -EPIPE;
-               printk_safe_exit_irq();
                goto out;
        }
  
                                  &r->info->dev_info);
  
        atomic64_set(&user->seq, r->info->seq + 1);
-       printk_safe_exit_irq();
  
        if (len > count) {
                ret = -EINVAL;
@@@ -792,7 -791,6 +786,6 @@@ static loff_t devkmsg_llseek(struct fil
        if (offset)
                return -ESPIPE;
  
-       printk_safe_enter_irq();
        switch (whence) {
        case SEEK_SET:
                /* the first record */
        default:
                ret = -EINVAL;
        }
-       printk_safe_exit_irq();
        return ret;
  }
  
@@@ -828,7 -825,6 +820,6 @@@ static __poll_t devkmsg_poll(struct fil
  
        poll_wait(file, &log_wait, wait);
  
-       printk_safe_enter_irq();
        if (prb_read_valid_info(prb, atomic64_read(&user->seq), &info, NULL)) {
                /* return error when data has vanished underneath us */
                if (info.seq != atomic64_read(&user->seq))
                else
                        ret = EPOLLIN|EPOLLRDNORM;
        }
-       printk_safe_exit_irq();
  
        return ret;
  }
@@@ -869,9 -864,7 +859,7 @@@ static int devkmsg_open(struct inode *i
        prb_rec_init_rd(&user->record, &user->info,
                        &user->text_buf[0], sizeof(user->text_buf));
  
-       printk_safe_enter_irq();
        atomic64_set(&user->seq, prb_first_valid_seq(prb));
-       printk_safe_exit_irq();
  
        file->private_data = user;
        return 0;
@@@ -1037,9 -1030,6 +1025,6 @@@ static inline void log_buf_add_cpu(void
  
  static void __init set_percpu_data_ready(void)
  {
-       printk_safe_init();
-       /* Make sure we set this flag only after printk_safe() init is done */
-       barrier();
        __printk_percpu_data_ready = true;
  }
  
@@@ -1077,6 -1067,7 +1062,7 @@@ void __init setup_log_buf(int early
        struct prb_desc *new_descs;
        struct printk_info info;
        struct printk_record r;
+       unsigned int text_size;
        size_t new_descs_size;
        size_t new_infos_size;
        unsigned long flags;
                 new_descs, ilog2(new_descs_count),
                 new_infos);
  
-       printk_safe_enter_irqsave(flags);
+       local_irq_save(flags);
  
        log_buf_len = new_log_buf_len;
        log_buf = new_log_buf;
        new_log_buf_len = 0;
  
        free = __LOG_BUF_LEN;
-       prb_for_each_record(0, &printk_rb_static, seq, &r)
-               free -= add_to_rb(&printk_rb_dynamic, &r);
+       prb_for_each_record(0, &printk_rb_static, seq, &r) {
+               text_size = add_to_rb(&printk_rb_dynamic, &r);
+               if (text_size > free)
+                       free = 0;
+               else
+                       free -= text_size;
+       }
  
-       /*
-        * This is early enough that everything is still running on the
-        * boot CPU and interrupts are disabled. So no new messages will
-        * appear during the transition to the dynamic buffer.
-        */
        prb = &printk_rb_dynamic;
  
-       printk_safe_exit_irqrestore(flags);
+       local_irq_restore(flags);
+       /*
+        * Copy any remaining messages that might have appeared from
+        * NMI context after copying but before switching to the
+        * dynamic buffer.
+        */
+       prb_for_each_record(seq, &printk_rb_static, seq, &r) {
+               text_size = add_to_rb(&printk_rb_dynamic, &r);
+               if (text_size > free)
+                       free = 0;
+               else
+                       free -= text_size;
+       }
  
        if (seq != prb_next_seq(&printk_rb_static)) {
                pr_err("dropped %llu messages\n",
@@@ -1476,12 -1480,14 +1475,14 @@@ static u64 find_first_fitting_seq(u64 s
        return seq;
  }
  
+ /* The caller is responsible for making sure @size is greater than 0. */
  static int syslog_print(char __user *buf, int size)
  {
        struct printk_info info;
        struct printk_record r;
        char *text;
        int len = 0;
+       u64 seq;
  
        text = kmalloc(CONSOLE_LOG_MAX, GFP_KERNEL);
        if (!text)
  
        prb_rec_init_rd(&r, &info, text, CONSOLE_LOG_MAX);
  
-       while (size > 0) {
+       mutex_lock(&syslog_lock);
+       /*
+        * Wait for the @syslog_seq record to be available. @syslog_seq may
+        * change while waiting.
+        */
+       do {
+               seq = syslog_seq;
+               mutex_unlock(&syslog_lock);
+               len = wait_event_interruptible(log_wait, prb_read_valid(prb, seq, NULL));
+               mutex_lock(&syslog_lock);
+               if (len)
+                       goto out;
+       } while (syslog_seq != seq);
+       /*
+        * Copy records that fit into the buffer. The above cycle makes sure
+        * that the first record is always available.
+        */
+       do {
                size_t n;
                size_t skip;
+               int err;
  
-               printk_safe_enter_irq();
-               raw_spin_lock(&syslog_lock);
-               if (!prb_read_valid(prb, syslog_seq, &r)) {
-                       raw_spin_unlock(&syslog_lock);
-                       printk_safe_exit_irq();
+               if (!prb_read_valid(prb, syslog_seq, &r))
                        break;
-               }
                if (r.info->seq != syslog_seq) {
                        /* message is gone, move to next valid one */
                        syslog_seq = r.info->seq;
                        syslog_partial += n;
                } else
                        n = 0;
-               raw_spin_unlock(&syslog_lock);
-               printk_safe_exit_irq();
  
                if (!n)
                        break;
  
-               if (copy_to_user(buf, text + skip, n)) {
+               mutex_unlock(&syslog_lock);
+               err = copy_to_user(buf, text + skip, n);
+               mutex_lock(&syslog_lock);
+               if (err) {
                        if (!len)
                                len = -EFAULT;
                        break;
                len += n;
                size -= n;
                buf += n;
-       }
+       } while (size);
+ out:
+       mutex_unlock(&syslog_lock);
        kfree(text);
        return len;
  }
@@@ -1561,7 -1588,6 +1583,6 @@@ static int syslog_print_all(char __use
                return -ENOMEM;
  
        time = printk_time;
-       printk_safe_enter_irq();
        /*
         * Find first record that fits, including all following records,
         * into the user-provided buffer for this dump.
                        break;
                }
  
-               printk_safe_exit_irq();
                if (copy_to_user(buf + len, text, textlen))
                        len = -EFAULT;
                else
                        len += textlen;
-               printk_safe_enter_irq();
  
                if (len < 0)
                        break;
        }
  
        if (clear) {
-               raw_spin_lock(&syslog_lock);
+               mutex_lock(&syslog_lock);
                latched_seq_write(&clear_seq, seq);
-               raw_spin_unlock(&syslog_lock);
+               mutex_unlock(&syslog_lock);
        }
-       printk_safe_exit_irq();
  
        kfree(text);
        return len;
  
  static void syslog_clear(void)
  {
-       printk_safe_enter_irq();
-       raw_spin_lock(&syslog_lock);
+       mutex_lock(&syslog_lock);
        latched_seq_write(&clear_seq, prb_next_seq(prb));
-       raw_spin_unlock(&syslog_lock);
-       printk_safe_exit_irq();
- }
- /* Return a consistent copy of @syslog_seq. */
- static u64 read_syslog_seq_irq(void)
- {
-       u64 seq;
-       raw_spin_lock_irq(&syslog_lock);
-       seq = syslog_seq;
-       raw_spin_unlock_irq(&syslog_lock);
-       return seq;
+       mutex_unlock(&syslog_lock);
  }
  
  int do_syslog(int type, char __user *buf, int len, int source)
                        return 0;
                if (!access_ok(buf, len))
                        return -EFAULT;
-               error = wait_event_interruptible(log_wait,
-                               prb_read_valid(prb, read_syslog_seq_irq(), NULL));
-               if (error)
-                       return error;
                error = syslog_print(buf, len);
                break;
        /* Read/clear last kernel messages */
                break;
        /* Number of chars in the log buffer */
        case SYSLOG_ACTION_SIZE_UNREAD:
-               printk_safe_enter_irq();
-               raw_spin_lock(&syslog_lock);
+               mutex_lock(&syslog_lock);
                if (!prb_read_valid_info(prb, syslog_seq, &info, NULL)) {
                        /* No unread messages. */
-                       raw_spin_unlock(&syslog_lock);
-                       printk_safe_exit_irq();
+                       mutex_unlock(&syslog_lock);
                        return 0;
                }
                if (info.seq != syslog_seq) {
                        }
                        error -= syslog_partial;
                }
-               raw_spin_unlock(&syslog_lock);
-               printk_safe_exit_irq();
+               mutex_unlock(&syslog_lock);
                break;
        /* Size of the log buffer */
        case SYSLOG_ACTION_SIZE_BUFFER:
@@@ -1935,6 -1936,76 +1931,76 @@@ static void call_console_drivers(const 
        }
  }
  
+ /*
+  * Recursion is tracked separately on each CPU. If NMIs are supported, an
+  * additional NMI context per CPU is also separately tracked. Until per-CPU
+  * is available, a separate "early tracking" is performed.
+  */
+ static DEFINE_PER_CPU(u8, printk_count);
+ static u8 printk_count_early;
+ #ifdef CONFIG_HAVE_NMI
+ static DEFINE_PER_CPU(u8, printk_count_nmi);
+ static u8 printk_count_nmi_early;
+ #endif
+ /*
+  * Recursion is limited to keep the output sane. printk() should not require
+  * more than 1 level of recursion (allowing, for example, printk() to trigger
+  * a WARN), but a higher value is used in case some printk-internal errors
+  * exist, such as the ringbuffer validation checks failing.
+  */
+ #define PRINTK_MAX_RECURSION 3
+ /*
+  * Return a pointer to the dedicated counter for the CPU+context of the
+  * caller.
+  */
+ static u8 *__printk_recursion_counter(void)
+ {
+ #ifdef CONFIG_HAVE_NMI
+       if (in_nmi()) {
+               if (printk_percpu_data_ready())
+                       return this_cpu_ptr(&printk_count_nmi);
+               return &printk_count_nmi_early;
+       }
+ #endif
+       if (printk_percpu_data_ready())
+               return this_cpu_ptr(&printk_count);
+       return &printk_count_early;
+ }
+ /*
+  * Enter recursion tracking. Interrupts are disabled to simplify tracking.
+  * The caller must check the boolean return value to see if the recursion is
+  * allowed. On failure, interrupts are not disabled.
+  *
+  * @recursion_ptr must be a variable of type (u8 *) and is the same variable
+  * that is passed to printk_exit_irqrestore().
+  */
+ #define printk_enter_irqsave(recursion_ptr, flags)    \
+ ({                                                    \
+       bool success = true;                            \
+                                                       \
+       typecheck(u8 *, recursion_ptr);                 \
+       local_irq_save(flags);                          \
+       (recursion_ptr) = __printk_recursion_counter(); \
+       if (*(recursion_ptr) > PRINTK_MAX_RECURSION) {  \
+               local_irq_restore(flags);               \
+               success = false;                        \
+       } else {                                        \
+               (*(recursion_ptr))++;                   \
+       }                                               \
+       success;                                        \
+ })
+ /* Exit recursion tracking, restoring interrupts. */
+ #define printk_exit_irqrestore(recursion_ptr, flags)  \
+       do {                                            \
+               typecheck(u8 *, recursion_ptr);         \
+               (*(recursion_ptr))--;                   \
+               local_irq_restore(flags);               \
+       } while (0)
  int printk_delay_msec __read_mostly;
  
  static inline void printk_delay(void)
@@@ -1956,24 -2027,23 +2022,24 @@@ static inline u32 printk_caller_id(void
  }
  
  /**
 - * parse_prefix - Parse level and control flags.
 + * printk_parse_prefix - Parse level and control flags.
   *
   * @text:     The terminated text message.
   * @level:    A pointer to the current level value, will be updated.
 - * @lflags:   A pointer to the current log flags, will be updated.
 + * @flags:    A pointer to the current printk_info flags, will be updated.
   *
   * @level may be NULL if the caller is not interested in the parsed value.
   * Otherwise the variable pointed to by @level must be set to
   * LOGLEVEL_DEFAULT in order to be updated with the parsed value.
   *
 - * @lflags may be NULL if the caller is not interested in the parsed value.
 - * Otherwise the variable pointed to by @lflags will be OR'd with the parsed
 + * @flags may be NULL if the caller is not interested in the parsed value.
 + * Otherwise the variable pointed to by @flags will be OR'd with the parsed
   * value.
   *
   * Return: The length of the parsed level and control flags.
   */
 -static u16 parse_prefix(char *text, int *level, enum log_flags *lflags)
 +u16 printk_parse_prefix(const char *text, int *level,
 +                      enum printk_info_flags *flags)
  {
        u16 prefix_len = 0;
        int kern_level;
                                *level = kern_level - '0';
                        break;
                case 'c':       /* KERN_CONT */
 -                      if (lflags)
 -                              *lflags |= LOG_CONT;
 +                      if (flags)
 +                              *flags |= LOG_CONT;
                }
  
                prefix_len += 2;
        return prefix_len;
  }
  
 -static u16 printk_sprint(char *text, u16 size, int facility, enum log_flags *lflags,
 -                       const char *fmt, va_list args)
 +static u16 printk_sprint(char *text, u16 size, int facility,
 +                       enum printk_info_flags *flags, const char *fmt,
 +                       va_list args)
  {
        u16 text_len;
  
        /* Mark and strip a trailing newline. */
        if (text_len && text[text_len - 1] == '\n') {
                text_len--;
 -              *lflags |= LOG_NEWLINE;
 +              *flags |= LOG_NEWLINE;
        }
  
        /* Strip log level and control flags. */
        if (facility == 0) {
                u16 prefix_len;
  
 -              prefix_len = parse_prefix(text, NULL, NULL);
 +              prefix_len = printk_parse_prefix(text, NULL, NULL);
                if (prefix_len) {
                        text_len -= prefix_len;
                        memmove(text, text + prefix_len, text_len);
@@@ -2035,13 -2104,16 +2101,16 @@@ int vprintk_store(int facility, int lev
  {
        const u32 caller_id = printk_caller_id();
        struct prb_reserved_entry e;
 -      enum log_flags lflags = 0;
 +      enum printk_info_flags flags = 0;
        struct printk_record r;
+       unsigned long irqflags;
        u16 trunc_msg_len = 0;
        char prefix_buf[8];
+       u8 *recursion_ptr;
        u16 reserve_size;
        va_list args2;
        u16 text_len;
+       int ret = 0;
        u64 ts_nsec;
  
        /*
         */
        ts_nsec = local_clock();
  
+       if (!printk_enter_irqsave(recursion_ptr, irqflags))
+               return 0;
        /*
         * The sprintf needs to come first since the syslog prefix might be
         * passed in as a parameter. An extra byte must be reserved so that
  
        /* Extract log level or control flags. */
        if (facility == 0)
 -              parse_prefix(&prefix_buf[0], &level, &lflags);
 +              printk_parse_prefix(&prefix_buf[0], &level, &flags);
  
        if (level == LOGLEVEL_DEFAULT)
                level = default_message_loglevel;
  
        if (dev_info)
 -              lflags |= LOG_NEWLINE;
 +              flags |= LOG_NEWLINE;
  
 -      if (lflags & LOG_CONT) {
 +      if (flags & LOG_CONT) {
                prb_rec_init_wr(&r, reserve_size);
                if (prb_reserve_in_last(&e, prb, &r, caller_id, LOG_LINE_MAX)) {
                        text_len = printk_sprint(&r.text_buf[r.info->text_len], reserve_size,
 -                                               facility, &lflags, fmt, args);
 +                                               facility, &flags, fmt, args);
                        r.info->text_len += text_len;
  
 -                      if (lflags & LOG_NEWLINE) {
 +                      if (flags & LOG_NEWLINE) {
                                r.info->flags |= LOG_NEWLINE;
                                prb_final_commit(&e);
                        } else {
                                prb_commit(&e);
                        }
  
-                       return text_len;
+                       ret = text_len;
+                       goto out;
                }
        }
  
  
                prb_rec_init_wr(&r, reserve_size + trunc_msg_len);
                if (!prb_reserve(&e, prb, &r))
-                       return 0;
+                       goto out;
        }
  
        /* fill message */
 -      text_len = printk_sprint(&r.text_buf[0], reserve_size, facility, &lflags, fmt, args);
 +      text_len = printk_sprint(&r.text_buf[0], reserve_size, facility, &flags, fmt, args);
        if (trunc_msg_len)
                memcpy(&r.text_buf[text_len], trunc_msg, trunc_msg_len);
        r.info->text_len = text_len + trunc_msg_len;
        r.info->facility = facility;
        r.info->level = level & 7;
 -      r.info->flags = lflags & 0x1f;
 +      r.info->flags = flags & 0x1f;
        r.info->ts_nsec = ts_nsec;
        r.info->caller_id = caller_id;
        if (dev_info)
                memcpy(&r.info->dev_info, dev_info, sizeof(r.info->dev_info));
  
        /* A message without a trailing newline can be continued. */
 -      if (!(lflags & LOG_NEWLINE))
 +      if (!(flags & LOG_NEWLINE))
                prb_commit(&e);
        else
                prb_final_commit(&e);
  
-       return (text_len + trunc_msg_len);
+       ret = text_len + trunc_msg_len;
+ out:
+       printk_exit_irqrestore(recursion_ptr, irqflags);
+       return ret;
  }
  
  asmlinkage int vprintk_emit(int facility, int level,
  {
        int printed_len;
        bool in_sched = false;
-       unsigned long flags;
  
        /* Suppress unimportant messages after panic happens */
        if (unlikely(suppress_printk))
        boot_delay_msec(level);
        printk_delay();
  
-       printk_safe_enter_irqsave(flags);
        printed_len = vprintk_store(facility, level, dev_info, fmt, args);
-       printk_safe_exit_irqrestore(flags);
  
        /* If called from the scheduler, we can not call up(). */
        if (!in_sched) {
@@@ -2183,7 -2259,28 +2256,7 @@@ int vprintk_default(const char *fmt, va
  }
  EXPORT_SYMBOL_GPL(vprintk_default);
  
 -/**
 - * printk - print a kernel message
 - * @fmt: format string
 - *
 - * This is printk(). It can be called from any context. We want it to work.
 - *
 - * We try to grab the console_lock. If we succeed, it's easy - we log the
 - * output and call the console drivers.  If we fail to get the semaphore, we
 - * place the output into the log buffer and return. The current holder of
 - * the console_sem will notice the new output in console_unlock(); and will
 - * send it to the consoles before releasing the lock.
 - *
 - * One effect of this deferred printing is that code which calls printk() and
 - * then changes console_loglevel may break. This is because console_loglevel
 - * is inspected when the actual printing occurs.
 - *
 - * See also:
 - * printf(3)
 - *
 - * See the vsnprintf() documentation for format string extensions over C99.
 - */
 -asmlinkage __visible int printk(const char *fmt, ...)
 +asmlinkage __visible int _printk(const char *fmt, ...)
  {
        va_list args;
        int r;
  
        return r;
  }
 -EXPORT_SYMBOL(printk);
 +EXPORT_SYMBOL(_printk);
  
  #else /* CONFIG_PRINTK */
  
@@@ -2380,18 -2477,6 +2453,18 @@@ module_param_named(console_suspend, con
  MODULE_PARM_DESC(console_suspend, "suspend console during suspend"
        " and hibernate operations");
  
 +static bool printk_console_no_auto_verbose;
 +
 +void console_verbose(void)
 +{
 +      if (console_loglevel && !printk_console_no_auto_verbose)
 +              console_loglevel = CONSOLE_LOGLEVEL_MOTORMOUTH;
 +}
 +EXPORT_SYMBOL_GPL(console_verbose);
 +
 +module_param_named(console_no_auto_verbose, printk_console_no_auto_verbose, bool, 0644);
 +MODULE_PARM_DESC(console_no_auto_verbose, "Disable console loglevel raise to highest on oops/panic/etc");
 +
  /**
   * suspend_console - suspend the console subsystem
   *
@@@ -2533,7 -2618,6 +2606,7 @@@ void console_unlock(void
        bool do_cond_resched, retry;
        struct printk_info info;
        struct printk_record r;
 +      u64 __maybe_unused next_seq;
  
        if (console_suspended) {
                up_console_sem();
@@@ -2573,9 -2657,9 +2646,9 @@@ again
  
        for (;;) {
                size_t ext_len = 0;
+               int handover;
                size_t len;
  
-               printk_safe_enter_irqsave(flags);
  skip:
                if (!prb_read_valid(prb, console_seq, &r))
                        break;
                 * were to occur on another CPU, it may wait for this one to
                 * finish. This task can not be preempted if there is a
                 * waiter waiting to take over.
+                *
+                * Interrupts are disabled because the hand over to a waiter
+                * must not be interrupted until the hand over is completed
+                * (@console_waiter is cleared).
                 */
+               printk_safe_enter_irqsave(flags);
                console_lock_spinning_enable();
  
                stop_critical_timings();        /* don't trace print latency */
                call_console_drivers(ext_text, ext_len, text, len);
                start_critical_timings();
  
-               if (console_lock_spinning_disable_and_check()) {
-                       printk_safe_exit_irqrestore(flags);
-                       return;
-               }
+               handover = console_lock_spinning_disable_and_check();
                printk_safe_exit_irqrestore(flags);
+               if (handover)
+                       return;
  
                if (do_cond_resched)
                        cond_resched();
        }
  
 -      console_locked = 0;
 +      /* Get consistent value of the next-to-be-used sequence number. */
 +      next_seq = console_seq;
  
 +      console_locked = 0;
        up_console_sem();
  
        /*
         * there's a new owner and the console_unlock() from them will do the
         * flush, no worries.
         */
 -      retry = prb_read_valid(prb, console_seq, NULL);
 +      retry = prb_read_valid(prb, next_seq, NULL);
-       printk_safe_exit_irqrestore(flags);
        if (retry && console_trylock())
                goto again;
  }
@@@ -2719,13 -2802,8 +2793,8 @@@ void console_flush_on_panic(enum con_fl
        console_trylock();
        console_may_schedule = 0;
  
-       if (mode == CONSOLE_REPLAY_ALL) {
-               unsigned long flags;
-               printk_safe_enter_irqsave(flags);
+       if (mode == CONSOLE_REPLAY_ALL)
                console_seq = prb_first_valid_seq(prb);
-               printk_safe_exit_irqrestore(flags);
-       }
        console_unlock();
  }
  
@@@ -2860,7 -2938,6 +2929,6 @@@ static int try_enable_new_console(struc
   */
  void register_console(struct console *newcon)
  {
-       unsigned long flags;
        struct console *bcon = NULL;
        int err;
  
                exclusive_console_stop_seq = console_seq;
  
                /* Get a consistent copy of @syslog_seq. */
-               raw_spin_lock_irqsave(&syslog_lock, flags);
+               mutex_lock(&syslog_lock);
                console_seq = syslog_seq;
-               raw_spin_unlock_irqrestore(&syslog_lock, flags);
+               mutex_unlock(&syslog_lock);
        }
        console_unlock();
        console_sysfs_notify();
@@@ -3194,7 -3271,7 +3262,7 @@@ int vprintk_deferred(const char *fmt, v
        return r;
  }
  
 -int printk_deferred(const char *fmt, ...)
 +int _printk_deferred(const char *fmt, ...)
  {
        va_list args;
        int r;
@@@ -3377,14 -3454,12 +3445,12 @@@ bool kmsg_dump_get_line(struct kmsg_dum
        struct printk_info info;
        unsigned int line_count;
        struct printk_record r;
-       unsigned long flags;
        size_t l = 0;
        bool ret = false;
  
        if (iter->cur_seq < min_seq)
                iter->cur_seq = min_seq;
  
-       printk_safe_enter_irqsave(flags);
        prb_rec_init_rd(&r, &info, line, size);
  
        /* Read text or count text lines? */
        iter->cur_seq = r.info->seq + 1;
        ret = true;
  out:
-       printk_safe_exit_irqrestore(flags);
        if (len)
                *len = l;
        return ret;
@@@ -3437,7 -3511,6 +3502,6 @@@ bool kmsg_dump_get_buffer(struct kmsg_d
        u64 min_seq = latched_seq_read_nolock(&clear_seq);
        struct printk_info info;
        struct printk_record r;
-       unsigned long flags;
        u64 seq;
        u64 next_seq;
        size_t len = 0;
        if (iter->cur_seq < min_seq)
                iter->cur_seq = min_seq;
  
-       printk_safe_enter_irqsave(flags);
        if (prb_read_valid_info(prb, iter->cur_seq, &info, NULL)) {
                if (info.seq != iter->cur_seq) {
                        /* messages are gone, move to first available one */
        }
  
        /* last entry */
-       if (iter->cur_seq >= iter->next_seq) {
-               printk_safe_exit_irqrestore(flags);
+       if (iter->cur_seq >= iter->next_seq)
                goto out;
-       }
  
        /*
         * Find first record that fits, including all following records,
  
        iter->next_seq = next_seq;
        ret = true;
-       printk_safe_exit_irqrestore(flags);
  out:
        if (len_out)
                *len_out = len;
@@@ -3512,12 -3581,8 +3572,8 @@@ EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer)
   */
  void kmsg_dump_rewind(struct kmsg_dump_iter *iter)
  {
-       unsigned long flags;
-       printk_safe_enter_irqsave(flags);
        iter->cur_seq = latched_seq_read_nolock(&clear_seq);
        iter->next_seq = prb_next_seq(prb);
-       printk_safe_exit_irqrestore(flags);
  }
  EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
  
diff --combined kernel/trace/trace.c
@@@ -39,7 -39,6 +39,7 @@@
  #include <linux/slab.h>
  #include <linux/ctype.h>
  #include <linux/init.h>
 +#include <linux/panic_notifier.h>
  #include <linux/poll.h>
  #include <linux/nmi.h>
  #include <linux/fs.h>
@@@ -87,7 -86,6 +87,7 @@@ void __init disable_tracing_selftest(co
  /* Pipe tracepoints to printk */
  struct trace_iterator *tracepoint_print_iter;
  int tracepoint_printk;
 +static bool tracepoint_printk_stop_on_boot __initdata;
  static DEFINE_STATIC_KEY_FALSE(tracepoint_printk_key);
  
  /* For tracers that don't implement custom flags */
@@@ -198,12 -196,12 +198,12 @@@ __setup("ftrace=", set_cmdline_ftrace)
  
  static int __init set_ftrace_dump_on_oops(char *str)
  {
 -      if (*str++ != '=' || !*str) {
 +      if (*str++ != '=' || !*str || !strcmp("1", str)) {
                ftrace_dump_on_oops = DUMP_ALL;
                return 1;
        }
  
 -      if (!strcmp("orig_cpu", str)) {
 +      if (!strcmp("orig_cpu", str) || !strcmp("2", str)) {
                ftrace_dump_on_oops = DUMP_ORIG;
                  return 1;
          }
@@@ -258,13 -256,6 +258,13 @@@ static int __init set_tracepoint_printk
  }
  __setup("tp_printk", set_tracepoint_printk);
  
 +static int __init set_tracepoint_printk_stop(char *str)
 +{
 +      tracepoint_printk_stop_on_boot = true;
 +      return 1;
 +}
 +__setup("tp_printk_stop_on_boot", set_tracepoint_printk_stop);
 +
  unsigned long long ns2usecs(u64 nsec)
  {
        nsec += 500;
@@@ -1691,7 -1682,8 +1691,7 @@@ static ssize_t trace_seq_to_buffer(stru
  unsigned long __read_mostly   tracing_thresh;
  static const struct file_operations tracing_max_lat_fops;
  
 -#if (defined(CONFIG_TRACER_MAX_TRACE) || defined(CONFIG_HWLAT_TRACER)) && \
 -      defined(CONFIG_FSNOTIFY)
 +#ifdef LATENCY_FS_NOTIFY
  
  static struct workqueue_struct *fsnotify_wq;
  
@@@ -2192,15 -2184,8 +2192,15 @@@ void tracing_reset_all_online_cpus(void
        }
  }
  
 +/*
 + * The tgid_map array maps from pid to tgid; i.e. the value stored at index i
 + * is the tgid last observed corresponding to pid=i.
 + */
  static int *tgid_map;
  
 +/* The maximum valid index into tgid_map. */
 +static size_t tgid_map_max;
 +
  #define SAVED_CMDLINES_DEFAULT 128
  #define NO_CMDLINE_MAP UINT_MAX
  static arch_spinlock_t trace_cmdline_lock = __ARCH_SPIN_LOCK_UNLOCKED;
@@@ -2473,41 -2458,24 +2473,41 @@@ void trace_find_cmdline(int pid, char c
        preempt_enable();
  }
  
 +static int *trace_find_tgid_ptr(int pid)
 +{
 +      /*
 +       * Pairs with the smp_store_release in set_tracer_flag() to ensure that
 +       * if we observe a non-NULL tgid_map then we also observe the correct
 +       * tgid_map_max.
 +       */
 +      int *map = smp_load_acquire(&tgid_map);
 +
 +      if (unlikely(!map || pid > tgid_map_max))
 +              return NULL;
 +
 +      return &map[pid];
 +}
 +
  int trace_find_tgid(int pid)
  {
 -      if (unlikely(!tgid_map || !pid || pid > PID_MAX_DEFAULT))
 -              return 0;
 +      int *ptr = trace_find_tgid_ptr(pid);
  
 -      return tgid_map[pid];
 +      return ptr ? *ptr : 0;
  }
  
  static int trace_save_tgid(struct task_struct *tsk)
  {
 +      int *ptr;
 +
        /* treat recording of idle task as a success */
        if (!tsk->pid)
                return 1;
  
 -      if (unlikely(!tgid_map || tsk->pid > PID_MAX_DEFAULT))
 +      ptr = trace_find_tgid_ptr(tsk->pid);
 +      if (!ptr)
                return 0;
  
 -      tgid_map[tsk->pid] = tsk->tgid;
 +      *ptr = tsk->tgid;
        return 1;
  }
  
@@@ -2761,45 -2729,9 +2761,45 @@@ trace_event_buffer_lock_reserve(struct 
        if (!tr->no_filter_buffering_ref &&
            (trace_file->flags & (EVENT_FILE_FL_SOFT_DISABLED | EVENT_FILE_FL_FILTERED)) &&
            (entry = this_cpu_read(trace_buffered_event))) {
 -              /* Try to use the per cpu buffer first */
 +              /*
 +               * Filtering is on, so try to use the per cpu buffer first.
 +               * This buffer will simulate a ring_buffer_event,
 +               * where the type_len is zero and the array[0] will
 +               * hold the full length.
 +               * (see include/linux/ring-buffer.h for details on
 +               *  how the ring_buffer_event is structured).
 +               *
 +               * Using a temp buffer during filtering and copying it
 +               * on a matched filter is quicker than writing directly
 +               * into the ring buffer and then discarding it when
 +               * it doesn't match. That is because the discard
 +               * requires several atomic operations to get right.
 +               * Copying on match and doing nothing on a failed match
 +               * is still quicker than no copy on match, but having
 +               * to discard out of the ring buffer on a failed match.
 +               */
 +              int max_len = PAGE_SIZE - struct_size(entry, array, 1);
 +
                val = this_cpu_inc_return(trace_buffered_event_cnt);
 -              if ((len < (PAGE_SIZE - sizeof(*entry) - sizeof(entry->array[0]))) && val == 1) {
 +
 +              /*
 +               * Preemption is disabled, but interrupts and NMIs
 +               * can still come in now. If that happens after
 +               * the above increment, then it will have to go
 +               * back to the old method of allocating the event
 +               * on the ring buffer, and if the filter fails, it
 +               * will have to call ring_buffer_discard_commit()
 +               * to remove it.
 +               *
 +               * Need to also check the unlikely case that the
 +               * length is bigger than the temp buffer size.
 +               * If that happens, then the reserve is pretty much
 +               * guaranteed to fail, as the ring buffer currently
 +               * only allows events less than a page. But that may
 +               * change in the future, so let the ring buffer reserve
 +               * handle the failure in that case.
 +               */
 +              if (val == 1 && likely(len <= max_len)) {
                        trace_event_setup(entry, type, trace_ctx);
                        entry->array[0] = len;
                        return entry;
@@@ -5239,8 -5171,6 +5239,8 @@@ int trace_keep_overwrite(struct tracer 
  
  int set_tracer_flag(struct trace_array *tr, unsigned int mask, int enabled)
  {
 +      int *map;
 +
        if ((mask == TRACE_ITER_RECORD_TGID) ||
            (mask == TRACE_ITER_RECORD_CMD))
                lockdep_assert_held(&event_mutex);
                trace_event_enable_cmd_record(enabled);
  
        if (mask == TRACE_ITER_RECORD_TGID) {
 -              if (!tgid_map)
 -                      tgid_map = kvcalloc(PID_MAX_DEFAULT + 1,
 -                                         sizeof(*tgid_map),
 -                                         GFP_KERNEL);
 +              if (!tgid_map) {
 +                      tgid_map_max = pid_max;
 +                      map = kvcalloc(tgid_map_max + 1, sizeof(*tgid_map),
 +                                     GFP_KERNEL);
 +
 +                      /*
 +                       * Pairs with smp_load_acquire() in
 +                       * trace_find_tgid_ptr() to ensure that if it observes
 +                       * the tgid_map we just allocated then it also observes
 +                       * the corresponding tgid_map_max value.
 +                       */
 +                      smp_store_release(&tgid_map, map);
 +              }
                if (!tgid_map) {
                        tr->trace_flags &= ~TRACE_ITER_RECORD_TGID;
                        return -ENOMEM;
@@@ -5687,16 -5608,37 +5687,16 @@@ static const struct file_operations tra
  
  static void *saved_tgids_next(struct seq_file *m, void *v, loff_t *pos)
  {
 -      int *ptr = v;
 -
 -      if (*pos || m->count)
 -              ptr++;
 -
 -      (*pos)++;
 -
 -      for (; ptr <= &tgid_map[PID_MAX_DEFAULT]; ptr++) {
 -              if (trace_find_tgid(*ptr))
 -                      return ptr;
 -      }
 +      int pid = ++(*pos);
  
 -      return NULL;
 +      return trace_find_tgid_ptr(pid);
  }
  
  static void *saved_tgids_start(struct seq_file *m, loff_t *pos)
  {
 -      void *v;
 -      loff_t l = 0;
 -
 -      if (!tgid_map)
 -              return NULL;
 +      int pid = *pos;
  
 -      v = &tgid_map[0];
 -      while (l <= *pos) {
 -              v = saved_tgids_next(m, v, &l);
 -              if (!v)
 -                      return NULL;
 -      }
 -
 -      return v;
 +      return trace_find_tgid_ptr(pid);
  }
  
  static void saved_tgids_stop(struct seq_file *m, void *v)
  
  static int saved_tgids_show(struct seq_file *m, void *v)
  {
 -      int pid = (int *)v - tgid_map;
 +      int *entry = (int *)v;
 +      int pid = entry - tgid_map;
 +      int tgid = *entry;
  
 -      seq_printf(m, "%d %d\n", pid, trace_find_tgid(pid));
 +      if (tgid == 0)
 +              return SEQ_SKIP;
 +
 +      seq_printf(m, "%d %d\n", pid, tgid);
        return 0;
  }
  
@@@ -6197,7 -6134,7 +6197,7 @@@ static int __tracing_resize_ring_buffer
  ssize_t tracing_resize_ring_buffer(struct trace_array *tr,
                                  unsigned long size, int cpu_id)
  {
 -      int ret = size;
 +      int ret;
  
        mutex_lock(&trace_types_lock);
  
@@@ -7591,91 -7528,6 +7591,91 @@@ static const struct file_operations sna
  
  #endif /* CONFIG_TRACER_SNAPSHOT */
  
 +/*
 + * trace_min_max_write - Write a u64 value to a trace_min_max_param struct
 + * @filp: The active open file structure
 + * @ubuf: The userspace provided buffer to read value into
 + * @cnt: The maximum number of bytes to read
 + * @ppos: The current "file" position
 + *
 + * This function implements the write interface for a struct trace_min_max_param.
 + * The filp->private_data must point to a trace_min_max_param structure that
 + * defines where to write the value, the min and the max acceptable values,
 + * and a lock to protect the write.
 + */
 +static ssize_t
 +trace_min_max_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos)
 +{
 +      struct trace_min_max_param *param = filp->private_data;
 +      u64 val;
 +      int err;
 +
 +      if (!param)
 +              return -EFAULT;
 +
 +      err = kstrtoull_from_user(ubuf, cnt, 10, &val);
 +      if (err)
 +              return err;
 +
 +      if (param->lock)
 +              mutex_lock(param->lock);
 +
 +      if (param->min && val < *param->min)
 +              err = -EINVAL;
 +
 +      if (param->max && val > *param->max)
 +              err = -EINVAL;
 +
 +      if (!err)
 +              *param->val = val;
 +
 +      if (param->lock)
 +              mutex_unlock(param->lock);
 +
 +      if (err)
 +              return err;
 +
 +      return cnt;
 +}
 +
 +/*
 + * trace_min_max_read - Read a u64 value from a trace_min_max_param struct
 + * @filp: The active open file structure
 + * @ubuf: The userspace provided buffer to read value into
 + * @cnt: The maximum number of bytes to read
 + * @ppos: The current "file" position
 + *
 + * This function implements the read interface for a struct trace_min_max_param.
 + * The filp->private_data must point to a trace_min_max_param struct with valid
 + * data.
 + */
 +static ssize_t
 +trace_min_max_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos)
 +{
 +      struct trace_min_max_param *param = filp->private_data;
 +      char buf[U64_STR_SIZE];
 +      int len;
 +      u64 val;
 +
 +      if (!param)
 +              return -EFAULT;
 +
 +      val = *param->val;
 +
 +      if (cnt > sizeof(buf))
 +              cnt = sizeof(buf);
 +
 +      len = snprintf(buf, sizeof(buf), "%llu\n", val);
 +
 +      return simple_read_from_buffer(ubuf, cnt, ppos, buf, len);
 +}
 +
 +const struct file_operations trace_min_max_fops = {
 +      .open           = tracing_open_generic,
 +      .read           = trace_min_max_read,
 +      .write          = trace_min_max_write,
 +};
 +
  #define TRACING_LOG_ERRS_MAX  8
  #define TRACING_LOG_LOC_MAX   128
  
@@@ -9679,8 -9531,6 +9679,8 @@@ static __init int tracer_init_tracefs(v
        return 0;
  }
  
 +fs_initcall(tracer_init_tracefs);
 +
  static int trace_panic_handler(struct notifier_block *this,
                               unsigned long event, void *unused)
  {
@@@ -9797,7 -9647,6 +9797,6 @@@ void ftrace_dump(enum ftrace_dump_mode 
        tracing_off();
  
        local_irq_save(flags);
-       printk_nmi_direct_enter();
  
        /* Simulate the iterator */
        trace_init_global_iter(&iter);
                atomic_dec(&per_cpu_ptr(iter.array_buffer->data, cpu)->disabled);
        }
        atomic_dec(&dump_running);
-       printk_nmi_direct_exit();
        local_irq_restore(flags);
  }
  EXPORT_SYMBOL_GPL(ftrace_dump);
@@@ -10101,7 -9949,7 +10099,7 @@@ void __init trace_init(void
        trace_event_init();
  }
  
 -__init static int clear_boot_tracer(void)
 +__init static void clear_boot_tracer(void)
  {
        /*
         * The default tracer at boot buffer is an init section.
         * about to be freed.
         */
        if (!default_bootup_tracer)
 -              return 0;
 +              return;
  
        printk(KERN_INFO "ftrace bootup tracer '%s' not registered.\n",
               default_bootup_tracer);
        default_bootup_tracer = NULL;
 -
 -      return 0;
  }
  
 -fs_initcall(tracer_init_tracefs);
 -late_initcall_sync(clear_boot_tracer);
 -
  #ifdef CONFIG_HAVE_UNSTABLE_SCHED_CLOCK
 -__init static int tracing_set_default_clock(void)
 +__init static void tracing_set_default_clock(void)
  {
        /* sched_clock_stable() is determined in late_initcall */
        if (!trace_boot_clock && !sched_clock_stable()) {
                if (security_locked_down(LOCKDOWN_TRACEFS)) {
                        pr_warn("Can not set tracing clock due to lockdown\n");
 -                      return -EPERM;
 +                      return;
                }
  
                printk(KERN_WARNING
                       "on the kernel command line\n");
                tracing_set_clock(&global_trace, "global");
        }
 +}
 +#else
 +static inline void tracing_set_default_clock(void) { }
 +#endif
 +
 +__init static int late_trace_init(void)
 +{
 +      if (tracepoint_printk && tracepoint_printk_stop_on_boot) {
 +              static_key_disable(&tracepoint_printk_key.key);
 +              tracepoint_printk = 0;
 +      }
  
 +      tracing_set_default_clock();
 +      clear_boot_tracer();
        return 0;
  }
 -late_initcall_sync(tracing_set_default_clock);
 -#endif
 +
 +late_initcall_sync(late_trace_init);