diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index af1fdb01e458a2e9769e736c62b68282072b7a85..ca3955b92a2403db2d9fc6f16dd419b13648c95a 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -3244,6 +3245,8 @@ void serial8250_console_write(struct uart_8250_port *up, const char *s, if (port->sysrq || oops_in_progress) locked = 0; + else if (in_kdb_printk()) + locked = spin_trylock_irqsave(&port->lock, flags); else spin_lock_irqsave(&port->lock, flags); diff --git a/include/linux/kdb.h b/include/linux/kdb.h index 68bd88223417c058d7a319790993fe8691800e68..e033b25b0b72549944c866f1d56666bfd6ccdfc4 100644 --- a/include/linux/kdb.h +++ b/include/linux/kdb.h @@ -167,6 +167,7 @@ extern __printf(2, 0) int vkdb_printf(enum kdb_msgsrc src, const char *fmt, extern __printf(1, 2) int kdb_printf(const char *, ...); typedef __printf(1, 2) int (*kdb_printf_t)(const char *, ...); +#define in_kdb_printk() (kdb_trap_printk) extern void kdb_init(int level); /* Access to kdb specific polling devices */ @@ -201,6 +202,7 @@ extern int kdb_register_flags(char *, kdb_func_t, char *, char *, extern int kdb_unregister(char *); #else /* ! CONFIG_KGDB_KDB */ static inline __printf(1, 2) int kdb_printf(const char *fmt, ...) { return 0; } +#define in_kdb_printk() (0) static inline void kdb_init(int level) {} static inline int kdb_register(char *cmd, kdb_func_t func, char *usage, char *help, short minlen) { return 0; } diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index 6a4b41484afe654572f3b4f37186046c4541c8e5..197cb422f6e15228e5fd93afdc14a11060907b09 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -857,9 +857,11 @@ int kdb_printf(const char *fmt, ...) va_list ap; int r; + kdb_trap_printk++; va_start(ap, fmt); r = vkdb_printf(KDB_MSGSRC_INTERNAL, fmt, ap); va_end(ap); + kdb_trap_printk--; return r; } diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 57b61801f2d150dc22644385cccd0a1dad4ec85a..1a61989c4721c94b6bf33668274e9feeb29f86d1 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -1438,12 +1438,23 @@ static int syslog_print_all(char __user *buf, int size, bool clear) u64 next_seq; u64 seq; u32 idx; + int attempts = 0; + int num_msg; text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL); if (!text) return -ENOMEM; logbuf_lock_irq(); + +try_again: + attempts++; + if (attempts > 10) { + len = -EBUSY; + goto out; + } + num_msg = 0; + /* * Find first record that fits, including all following records, * into the user-provided buffer for this dump. @@ -1456,6 +1467,14 @@ static int syslog_print_all(char __user *buf, int size, bool clear) len += msg_print_text(msg, true, NULL, 0); idx = log_next(idx); seq++; + num_msg++; + if (num_msg > 5) { + num_msg = 0; + logbuf_unlock_irq(); + logbuf_lock_irq(); + if (clear_seq < log_first_seq) + goto try_again; + } } /* move first record forward until length fits into the buffer */ @@ -1467,6 +1486,14 @@ static int syslog_print_all(char __user *buf, int size, bool clear) len -= msg_print_text(msg, true, NULL, 0); idx = log_next(idx); seq++; + num_msg++; + if (num_msg > 5) { + num_msg = 0; + logbuf_unlock_irq(); + logbuf_lock_irq(); + if (clear_seq < log_first_seq) + goto try_again; + } } /* last message fitting into this dump */ @@ -1504,6 +1531,7 @@ static int syslog_print_all(char __user *buf, int size, bool clear) clear_seq = log_next_seq; clear_idx = log_next_idx; } +out: logbuf_unlock_irq(); kfree(text); @@ -1635,6 +1663,7 @@ SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len) return do_syslog(type, buf, len, SYSLOG_FROM_READER); } +#ifndef CONFIG_PREEMPT_RT_FULL /* * Special console_lock variants that help to reduce the risk of soft-lockups. * They allow to pass console_lock to another printk() call using a busy wait. @@ -1785,6 +1814,15 @@ static int console_trylock_spinning(void) return 1; } +#else + +static int console_trylock_spinning(void) +{ + return console_trylock(); +} + +#endif + /* * Call the console drivers, asking them to write out * log_buf[start] to log_buf[end - 1]. @@ -1800,6 +1838,12 @@ static void call_console_drivers(const char *ext_text, size_t ext_len, if (!console_drivers) return; + if (IS_ENABLED(CONFIG_PREEMPT_RT_BASE)) { + if (in_irq() || in_nmi()) + return; + } + + migrate_disable(); for_each_console(con) { if (exclusive_console && con != exclusive_console) continue; @@ -1815,6 +1859,7 @@ static void call_console_drivers(const char *ext_text, size_t ext_len, else con->write(con, text, len); } + migrate_enable(); } int printk_delay_msec __read_mostly; @@ -2013,20 +2058,30 @@ asmlinkage int vprintk_emit(int facility, int level, /* If called from the scheduler, we can not call up(). */ if (!in_sched && pending_output) { + int may_trylock = 1; + +#ifdef CONFIG_PREEMPT_RT_FULL + /* + * we can't take a sleeping lock with IRQs or preeption disabled + * so we can't print in these contexts + */ + if (!(preempt_count() == 0 && !irqs_disabled())) + may_trylock = 0; +#endif /* * Disable preemption to avoid being preempted while holding * console_sem which would prevent anyone from printing to * console */ - preempt_disable(); + migrate_disable(); /* * Try to acquire and then immediately release the console * semaphore. The release will print out buffers and wake up * /dev/kmsg and syslog() users. */ - if (console_trylock_spinning()) + if (may_trylock && console_trylock_spinning()) console_unlock(); - preempt_enable(); + migrate_enable(); } if (pending_output) @@ -2512,6 +2567,10 @@ void console_unlock(void) console_seq++; raw_spin_unlock(&logbuf_lock); +#ifdef CONFIG_PREEMPT_RT_FULL + printk_safe_exit_irqrestore(flags); + call_console_drivers(ext_text, ext_len, text, len); +#else /* * While actively printing out messages, if another printk() * were to occur on another CPU, it may wait for this one to @@ -2530,6 +2589,7 @@ void console_unlock(void) } printk_safe_exit_irqrestore(flags); +#endif /* Allow panic_cpu to take over the consoles safely */ if (abandon_console_lock_in_panic()) @@ -2581,6 +2641,11 @@ void console_unblank(void) { struct console *c; + if (IS_ENABLED(CONFIG_PREEMPT_RT_BASE)) { + if (in_irq() || in_nmi()) + return; + } + /* * console_unblank can no longer be called in interrupt context unless * oops_in_progress is set to 1..