Loading include/linux/console.h +3 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,8 @@ static inline int con_debug_leave(void) * receiving the printk spam for obvious reasons. * @CON_EXTENDED: The console supports the extended output format of * /dev/kmesg which requires a larger output buffer. * @CON_SUSPENDED: Indicates if a console is suspended. If true, the * printing callbacks must not be called. */ enum cons_flags { CON_PRINTBUFFER = BIT(0), Loading @@ -163,6 +165,7 @@ enum cons_flags { CON_ANYTIME = BIT(4), CON_BRL = BIT(5), CON_EXTENDED = BIT(6), CON_SUSPENDED = BIT(7), }; /** Loading kernel/debug/kdb/kdb_io.c +2 −0 Original line number Diff line number Diff line Loading @@ -576,6 +576,8 @@ static void kdb_msg_write(const char *msg, int msg_len) continue; if (c == dbg_io_ops->cons) continue; if (!c->write) continue; /* * Set oops_in_progress to encourage the console drivers to * disregard their internal spin locks: in the current calling Loading kernel/printk/internal.h +2 −0 Original line number Diff line number Diff line Loading @@ -103,3 +103,5 @@ struct printk_message { u64 seq; unsigned long dropped; }; bool other_cpu_in_panic(void); kernel/printk/printk.c +147 −66 Original line number Diff line number Diff line Loading @@ -86,7 +86,7 @@ EXPORT_SYMBOL(oops_in_progress); static DEFINE_MUTEX(console_mutex); /* * console_sem protects updates to console->seq and console_suspended, * console_sem protects updates to console->seq * and also provides serialization for console printing. */ static DEFINE_SEMAPHORE(console_sem); Loading Loading @@ -359,7 +359,7 @@ static bool panic_in_progress(void) * paths in the console code where we end up in places I want * locked without the console semaphore held). */ static int console_locked, console_suspended; static int console_locked; /* * Array of consoles built from command line options (console=) Loading Loading @@ -2308,7 +2308,11 @@ asmlinkage int vprintk_emit(int facility, int level, preempt_enable(); } if (in_sched) defer_console_output(); else wake_up_klogd(); return printed_len; } EXPORT_SYMBOL(vprintk_emit); Loading Loading @@ -2547,22 +2551,46 @@ MODULE_PARM_DESC(console_no_auto_verbose, "Disable console loglevel raise to hig */ void suspend_console(void) { struct console *con; if (!console_suspend_enabled) return; pr_info("Suspending console(s) (use no_console_suspend to debug)\n"); pr_flush(1000, true); console_lock(); console_suspended = 1; up_console_sem(); console_list_lock(); for_each_console(con) console_srcu_write_flags(con, con->flags | CON_SUSPENDED); console_list_unlock(); /* * Ensure that all SRCU list walks have completed. All printing * contexts must be able to see that they are suspended so that it * is guaranteed that all printing has stopped when this function * completes. */ synchronize_srcu(&console_srcu); } void resume_console(void) { struct console *con; if (!console_suspend_enabled) return; down_console_sem(); console_suspended = 0; console_unlock(); console_list_lock(); for_each_console(con) console_srcu_write_flags(con, con->flags & ~CON_SUSPENDED); console_list_unlock(); /* * Ensure that all SRCU list walks have completed. All printing * contexts must be able to see they are no longer suspended so * that they are guaranteed to wake up and resume printing. */ synchronize_srcu(&console_srcu); pr_flush(1000, true); } Loading @@ -2585,6 +2613,26 @@ static int console_cpu_notify(unsigned int cpu) return 0; } /* * Return true if a panic is in progress on a remote CPU. * * On true, the local CPU should immediately release any printing resources * that may be needed by the panic CPU. */ bool other_cpu_in_panic(void) { if (!panic_in_progress()) return false; /* * We can use raw_smp_processor_id() here because it is impossible for * the task to be migrated to the panic_cpu, or away from it. If * panic_cpu has already been set, and we're not currently executing on * that CPU, then we never will be. */ return atomic_read(&panic_cpu) != raw_smp_processor_id(); } /** * console_lock - block the console subsystem from printing * Loading @@ -2597,9 +2645,11 @@ void console_lock(void) { might_sleep(); /* On panic, the console_lock must be left to the panic cpu. */ while (other_cpu_in_panic()) msleep(1000); down_console_sem(); if (console_suspended) return; console_locked = 1; console_may_schedule = 1; } Loading @@ -2615,12 +2665,11 @@ EXPORT_SYMBOL(console_lock); */ int console_trylock(void) { if (down_trylock_console_sem()) /* On panic, the console_lock must be left to the panic cpu. */ if (other_cpu_in_panic()) return 0; if (console_suspended) { up_console_sem(); if (down_trylock_console_sem()) return 0; } console_locked = 1; console_may_schedule = 0; return 1; Loading @@ -2633,25 +2682,6 @@ int is_console_locked(void) } EXPORT_SYMBOL(is_console_locked); /* * Return true when this CPU should unlock console_sem without pushing all * messages to the console. This reduces the chance that the console is * locked when the panic CPU tries to use it. */ static bool abandon_console_lock_in_panic(void) { if (!panic_in_progress()) return false; /* * We can use raw_smp_processor_id() here because it is impossible for * the task to be migrated to the panic_cpu, or away from it. If * panic_cpu has already been set, and we're not currently executing on * that CPU, then we never will be. */ return atomic_read(&panic_cpu) != raw_smp_processor_id(); } /* * Check if the given console is currently capable and allowed to print * records. Loading @@ -2665,6 +2695,9 @@ static inline bool console_is_usable(struct console *con) if (!(flags & CON_ENABLED)) return false; if ((flags & CON_SUSPENDED)) return false; if (!con->write) return false; Loading Loading @@ -2948,7 +2981,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove any_progress = true; /* Allow panic_cpu to take over the consoles safely. */ if (abandon_console_lock_in_panic()) if (other_cpu_in_panic()) goto abandon; if (do_cond_resched) Loading Loading @@ -2983,11 +3016,6 @@ void console_unlock(void) bool flushed; u64 next_seq; if (console_suspended) { up_console_sem(); return; } /* * Console drivers are called with interrupts disabled, so * @console_may_schedule should be cleared before; however, we may Loading Loading @@ -3045,9 +3073,27 @@ EXPORT_SYMBOL(console_conditional_schedule); void console_unblank(void) { bool found_unblank = false; struct console *c; int cookie; /* * First check if there are any consoles implementing the unblank() * callback. If not, there is no reason to continue and take the * console lock, which in particular can be dangerous if * @oops_in_progress is set. */ cookie = console_srcu_read_lock(); for_each_console_srcu(c) { if ((console_srcu_read_flags(c) & CON_ENABLED) && c->unblank) { found_unblank = true; break; } } console_srcu_read_unlock(cookie); if (!found_unblank) return; /* * Stop console printing because the unblank() callback may * assume the console is not within its write() callback. Loading @@ -3056,6 +3102,16 @@ void console_unblank(void) * In that case, attempt a trylock as best-effort. */ if (oops_in_progress) { /* Semaphores are not NMI-safe. */ if (in_nmi()) return; /* * Attempting to trylock the console lock can deadlock * if another CPU was stopped while modifying the * semaphore. "Hope and pray" that this is not the * current situation. */ if (down_trylock_console_sem() != 0) return; } else Loading Loading @@ -3085,14 +3141,24 @@ void console_unblank(void) */ void console_flush_on_panic(enum con_flush_mode mode) { bool handover; u64 next_seq; /* * If someone else is holding the console lock, trylock will fail * and may_schedule may be set. Ignore and proceed to unlock so * that messages are flushed out. As this can be called from any * context and we don't want to get preempted while flushing, * ensure may_schedule is cleared. * Ignore the console lock and flush out the messages. Attempting a * trylock would not be useful because: * * - if it is contended, it must be ignored anyway * - console_lock() and console_trylock() block and fail * respectively in panic for non-panic CPUs * - semaphores are not NMI-safe */ /* * If another context is holding the console lock, * @console_may_schedule might be set. Clear it so that * this context does not call cond_resched() while flushing. */ console_trylock(); console_may_schedule = 0; if (mode == CONSOLE_REPLAY_ALL) { Loading @@ -3105,15 +3171,15 @@ void console_flush_on_panic(enum con_flush_mode mode) cookie = console_srcu_read_lock(); for_each_console_srcu(c) { /* * If the above console_trylock() failed, this is an * unsynchronized assignment. But in that case, the * This is an unsynchronized assignment, but the * kernel is in "hope and pray" mode anyway. */ c->seq = seq; } console_srcu_read_unlock(cookie); } console_unlock(); console_flush_all(false, &next_seq, &handover); } /* Loading Loading @@ -3679,8 +3745,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre /* * Hold the console_lock to guarantee safe access to * console->seq and to prevent changes to @console_suspended * until all consoles have been processed. * console->seq. */ console_lock(); Loading @@ -3688,6 +3753,11 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre for_each_console_srcu(c) { if (con && con != c) continue; /* * If consoles are not usable, it cannot be expected * that they make forward progress, so only increment * @diff for usable consoles. */ if (!console_is_usable(c)) continue; printk_seq = c->seq; Loading @@ -3696,18 +3766,12 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre } console_srcu_read_unlock(cookie); /* * If consoles are suspended, it cannot be expected that they * make forward progress, so timeout immediately. @diff is * still used to return a valid flush status. */ if (console_suspended) remaining = 0; else if (diff != last_diff && reset_on_progress) if (diff != last_diff && reset_on_progress) remaining = timeout_ms; console_unlock(); /* Note: @diff is 0 if there are no usable consoles. */ if (diff == 0 || remaining == 0) break; Loading Loading @@ -3741,7 +3805,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre * printer has been seen to make some forward progress. * * Context: Process context. May sleep while acquiring console lock. * Return: true if all enabled printers are caught up. * Return: true if all usable printers are caught up. */ static bool pr_flush(int timeout_ms, bool reset_on_progress) { Loading Loading @@ -3798,11 +3862,33 @@ static void __wake_up_klogd(int val) preempt_enable(); } /** * wake_up_klogd - Wake kernel logging daemon * * Use this function when new records have been added to the ringbuffer * and the console printing of those records has already occurred or is * known to be handled by some other context. This function will only * wake the logging daemon. * * Context: Any context. */ void wake_up_klogd(void) { __wake_up_klogd(PRINTK_PENDING_WAKEUP); } /** * defer_console_output - Wake kernel logging daemon and trigger * console printing in a deferred context * * Use this function when new records have been added to the ringbuffer, * this context is responsible for console printing those records, but * the current context is not allowed to perform the console printing. * Trigger an irq_work context to perform the console printing. This * function also wakes the logging daemon. * * Context: Any context. */ void defer_console_output(void) { /* Loading @@ -3819,12 +3905,7 @@ void printk_trigger_flush(void) int vprintk_deferred(const char *fmt, va_list args) { int r; r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args); defer_console_output(); return r; return vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args); } int _printk_deferred(const char *fmt, ...) Loading kernel/printk/printk_safe.c +2 −7 Original line number Diff line number Diff line Loading @@ -38,13 +38,8 @@ asmlinkage int vprintk(const char *fmt, va_list args) * Use the main logbuf even in NMI. But avoid calling console * drivers that might have their own locks. */ if (this_cpu_read(printk_context) || in_nmi()) { int len; len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, fmt, args); defer_console_output(); return len; } if (this_cpu_read(printk_context) || in_nmi()) return vprintk_deferred(fmt, args); /* No obstacles. */ return vprintk_default(fmt, args); Loading Loading
include/linux/console.h +3 −0 Original line number Diff line number Diff line Loading @@ -154,6 +154,8 @@ static inline int con_debug_leave(void) * receiving the printk spam for obvious reasons. * @CON_EXTENDED: The console supports the extended output format of * /dev/kmesg which requires a larger output buffer. * @CON_SUSPENDED: Indicates if a console is suspended. If true, the * printing callbacks must not be called. */ enum cons_flags { CON_PRINTBUFFER = BIT(0), Loading @@ -163,6 +165,7 @@ enum cons_flags { CON_ANYTIME = BIT(4), CON_BRL = BIT(5), CON_EXTENDED = BIT(6), CON_SUSPENDED = BIT(7), }; /** Loading
kernel/debug/kdb/kdb_io.c +2 −0 Original line number Diff line number Diff line Loading @@ -576,6 +576,8 @@ static void kdb_msg_write(const char *msg, int msg_len) continue; if (c == dbg_io_ops->cons) continue; if (!c->write) continue; /* * Set oops_in_progress to encourage the console drivers to * disregard their internal spin locks: in the current calling Loading
kernel/printk/internal.h +2 −0 Original line number Diff line number Diff line Loading @@ -103,3 +103,5 @@ struct printk_message { u64 seq; unsigned long dropped; }; bool other_cpu_in_panic(void);
kernel/printk/printk.c +147 −66 Original line number Diff line number Diff line Loading @@ -86,7 +86,7 @@ EXPORT_SYMBOL(oops_in_progress); static DEFINE_MUTEX(console_mutex); /* * console_sem protects updates to console->seq and console_suspended, * console_sem protects updates to console->seq * and also provides serialization for console printing. */ static DEFINE_SEMAPHORE(console_sem); Loading Loading @@ -359,7 +359,7 @@ static bool panic_in_progress(void) * paths in the console code where we end up in places I want * locked without the console semaphore held). */ static int console_locked, console_suspended; static int console_locked; /* * Array of consoles built from command line options (console=) Loading Loading @@ -2308,7 +2308,11 @@ asmlinkage int vprintk_emit(int facility, int level, preempt_enable(); } if (in_sched) defer_console_output(); else wake_up_klogd(); return printed_len; } EXPORT_SYMBOL(vprintk_emit); Loading Loading @@ -2547,22 +2551,46 @@ MODULE_PARM_DESC(console_no_auto_verbose, "Disable console loglevel raise to hig */ void suspend_console(void) { struct console *con; if (!console_suspend_enabled) return; pr_info("Suspending console(s) (use no_console_suspend to debug)\n"); pr_flush(1000, true); console_lock(); console_suspended = 1; up_console_sem(); console_list_lock(); for_each_console(con) console_srcu_write_flags(con, con->flags | CON_SUSPENDED); console_list_unlock(); /* * Ensure that all SRCU list walks have completed. All printing * contexts must be able to see that they are suspended so that it * is guaranteed that all printing has stopped when this function * completes. */ synchronize_srcu(&console_srcu); } void resume_console(void) { struct console *con; if (!console_suspend_enabled) return; down_console_sem(); console_suspended = 0; console_unlock(); console_list_lock(); for_each_console(con) console_srcu_write_flags(con, con->flags & ~CON_SUSPENDED); console_list_unlock(); /* * Ensure that all SRCU list walks have completed. All printing * contexts must be able to see they are no longer suspended so * that they are guaranteed to wake up and resume printing. */ synchronize_srcu(&console_srcu); pr_flush(1000, true); } Loading @@ -2585,6 +2613,26 @@ static int console_cpu_notify(unsigned int cpu) return 0; } /* * Return true if a panic is in progress on a remote CPU. * * On true, the local CPU should immediately release any printing resources * that may be needed by the panic CPU. */ bool other_cpu_in_panic(void) { if (!panic_in_progress()) return false; /* * We can use raw_smp_processor_id() here because it is impossible for * the task to be migrated to the panic_cpu, or away from it. If * panic_cpu has already been set, and we're not currently executing on * that CPU, then we never will be. */ return atomic_read(&panic_cpu) != raw_smp_processor_id(); } /** * console_lock - block the console subsystem from printing * Loading @@ -2597,9 +2645,11 @@ void console_lock(void) { might_sleep(); /* On panic, the console_lock must be left to the panic cpu. */ while (other_cpu_in_panic()) msleep(1000); down_console_sem(); if (console_suspended) return; console_locked = 1; console_may_schedule = 1; } Loading @@ -2615,12 +2665,11 @@ EXPORT_SYMBOL(console_lock); */ int console_trylock(void) { if (down_trylock_console_sem()) /* On panic, the console_lock must be left to the panic cpu. */ if (other_cpu_in_panic()) return 0; if (console_suspended) { up_console_sem(); if (down_trylock_console_sem()) return 0; } console_locked = 1; console_may_schedule = 0; return 1; Loading @@ -2633,25 +2682,6 @@ int is_console_locked(void) } EXPORT_SYMBOL(is_console_locked); /* * Return true when this CPU should unlock console_sem without pushing all * messages to the console. This reduces the chance that the console is * locked when the panic CPU tries to use it. */ static bool abandon_console_lock_in_panic(void) { if (!panic_in_progress()) return false; /* * We can use raw_smp_processor_id() here because it is impossible for * the task to be migrated to the panic_cpu, or away from it. If * panic_cpu has already been set, and we're not currently executing on * that CPU, then we never will be. */ return atomic_read(&panic_cpu) != raw_smp_processor_id(); } /* * Check if the given console is currently capable and allowed to print * records. Loading @@ -2665,6 +2695,9 @@ static inline bool console_is_usable(struct console *con) if (!(flags & CON_ENABLED)) return false; if ((flags & CON_SUSPENDED)) return false; if (!con->write) return false; Loading Loading @@ -2948,7 +2981,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove any_progress = true; /* Allow panic_cpu to take over the consoles safely. */ if (abandon_console_lock_in_panic()) if (other_cpu_in_panic()) goto abandon; if (do_cond_resched) Loading Loading @@ -2983,11 +3016,6 @@ void console_unlock(void) bool flushed; u64 next_seq; if (console_suspended) { up_console_sem(); return; } /* * Console drivers are called with interrupts disabled, so * @console_may_schedule should be cleared before; however, we may Loading Loading @@ -3045,9 +3073,27 @@ EXPORT_SYMBOL(console_conditional_schedule); void console_unblank(void) { bool found_unblank = false; struct console *c; int cookie; /* * First check if there are any consoles implementing the unblank() * callback. If not, there is no reason to continue and take the * console lock, which in particular can be dangerous if * @oops_in_progress is set. */ cookie = console_srcu_read_lock(); for_each_console_srcu(c) { if ((console_srcu_read_flags(c) & CON_ENABLED) && c->unblank) { found_unblank = true; break; } } console_srcu_read_unlock(cookie); if (!found_unblank) return; /* * Stop console printing because the unblank() callback may * assume the console is not within its write() callback. Loading @@ -3056,6 +3102,16 @@ void console_unblank(void) * In that case, attempt a trylock as best-effort. */ if (oops_in_progress) { /* Semaphores are not NMI-safe. */ if (in_nmi()) return; /* * Attempting to trylock the console lock can deadlock * if another CPU was stopped while modifying the * semaphore. "Hope and pray" that this is not the * current situation. */ if (down_trylock_console_sem() != 0) return; } else Loading Loading @@ -3085,14 +3141,24 @@ void console_unblank(void) */ void console_flush_on_panic(enum con_flush_mode mode) { bool handover; u64 next_seq; /* * If someone else is holding the console lock, trylock will fail * and may_schedule may be set. Ignore and proceed to unlock so * that messages are flushed out. As this can be called from any * context and we don't want to get preempted while flushing, * ensure may_schedule is cleared. * Ignore the console lock and flush out the messages. Attempting a * trylock would not be useful because: * * - if it is contended, it must be ignored anyway * - console_lock() and console_trylock() block and fail * respectively in panic for non-panic CPUs * - semaphores are not NMI-safe */ /* * If another context is holding the console lock, * @console_may_schedule might be set. Clear it so that * this context does not call cond_resched() while flushing. */ console_trylock(); console_may_schedule = 0; if (mode == CONSOLE_REPLAY_ALL) { Loading @@ -3105,15 +3171,15 @@ void console_flush_on_panic(enum con_flush_mode mode) cookie = console_srcu_read_lock(); for_each_console_srcu(c) { /* * If the above console_trylock() failed, this is an * unsynchronized assignment. But in that case, the * This is an unsynchronized assignment, but the * kernel is in "hope and pray" mode anyway. */ c->seq = seq; } console_srcu_read_unlock(cookie); } console_unlock(); console_flush_all(false, &next_seq, &handover); } /* Loading Loading @@ -3679,8 +3745,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre /* * Hold the console_lock to guarantee safe access to * console->seq and to prevent changes to @console_suspended * until all consoles have been processed. * console->seq. */ console_lock(); Loading @@ -3688,6 +3753,11 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre for_each_console_srcu(c) { if (con && con != c) continue; /* * If consoles are not usable, it cannot be expected * that they make forward progress, so only increment * @diff for usable consoles. */ if (!console_is_usable(c)) continue; printk_seq = c->seq; Loading @@ -3696,18 +3766,12 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre } console_srcu_read_unlock(cookie); /* * If consoles are suspended, it cannot be expected that they * make forward progress, so timeout immediately. @diff is * still used to return a valid flush status. */ if (console_suspended) remaining = 0; else if (diff != last_diff && reset_on_progress) if (diff != last_diff && reset_on_progress) remaining = timeout_ms; console_unlock(); /* Note: @diff is 0 if there are no usable consoles. */ if (diff == 0 || remaining == 0) break; Loading Loading @@ -3741,7 +3805,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre * printer has been seen to make some forward progress. * * Context: Process context. May sleep while acquiring console lock. * Return: true if all enabled printers are caught up. * Return: true if all usable printers are caught up. */ static bool pr_flush(int timeout_ms, bool reset_on_progress) { Loading Loading @@ -3798,11 +3862,33 @@ static void __wake_up_klogd(int val) preempt_enable(); } /** * wake_up_klogd - Wake kernel logging daemon * * Use this function when new records have been added to the ringbuffer * and the console printing of those records has already occurred or is * known to be handled by some other context. This function will only * wake the logging daemon. * * Context: Any context. */ void wake_up_klogd(void) { __wake_up_klogd(PRINTK_PENDING_WAKEUP); } /** * defer_console_output - Wake kernel logging daemon and trigger * console printing in a deferred context * * Use this function when new records have been added to the ringbuffer, * this context is responsible for console printing those records, but * the current context is not allowed to perform the console printing. * Trigger an irq_work context to perform the console printing. This * function also wakes the logging daemon. * * Context: Any context. */ void defer_console_output(void) { /* Loading @@ -3819,12 +3905,7 @@ void printk_trigger_flush(void) int vprintk_deferred(const char *fmt, va_list args) { int r; r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args); defer_console_output(); return r; return vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args); } int _printk_deferred(const char *fmt, ...) Loading
kernel/printk/printk_safe.c +2 −7 Original line number Diff line number Diff line Loading @@ -38,13 +38,8 @@ asmlinkage int vprintk(const char *fmt, va_list args) * Use the main logbuf even in NMI. But avoid calling console * drivers that might have their own locks. */ if (this_cpu_read(printk_context) || in_nmi()) { int len; len = vprintk_store(0, LOGLEVEL_DEFAULT, NULL, fmt, args); defer_console_output(); return len; } if (this_cpu_read(printk_context) || in_nmi()) return vprintk_deferred(fmt, args); /* No obstacles. */ return vprintk_default(fmt, args); Loading