mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-12-28 06:44:36 -05:00
Merge branch 'rework/nbcon-in-kdb' into for-linus
This commit is contained in:
@@ -31,7 +31,7 @@ static void kmsg_dumper_stdout(struct kmsg_dumper *dumper,
|
||||
* expected to output the crash information.
|
||||
*/
|
||||
if (strcmp(con->name, "ttynull") != 0 &&
|
||||
(console_srcu_read_flags(con) & CON_ENABLED)) {
|
||||
console_is_usable(con, console_srcu_read_flags(con), true)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,7 +577,6 @@ static int __init kgdboc_earlycon_init(char *opt)
|
||||
console_list_lock();
|
||||
for_each_console(con) {
|
||||
if (con->write && con->read &&
|
||||
(con->flags & (CON_BOOT | CON_ENABLED)) &&
|
||||
(!opt || !opt[0] || strcmp(con->name, opt) == 0))
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcuwait.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/vesa.h>
|
||||
|
||||
@@ -602,16 +603,70 @@ static inline bool console_is_registered(const struct console *con)
|
||||
extern void nbcon_cpu_emergency_enter(void);
|
||||
extern void nbcon_cpu_emergency_exit(void);
|
||||
extern bool nbcon_can_proceed(struct nbcon_write_context *wctxt);
|
||||
extern void nbcon_write_context_set_buf(struct nbcon_write_context *wctxt,
|
||||
char *buf, unsigned int len);
|
||||
extern bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt);
|
||||
extern bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt);
|
||||
extern void nbcon_reacquire_nobuf(struct nbcon_write_context *wctxt);
|
||||
extern bool nbcon_kdb_try_acquire(struct console *con,
|
||||
struct nbcon_write_context *wctxt);
|
||||
extern void nbcon_kdb_release(struct nbcon_write_context *wctxt);
|
||||
|
||||
/*
|
||||
* Check if the given console is currently capable and allowed to print
|
||||
* records. Note that this function does not consider the current context,
|
||||
* which can also play a role in deciding if @con can be used to print
|
||||
* records.
|
||||
*/
|
||||
static inline bool console_is_usable(struct console *con, short flags, bool use_atomic)
|
||||
{
|
||||
if (!(flags & CON_ENABLED))
|
||||
return false;
|
||||
|
||||
if ((flags & CON_SUSPENDED))
|
||||
return false;
|
||||
|
||||
if (flags & CON_NBCON) {
|
||||
/* The write_atomic() callback is optional. */
|
||||
if (use_atomic && !con->write_atomic)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* For the !use_atomic case, @printk_kthreads_running is not
|
||||
* checked because the write_thread() callback is also used
|
||||
* via the legacy loop when the printer threads are not
|
||||
* available.
|
||||
*/
|
||||
} else {
|
||||
if (!con->write)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Console drivers may assume that per-cpu resources have been
|
||||
* allocated. So unless they're explicitly marked as being able to
|
||||
* cope (CON_ANYTIME) don't call them until this CPU is officially up.
|
||||
*/
|
||||
if (!cpu_online(raw_smp_processor_id()) && !(flags & CON_ANYTIME))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
static inline void nbcon_cpu_emergency_enter(void) { }
|
||||
static inline void nbcon_cpu_emergency_exit(void) { }
|
||||
static inline bool nbcon_can_proceed(struct nbcon_write_context *wctxt) { return false; }
|
||||
static inline void nbcon_write_context_set_buf(struct nbcon_write_context *wctxt,
|
||||
char *buf, unsigned int len) { }
|
||||
static inline bool nbcon_enter_unsafe(struct nbcon_write_context *wctxt) { return false; }
|
||||
static inline bool nbcon_exit_unsafe(struct nbcon_write_context *wctxt) { return false; }
|
||||
static inline void nbcon_reacquire_nobuf(struct nbcon_write_context *wctxt) { }
|
||||
static inline bool nbcon_kdb_try_acquire(struct console *con,
|
||||
struct nbcon_write_context *wctxt) { return false; }
|
||||
static inline void nbcon_kdb_release(struct nbcon_write_context *wctxt) { }
|
||||
static inline bool console_is_usable(struct console *con, short flags,
|
||||
bool use_atomic) { return false; }
|
||||
#endif
|
||||
|
||||
extern int console_set_on_cmdline;
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/smp.h>
|
||||
|
||||
/* Shifted versions of the command enable bits are be used if the command
|
||||
* has no arguments (see kdb_check_flags). This allows commands, such as
|
||||
@@ -207,11 +208,26 @@ static inline const char *kdb_walk_kallsyms(loff_t *pos)
|
||||
/* Dynamic kdb shell command registration */
|
||||
extern int kdb_register(kdbtab_t *cmd);
|
||||
extern void kdb_unregister(kdbtab_t *cmd);
|
||||
|
||||
/* Return true when KDB as locked for printing a message on this CPU. */
|
||||
static inline
|
||||
bool kdb_printf_on_this_cpu(void)
|
||||
{
|
||||
/*
|
||||
* We can use raw_smp_processor_id() here because the task could
|
||||
* not get migrated when KDB has locked for printing on this CPU.
|
||||
*/
|
||||
return unlikely(READ_ONCE(kdb_printf_cpu) == raw_smp_processor_id());
|
||||
}
|
||||
|
||||
#else /* ! CONFIG_KGDB_KDB */
|
||||
static inline __printf(1, 2) int kdb_printf(const char *fmt, ...) { return 0; }
|
||||
static inline void kdb_init(int level) {}
|
||||
static inline int kdb_register(kdbtab_t *cmd) { return 0; }
|
||||
static inline void kdb_unregister(kdbtab_t *cmd) {}
|
||||
|
||||
static inline bool kdb_printf_on_this_cpu(void) { return false; }
|
||||
|
||||
#endif /* CONFIG_KGDB_KDB */
|
||||
enum {
|
||||
KDB_NOT_INITIALIZED,
|
||||
|
||||
@@ -589,24 +589,41 @@ static void kdb_msg_write(const char *msg, int msg_len)
|
||||
*/
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(c) {
|
||||
if (!(console_srcu_read_flags(c) & CON_ENABLED))
|
||||
short flags = console_srcu_read_flags(c);
|
||||
|
||||
if (!console_is_usable(c, flags, true))
|
||||
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
|
||||
* context the risk of deadlock is a bigger problem than risks
|
||||
* due to re-entering the console driver. We operate directly on
|
||||
* oops_in_progress rather than using bust_spinlocks() because
|
||||
* the calls bust_spinlocks() makes on exit are not appropriate
|
||||
* for this calling context.
|
||||
*/
|
||||
++oops_in_progress;
|
||||
c->write(c, msg, msg_len);
|
||||
--oops_in_progress;
|
||||
|
||||
if (flags & CON_NBCON) {
|
||||
struct nbcon_write_context wctxt = { };
|
||||
|
||||
/*
|
||||
* Do not continue if the console is NBCON and the context
|
||||
* can't be acquired.
|
||||
*/
|
||||
if (!nbcon_kdb_try_acquire(c, &wctxt))
|
||||
continue;
|
||||
|
||||
nbcon_write_context_set_buf(&wctxt, (char *)msg, msg_len);
|
||||
|
||||
c->write_atomic(c, &wctxt);
|
||||
nbcon_kdb_release(&wctxt);
|
||||
} else {
|
||||
/*
|
||||
* Set oops_in_progress to encourage the console drivers to
|
||||
* disregard their internal spin locks: in the current calling
|
||||
* context the risk of deadlock is a bigger problem than risks
|
||||
* due to re-entering the console driver. We operate directly on
|
||||
* oops_in_progress rather than using bust_spinlocks() because
|
||||
* the calls bust_spinlocks() makes on exit are not appropriate
|
||||
* for this calling context.
|
||||
*/
|
||||
++oops_in_progress;
|
||||
c->write(c, msg, msg_len);
|
||||
--oops_in_progress;
|
||||
}
|
||||
touch_nmi_watchdog();
|
||||
}
|
||||
console_srcu_read_unlock(cookie);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
* internal.h - printk internal definitions
|
||||
*/
|
||||
#include <linux/console.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
|
||||
@@ -112,47 +111,6 @@ bool nbcon_kthread_create(struct console *con);
|
||||
void nbcon_kthread_stop(struct console *con);
|
||||
void nbcon_kthreads_wake(void);
|
||||
|
||||
/*
|
||||
* Check if the given console is currently capable and allowed to print
|
||||
* records. Note that this function does not consider the current context,
|
||||
* which can also play a role in deciding if @con can be used to print
|
||||
* records.
|
||||
*/
|
||||
static inline bool console_is_usable(struct console *con, short flags, bool use_atomic)
|
||||
{
|
||||
if (!(flags & CON_ENABLED))
|
||||
return false;
|
||||
|
||||
if ((flags & CON_SUSPENDED))
|
||||
return false;
|
||||
|
||||
if (flags & CON_NBCON) {
|
||||
/* The write_atomic() callback is optional. */
|
||||
if (use_atomic && !con->write_atomic)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* For the !use_atomic case, @printk_kthreads_running is not
|
||||
* checked because the write_thread() callback is also used
|
||||
* via the legacy loop when the printer threads are not
|
||||
* available.
|
||||
*/
|
||||
} else {
|
||||
if (!con->write)
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Console drivers may assume that per-cpu resources have been
|
||||
* allocated. So unless they're explicitly marked as being able to
|
||||
* cope (CON_ANYTIME) don't call them until this CPU is officially up.
|
||||
*/
|
||||
if (!cpu_online(raw_smp_processor_id()) && !(flags & CON_ANYTIME))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* nbcon_kthread_wake - Wake up a console printing thread
|
||||
* @con: Console to operate on
|
||||
@@ -204,9 +162,6 @@ static inline bool nbcon_legacy_emit_next_record(struct console *con, bool *hand
|
||||
static inline void nbcon_kthread_wake(struct console *con) { }
|
||||
static inline void nbcon_kthreads_wake(void) { }
|
||||
|
||||
static inline bool console_is_usable(struct console *con, short flags,
|
||||
bool use_atomic) { return false; }
|
||||
|
||||
#endif /* CONFIG_PRINTK */
|
||||
|
||||
extern bool have_boot_console;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/kdb.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/panic.h>
|
||||
@@ -252,13 +253,16 @@ static int nbcon_context_try_acquire_direct(struct nbcon_context *ctxt,
|
||||
* since all non-panic CPUs are stopped during panic(), it
|
||||
* is safer to have them avoid gaining console ownership.
|
||||
*
|
||||
* If this acquire is a reacquire (and an unsafe takeover
|
||||
* One exception is when kdb has locked for printing on this CPU.
|
||||
*
|
||||
* Second exception is a reacquire (and an unsafe takeover
|
||||
* has not previously occurred) then it is allowed to attempt
|
||||
* a direct acquire in panic. This gives console drivers an
|
||||
* opportunity to perform any necessary cleanup if they were
|
||||
* interrupted by the panic CPU while printing.
|
||||
*/
|
||||
if (panic_on_other_cpu() &&
|
||||
!kdb_printf_on_this_cpu() &&
|
||||
(!is_reacquire || cur->unsafe_takeover)) {
|
||||
return -EPERM;
|
||||
}
|
||||
@@ -853,8 +857,8 @@ static bool __nbcon_context_update_unsafe(struct nbcon_context *ctxt, bool unsaf
|
||||
return nbcon_context_can_proceed(ctxt, &cur);
|
||||
}
|
||||
|
||||
static void nbcon_write_context_set_buf(struct nbcon_write_context *wctxt,
|
||||
char *buf, unsigned int len)
|
||||
void nbcon_write_context_set_buf(struct nbcon_write_context *wctxt,
|
||||
char *buf, unsigned int len)
|
||||
{
|
||||
struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
|
||||
struct console *con = ctxt->console;
|
||||
@@ -1894,3 +1898,64 @@ void nbcon_device_release(struct console *con)
|
||||
console_srcu_read_unlock(cookie);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nbcon_device_release);
|
||||
|
||||
/**
|
||||
* nbcon_kdb_try_acquire - Try to acquire nbcon console and enter unsafe
|
||||
* section
|
||||
* @con: The nbcon console to acquire
|
||||
* @wctxt: The nbcon write context to be used on success
|
||||
*
|
||||
* Context: Under console_srcu_read_lock() for emitting a single kdb message
|
||||
* using the given con->write_atomic() callback. Can be called
|
||||
* only when the console is usable at the moment.
|
||||
*
|
||||
* Return: True if the console was acquired. False otherwise.
|
||||
*
|
||||
* kdb emits messages on consoles registered for printk() without
|
||||
* storing them into the ring buffer. It has to acquire the console
|
||||
* ownerhip so that it could call con->write_atomic() callback a safe way.
|
||||
*
|
||||
* This function acquires the nbcon console using priority NBCON_PRIO_EMERGENCY
|
||||
* and marks it unsafe for handover/takeover.
|
||||
*/
|
||||
bool nbcon_kdb_try_acquire(struct console *con,
|
||||
struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
|
||||
|
||||
memset(ctxt, 0, sizeof(*ctxt));
|
||||
ctxt->console = con;
|
||||
ctxt->prio = NBCON_PRIO_EMERGENCY;
|
||||
|
||||
if (!nbcon_context_try_acquire(ctxt, false))
|
||||
return false;
|
||||
|
||||
if (!nbcon_context_enter_unsafe(ctxt))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* nbcon_kdb_release - Exit unsafe section and release the nbcon console
|
||||
*
|
||||
* @wctxt: The nbcon write context initialized by a successful
|
||||
* nbcon_kdb_try_acquire()
|
||||
*/
|
||||
void nbcon_kdb_release(struct nbcon_write_context *wctxt)
|
||||
{
|
||||
struct nbcon_context *ctxt = &ACCESS_PRIVATE(wctxt, ctxt);
|
||||
|
||||
if (!nbcon_context_exit_unsafe(ctxt))
|
||||
return;
|
||||
|
||||
nbcon_context_release(ctxt);
|
||||
|
||||
/*
|
||||
* Flush any new printk() messages added when the console was blocked.
|
||||
* Only the console used by the given write context was blocked.
|
||||
* The console was locked only when the write_atomic() callback
|
||||
* was usable.
|
||||
*/
|
||||
__nbcon_atomic_flush_pending_con(ctxt->console, prb_next_reserve_seq(prb), false);
|
||||
}
|
||||
|
||||
@@ -3331,12 +3331,10 @@ void console_unblank(void)
|
||||
*/
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(c) {
|
||||
short flags = console_srcu_read_flags(c);
|
||||
|
||||
if (flags & CON_SUSPENDED)
|
||||
if (!console_is_usable(c, console_srcu_read_flags(c), true))
|
||||
continue;
|
||||
|
||||
if ((flags & CON_ENABLED) && c->unblank) {
|
||||
if (c->unblank) {
|
||||
found_unblank = true;
|
||||
break;
|
||||
}
|
||||
@@ -3373,12 +3371,10 @@ void console_unblank(void)
|
||||
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(c) {
|
||||
short flags = console_srcu_read_flags(c);
|
||||
|
||||
if (flags & CON_SUSPENDED)
|
||||
if (!console_is_usable(c, console_srcu_read_flags(c), true))
|
||||
continue;
|
||||
|
||||
if ((flags & CON_ENABLED) && c->unblank)
|
||||
if (c->unblank)
|
||||
c->unblank();
|
||||
}
|
||||
console_srcu_read_unlock(cookie);
|
||||
|
||||
Reference in New Issue
Block a user