@@ -1940,6 +1940,76 @@ static void call_console_drivers(const char *ext_text, size_t ext_len,
19401940 }
19411941}
19421942
1943+ /*
1944+ * Recursion is tracked separately on each CPU. If NMIs are supported, an
1945+ * additional NMI context per CPU is also separately tracked. Until per-CPU
1946+ * is available, a separate "early tracking" is performed.
1947+ */
1948+ static DEFINE_PER_CPU (u8 , printk_count ) ;
1949+ static u8 printk_count_early ;
1950+ #ifdef CONFIG_HAVE_NMI
1951+ static DEFINE_PER_CPU (u8 , printk_count_nmi ) ;
1952+ static u8 printk_count_nmi_early ;
1953+ #endif
1954+
1955+ /*
1956+ * Recursion is limited to keep the output sane. printk() should not require
1957+ * more than 1 level of recursion (allowing, for example, printk() to trigger
1958+ * a WARN), but a higher value is used in case some printk-internal errors
1959+ * exist, such as the ringbuffer validation checks failing.
1960+ */
1961+ #define PRINTK_MAX_RECURSION 3
1962+
1963+ /*
1964+ * Return a pointer to the dedicated counter for the CPU+context of the
1965+ * caller.
1966+ */
1967+ static u8 * __printk_recursion_counter (void )
1968+ {
1969+ #ifdef CONFIG_HAVE_NMI
1970+ if (in_nmi ()) {
1971+ if (printk_percpu_data_ready ())
1972+ return this_cpu_ptr (& printk_count_nmi );
1973+ return & printk_count_nmi_early ;
1974+ }
1975+ #endif
1976+ if (printk_percpu_data_ready ())
1977+ return this_cpu_ptr (& printk_count );
1978+ return & printk_count_early ;
1979+ }
1980+
1981+ /*
1982+ * Enter recursion tracking. Interrupts are disabled to simplify tracking.
1983+ * The caller must check the boolean return value to see if the recursion is
1984+ * allowed. On failure, interrupts are not disabled.
1985+ *
1986+ * @recursion_ptr must be a variable of type (u8 *) and is the same variable
1987+ * that is passed to printk_exit_irqrestore().
1988+ */
1989+ #define printk_enter_irqsave (recursion_ptr , flags ) \
1990+ ({ \
1991+ bool success = true; \
1992+ \
1993+ typecheck(u8 *, recursion_ptr); \
1994+ local_irq_save(flags); \
1995+ (recursion_ptr) = __printk_recursion_counter(); \
1996+ if (*(recursion_ptr) > PRINTK_MAX_RECURSION) { \
1997+ local_irq_restore(flags); \
1998+ success = false; \
1999+ } else { \
2000+ (*(recursion_ptr))++; \
2001+ } \
2002+ success; \
2003+ })
2004+
2005+ /* Exit recursion tracking, restoring interrupts. */
2006+ #define printk_exit_irqrestore (recursion_ptr , flags ) \
2007+ do { \
2008+ typecheck(u8 *, recursion_ptr); \
2009+ (*(recursion_ptr))--; \
2010+ local_irq_restore(flags); \
2011+ } while (0)
2012+
19432013int printk_delay_msec __read_mostly ;
19442014
19452015static inline void printk_delay (void )
@@ -2040,11 +2110,14 @@ int vprintk_store(int facility, int level,
20402110 struct prb_reserved_entry e ;
20412111 enum log_flags lflags = 0 ;
20422112 struct printk_record r ;
2113+ unsigned long irqflags ;
20432114 u16 trunc_msg_len = 0 ;
20442115 char prefix_buf [8 ];
2116+ u8 * recursion_ptr ;
20452117 u16 reserve_size ;
20462118 va_list args2 ;
20472119 u16 text_len ;
2120+ int ret = 0 ;
20482121 u64 ts_nsec ;
20492122
20502123 /*
@@ -2055,6 +2128,9 @@ int vprintk_store(int facility, int level,
20552128 */
20562129 ts_nsec = local_clock ();
20572130
2131+ if (!printk_enter_irqsave (recursion_ptr , irqflags ))
2132+ return 0 ;
2133+
20582134 /*
20592135 * The sprintf needs to come first since the syslog prefix might be
20602136 * passed in as a parameter. An extra byte must be reserved so that
@@ -2092,7 +2168,8 @@ int vprintk_store(int facility, int level,
20922168 prb_commit (& e );
20932169 }
20942170
2095- return text_len ;
2171+ ret = text_len ;
2172+ goto out ;
20962173 }
20972174 }
20982175
@@ -2108,7 +2185,7 @@ int vprintk_store(int facility, int level,
21082185
21092186 prb_rec_init_wr (& r , reserve_size + trunc_msg_len );
21102187 if (!prb_reserve (& e , prb , & r ))
2111- return 0 ;
2188+ goto out ;
21122189 }
21132190
21142191 /* fill message */
@@ -2130,7 +2207,10 @@ int vprintk_store(int facility, int level,
21302207 else
21312208 prb_final_commit (& e );
21322209
2133- return (text_len + trunc_msg_len );
2210+ ret = text_len + trunc_msg_len ;
2211+ out :
2212+ printk_exit_irqrestore (recursion_ptr , irqflags );
2213+ return ret ;
21342214}
21352215
21362216asmlinkage int vprintk_emit (int facility , int level ,
0 commit comments