mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
printk changes for 6.12
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEESH4wyp42V4tXvYsjUqAMR0iAlPIFAmbi598ACgkQUqAMR0iA lPL3lw//WaRDKJ1Cb/bKAn3nRpjdqiNBI//K1gRJp0LgLE7qEudE25t4j3F9tvvP pc9AB81g1Au8Br6iOd+NiGXXW5KWJHaZ3rUAdeo6co4NQCbrY6qTA78ItZSQImBH A9fhWWr1TGRX8L/N/gR2eYBnpbDGIbRahUOQraUpBn4kEPyR47KEx7Njjo48GcmR Ye8dIYwUOWEgQeIuIxIAwNf6KyNjo5tQpgve+M8HGwy8mZqP9XV6UjXUACVwQNx6 +CK+IGM+94tCq5KalOaJ5BtsXGKlabHIs7y9QpLS45M2QoHIlDIvpaxzLf0FTsPI CppqedAGN2jU0NyjfbFk1c+SNQfDZEAZVyF6vKFelP7t2jzAx301RyB2S+Cm7Hh+ PajFty41UT0/y17V4sZawfMqpFyp7Wr6RKQYYKMBRdSQQkToh/dmebBvqPAHW9cJ LInQQf+XdzbonKa+CTmT/Tg+eM2R124FWeMVnEMdtyXpKUV9qdKWfngtzyRMQiYI q54ZwKd3VJ9kRIfb7Fp0TBr2NErdnEQE5hh9QhI8SAWENskw5+GmYaQit734U9wA SU7t9rir7NS4Rc1jHP9SQ9oWWI9HT4hthRGkLh2Knx0O2c6AwOuEI4wkjzSWI3GX /eeofnbZiUpi7fESf9qmTGtQZ4/9ogQ7fNaroWCSfQzq3+wl+2o= =28sV -----END PGP SIGNATURE----- Merge tag 'printk-for-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux Pull printk updates from Petr Mladek: "This is the "last" part of the support for the new nbcon consoles. Where "nbcon" stays for "No Big console lock CONsoles" aka not under the console_lock. New callbacks are added to struct console: - write_thread() for flushing nbcon consoles in task context. - write_atomic() for flushing nbcon consoles in atomic context, including NMI. - con->device_lock() and device_unlock() for taking the driver specific lock, for example, port->lock. New printk-specific kthreads are created: - per-console kthreads which get responsible for flushing normal priority messages on nbcon consoles. - thread which gets responsible for flushing normal priority messages on all consoles when CONFIG_RT enabled. The new callbacks are called under a special per-console lock which has already been added back in v6.7. It allows to distinguish three severities: normal, emergency, and panic. A context with a higher priority could take over the ownership when it is safe even in the middle of handling a record. The panic context could do it even when it is not safe. But it is allowed only for the final desperate flush before entering the infinite loop. The new lock helps to flush the messages directly in emergency and panic contexts. But it is not enough in all situations: - console_lock() is still need for synchronization against boot consoles. - con->device_lock() is need for synchronization against other operations on the same HW, e.g. serial port speed setting, non-printk related read/write. The dependency on con->device_lock() is mutual. Any code taking the driver specific lock has to acquire the related nbcon console context as well. For example, see the new uart_port_lock() API. It provides the necessary synchronization against emergency and panic contexts where the messages are flushed only under the new per-console lock. Maybe surprisingly, a quite tricky part is the decision how to flush the consoles in various situations. It has to take into account: - message priority: normal, emergency, panic - scheduling context: task, atomic, deferred_legacy - registered consoles: boot, legacy, nbcon - threads are running: early boot, suspend, shutdown, panic - caller: printk(), pr_flush(), printk_flush_in_panic(), console_unlock(), console_start(), ... The primary decision is made in printk_get_console_flush_type(). It creates a hint what the caller should do: - flush nbcon consoles directly or via the kthread - call the legacy loop (console_unlock()) directly or via irq_work The existing behavior is preserved for the legacy consoles. The only exception is that they are not longer flushed directly from printk() in panic() before CPUs are stopped. But this blocking happens only when at least one nbcon console is registered. The motivation is to increase a chance to produce the crash dump. They legacy consoles might create a deadlock in compare with nbcon consoles. The nbcon console should allow to see the messages even when the crash dump fails. There are three possible ways how nbcon consoles are flushed: - The per-nbcon-console kthread is responsible for flushing messages added with the normal priority. This is the default mode. - The legacy loop, aka console_unlock(), is used when there is still a boot console registered. There is no easy way how to match an early console driver with a nbcon console driver. And the console_lock() provides the only reliable serialization at the moment. The legacy loop uses either con->write_atomic() or con->write_thread() callbacks depending on whether it is allowed to schedule. The atomic variant has to be used from printk(). - In other situations, the messages are flushed directly using write_atomic() which can be called in any context, including NMI. It is primary needed during early boot or shutdown, in emergency situations, and panic. The emergency priority is used by a code called within nbcon_cpu_emergency_enter()/exit(). At the moment, it is used in four situations: WARN(), Oops, lockdep, and RCU stall reports. Finally, there is no nbcon console at the moment. It means that the changes should _not_ modify the existing behavior. The only exception is CONFIG_RT which would force offloading the legacy loop, for normal priority context, into the dedicated kthread" * tag 'printk-for-6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux: (54 commits) printk: Avoid false positive lockdep report for legacy printing printk: nbcon: Assign nice -20 for printing threads printk: Implement legacy printer kthread for PREEMPT_RT tty: sysfs: Add nbcon support for 'active' proc: Add nbcon support for /proc/consoles proc: consoles: Add notation to c_start/c_stop printk: nbcon: Show replay message on takeover printk: Provide helper for message prepending printk: nbcon: Rely on kthreads for normal operation printk: nbcon: Use thread callback if in task context for legacy printk: nbcon: Relocate nbcon_atomic_emit_one() printk: nbcon: Introduce printer kthreads printk: nbcon: Init @nbcon_seq to highest possible printk: nbcon: Add context to usable() and emit() printk: Flush console on unregister_console() printk: Fail pr_flush() if before SYSTEM_SCHEDULING printk: nbcon: Add function for printers to reacquire ownership printk: nbcon: Use raw_cpu_ptr() instead of open coding printk: Use the BITS_PER_LONG macro lockdep: Mark emergency sections in lockdep splats ...
This commit is contained in:
commit
c903327d32
@ -423,11 +423,11 @@ static int univ8250_console_setup(struct console *co, char *options)
|
||||
|
||||
port = &serial8250_ports[co->index].port;
|
||||
/* link port to console */
|
||||
port->cons = co;
|
||||
uart_port_set_cons(port, co);
|
||||
|
||||
retval = serial8250_console_setup(port, options, false);
|
||||
if (retval != 0)
|
||||
port->cons = NULL;
|
||||
uart_port_set_cons(port, NULL);
|
||||
return retval;
|
||||
}
|
||||
|
||||
@ -485,7 +485,7 @@ static int univ8250_console_match(struct console *co, char *name, int idx,
|
||||
continue;
|
||||
|
||||
co->index = i;
|
||||
port->cons = co;
|
||||
uart_port_set_cons(port, co);
|
||||
return serial8250_console_setup(port, options, true);
|
||||
}
|
||||
|
||||
|
@ -2480,7 +2480,7 @@ static int pl011_console_match(struct console *co, char *name, int idx,
|
||||
continue;
|
||||
|
||||
co->index = i;
|
||||
port->cons = co;
|
||||
uart_port_set_cons(port, co);
|
||||
return pl011_console_setup(co, options);
|
||||
}
|
||||
|
||||
|
@ -3176,8 +3176,15 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u
|
||||
state->uart_port = uport;
|
||||
uport->state = state;
|
||||
|
||||
/*
|
||||
* If this port is in use as a console then the spinlock is already
|
||||
* initialised.
|
||||
*/
|
||||
if (!uart_console_registered(uport))
|
||||
uart_port_spin_lock_init(uport);
|
||||
|
||||
state->pm_state = UART_PM_STATE_UNDEFINED;
|
||||
uport->cons = drv->cons;
|
||||
uart_port_set_cons(uport, drv->cons);
|
||||
uport->minor = drv->tty_driver->minor_start + uport->line;
|
||||
uport->name = kasprintf(GFP_KERNEL, "%s%d", drv->dev_name,
|
||||
drv->tty_driver->name_base + uport->line);
|
||||
@ -3186,13 +3193,6 @@ static int serial_core_add_one_port(struct uart_driver *drv, struct uart_port *u
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this port is in use as a console then the spinlock is already
|
||||
* initialised.
|
||||
*/
|
||||
if (!uart_console_registered(uport))
|
||||
uart_port_spin_lock_init(uport);
|
||||
|
||||
if (uport->cons && uport->dev)
|
||||
of_console_check(uport->dev->of_node, uport->cons->name, uport->line);
|
||||
|
||||
|
@ -3573,7 +3573,7 @@ static ssize_t show_cons_active(struct device *dev,
|
||||
for_each_console(c) {
|
||||
if (!c->device)
|
||||
continue;
|
||||
if (!c->write)
|
||||
if (!(c->flags & CON_NBCON) && !c->write)
|
||||
continue;
|
||||
if ((c->flags & CON_ENABLED) == 0)
|
||||
continue;
|
||||
|
@ -21,6 +21,7 @@ static int show_console_dev(struct seq_file *m, void *v)
|
||||
{ CON_ENABLED, 'E' },
|
||||
{ CON_CONSDEV, 'C' },
|
||||
{ CON_BOOT, 'B' },
|
||||
{ CON_NBCON, 'N' },
|
||||
{ CON_PRINTBUFFER, 'p' },
|
||||
{ CON_BRL, 'b' },
|
||||
{ CON_ANYTIME, 'a' },
|
||||
@ -58,8 +59,8 @@ static int show_console_dev(struct seq_file *m, void *v)
|
||||
seq_printf(m, "%s%d", con->name, con->index);
|
||||
seq_pad(m, ' ');
|
||||
seq_printf(m, "%c%c%c (%s)", con->read ? 'R' : '-',
|
||||
con->write ? 'W' : '-', con->unblank ? 'U' : '-',
|
||||
flags);
|
||||
((con->flags & CON_NBCON) || con->write) ? 'W' : '-',
|
||||
con->unblank ? 'U' : '-', flags);
|
||||
if (dev)
|
||||
seq_printf(m, " %4d:%d", MAJOR(dev), MINOR(dev));
|
||||
|
||||
@ -68,6 +69,7 @@ static int show_console_dev(struct seq_file *m, void *v)
|
||||
}
|
||||
|
||||
static void *c_start(struct seq_file *m, loff_t *pos)
|
||||
__acquires(&console_mutex)
|
||||
{
|
||||
struct console *con;
|
||||
loff_t off = 0;
|
||||
@ -94,6 +96,7 @@ static void *c_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
}
|
||||
|
||||
static void c_stop(struct seq_file *m, void *v)
|
||||
__releases(&console_mutex)
|
||||
{
|
||||
console_list_unlock();
|
||||
}
|
||||
|
@ -16,7 +16,9 @@
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/irq_work.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/rcuwait.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/vesa.h>
|
||||
|
||||
@ -303,7 +305,7 @@ struct nbcon_write_context {
|
||||
/**
|
||||
* struct console - The console descriptor structure
|
||||
* @name: The name of the console driver
|
||||
* @write: Write callback to output messages (Optional)
|
||||
* @write: Legacy write callback to output messages (Optional)
|
||||
* @read: Read callback for console input (Optional)
|
||||
* @device: The underlying TTY device driver (Optional)
|
||||
* @unblank: Callback to unblank the console (Optional)
|
||||
@ -320,10 +322,14 @@ struct nbcon_write_context {
|
||||
* @data: Driver private data
|
||||
* @node: hlist node for the console list
|
||||
*
|
||||
* @write_atomic: Write callback for atomic context
|
||||
* @nbcon_state: State for nbcon consoles
|
||||
* @nbcon_seq: Sequence number of the next record for nbcon to print
|
||||
* @nbcon_device_ctxt: Context available for non-printing operations
|
||||
* @nbcon_prev_seq: Seq num the previous nbcon owner was assigned to print
|
||||
* @pbufs: Pointer to nbcon private buffer
|
||||
* @kthread: Printer kthread for this console
|
||||
* @rcuwait: RCU-safe wait object for @kthread waking
|
||||
* @irq_work: Defer @kthread waking to IRQ work context
|
||||
*/
|
||||
struct console {
|
||||
char name[16];
|
||||
@ -345,11 +351,121 @@ struct console {
|
||||
struct hlist_node node;
|
||||
|
||||
/* nbcon console specific members */
|
||||
bool (*write_atomic)(struct console *con,
|
||||
struct nbcon_write_context *wctxt);
|
||||
|
||||
/**
|
||||
* @write_atomic:
|
||||
*
|
||||
* NBCON callback to write out text in any context. (Optional)
|
||||
*
|
||||
* This callback is called with the console already acquired. However,
|
||||
* a higher priority context is allowed to take it over by default.
|
||||
*
|
||||
* The callback must call nbcon_enter_unsafe() and nbcon_exit_unsafe()
|
||||
* around any code where the takeover is not safe, for example, when
|
||||
* manipulating the serial port registers.
|
||||
*
|
||||
* nbcon_enter_unsafe() will fail if the context has lost the console
|
||||
* ownership in the meantime. In this case, the callback is no longer
|
||||
* allowed to go forward. It must back out immediately and carefully.
|
||||
* The buffer content is also no longer trusted since it no longer
|
||||
* belongs to the context.
|
||||
*
|
||||
* The callback should allow the takeover whenever it is safe. It
|
||||
* increases the chance to see messages when the system is in trouble.
|
||||
* If the driver must reacquire ownership in order to finalize or
|
||||
* revert hardware changes, nbcon_reacquire_nobuf() can be used.
|
||||
* However, on reacquire the buffer content is no longer available. A
|
||||
* reacquire cannot be used to resume printing.
|
||||
*
|
||||
* The callback can be called from any context (including NMI).
|
||||
* Therefore it must avoid usage of any locking and instead rely
|
||||
* on the console ownership for synchronization.
|
||||
*/
|
||||
void (*write_atomic)(struct console *con, struct nbcon_write_context *wctxt);
|
||||
|
||||
/**
|
||||
* @write_thread:
|
||||
*
|
||||
* NBCON callback to write out text in task context.
|
||||
*
|
||||
* This callback must be called only in task context with both
|
||||
* device_lock() and the nbcon console acquired with
|
||||
* NBCON_PRIO_NORMAL.
|
||||
*
|
||||
* The same rules for console ownership verification and unsafe
|
||||
* sections handling applies as with write_atomic().
|
||||
*
|
||||
* The console ownership handling is necessary for synchronization
|
||||
* against write_atomic() which is synchronized only via the context.
|
||||
*
|
||||
* The device_lock() provides the primary serialization for operations
|
||||
* on the device. It might be as relaxed (mutex)[*] or as tight
|
||||
* (disabled preemption and interrupts) as needed. It allows
|
||||
* the kthread to operate in the least restrictive mode[**].
|
||||
*
|
||||
* [*] Standalone nbcon_context_try_acquire() is not safe with
|
||||
* the preemption enabled, see nbcon_owner_matches(). But it
|
||||
* can be safe when always called in the preemptive context
|
||||
* under the device_lock().
|
||||
*
|
||||
* [**] The device_lock() makes sure that nbcon_context_try_acquire()
|
||||
* would never need to spin which is important especially with
|
||||
* PREEMPT_RT.
|
||||
*/
|
||||
void (*write_thread)(struct console *con, struct nbcon_write_context *wctxt);
|
||||
|
||||
/**
|
||||
* @device_lock:
|
||||
*
|
||||
* NBCON callback to begin synchronization with driver code.
|
||||
*
|
||||
* Console drivers typically must deal with access to the hardware
|
||||
* via user input/output (such as an interactive login shell) and
|
||||
* output of kernel messages via printk() calls. This callback is
|
||||
* called by the printk-subsystem whenever it needs to synchronize
|
||||
* with hardware access by the driver. It should be implemented to
|
||||
* use whatever synchronization mechanism the driver is using for
|
||||
* itself (for example, the port lock for uart serial consoles).
|
||||
*
|
||||
* The callback is always called from task context. It may use any
|
||||
* synchronization method required by the driver.
|
||||
*
|
||||
* IMPORTANT: The callback MUST disable migration. The console driver
|
||||
* may be using a synchronization mechanism that already takes
|
||||
* care of this (such as spinlocks). Otherwise this function must
|
||||
* explicitly call migrate_disable().
|
||||
*
|
||||
* The flags argument is provided as a convenience to the driver. It
|
||||
* will be passed again to device_unlock(). It can be ignored if the
|
||||
* driver does not need it.
|
||||
*/
|
||||
void (*device_lock)(struct console *con, unsigned long *flags);
|
||||
|
||||
/**
|
||||
* @device_unlock:
|
||||
*
|
||||
* NBCON callback to finish synchronization with driver code.
|
||||
*
|
||||
* It is the counterpart to device_lock().
|
||||
*
|
||||
* This callback is always called from task context. It must
|
||||
* appropriately re-enable migration (depending on how device_lock()
|
||||
* disabled migration).
|
||||
*
|
||||
* The flags argument is the value of the same variable that was
|
||||
* passed to device_lock().
|
||||
*/
|
||||
void (*device_unlock)(struct console *con, unsigned long flags);
|
||||
|
||||
atomic_t __private nbcon_state;
|
||||
atomic_long_t __private nbcon_seq;
|
||||
struct nbcon_context __private nbcon_device_ctxt;
|
||||
atomic_long_t __private nbcon_prev_seq;
|
||||
|
||||
struct printk_buffers *pbufs;
|
||||
struct task_struct *kthread;
|
||||
struct rcuwait rcuwait;
|
||||
struct irq_work irq_work;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
@ -378,28 +494,34 @@ extern void console_list_unlock(void) __releases(console_mutex);
|
||||
extern struct hlist_head console_list;
|
||||
|
||||
/**
|
||||
* console_srcu_read_flags - Locklessly read the console flags
|
||||
* console_srcu_read_flags - Locklessly read flags of a possibly registered
|
||||
* console
|
||||
* @con: struct console pointer of console to read flags from
|
||||
*
|
||||
* This function provides the necessary READ_ONCE() and data_race()
|
||||
* notation for locklessly reading the console flags. The READ_ONCE()
|
||||
* in this function matches the WRITE_ONCE() when @flags are modified
|
||||
* for registered consoles with console_srcu_write_flags().
|
||||
* Locklessly reading @con->flags provides a consistent read value because
|
||||
* there is at most one CPU modifying @con->flags and that CPU is using only
|
||||
* read-modify-write operations to do so.
|
||||
*
|
||||
* Only use this function to read console flags when locklessly
|
||||
* iterating the console list via srcu.
|
||||
* Requires console_srcu_read_lock to be held, which implies that @con might
|
||||
* be a registered console. The purpose of holding console_srcu_read_lock is
|
||||
* to guarantee that the console state is valid (CON_SUSPENDED/CON_ENABLED)
|
||||
* and that no exit/cleanup routines will run if the console is currently
|
||||
* undergoing unregistration.
|
||||
*
|
||||
* If the caller is holding the console_list_lock or it is _certain_ that
|
||||
* @con is not and will not become registered, the caller may read
|
||||
* @con->flags directly instead.
|
||||
*
|
||||
* Context: Any context.
|
||||
* Return: The current value of the @con->flags field.
|
||||
*/
|
||||
static inline short console_srcu_read_flags(const struct console *con)
|
||||
{
|
||||
WARN_ON_ONCE(!console_srcu_read_lock_is_held());
|
||||
|
||||
/*
|
||||
* Locklessly reading console->flags provides a consistent
|
||||
* read value because there is at most one CPU modifying
|
||||
* console->flags and that CPU is using only read-modify-write
|
||||
* operations to do so.
|
||||
* The READ_ONCE() matches the WRITE_ONCE() when @flags are modified
|
||||
* for registered consoles with console_srcu_write_flags().
|
||||
*/
|
||||
return data_race(READ_ONCE(con->flags));
|
||||
}
|
||||
@ -477,13 +599,19 @@ static inline bool console_is_registered(const struct console *con)
|
||||
hlist_for_each_entry(con, &console_list, node)
|
||||
|
||||
#ifdef CONFIG_PRINTK
|
||||
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 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);
|
||||
#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 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) { }
|
||||
#endif
|
||||
|
||||
extern int console_set_on_cmdline;
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <linux/ratelimit_types.h>
|
||||
#include <linux/once_lite.h>
|
||||
|
||||
struct console;
|
||||
|
||||
extern const char linux_banner[];
|
||||
extern const char linux_proc_banner[];
|
||||
|
||||
@ -161,15 +163,16 @@ int _printk(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);
|
||||
extern void __printk_deferred_enter(void);
|
||||
extern void __printk_deferred_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
|
||||
#define printk_deferred_enter() __printk_deferred_enter()
|
||||
#define printk_deferred_exit() __printk_deferred_exit()
|
||||
|
||||
/*
|
||||
* Please don't use printk_ratelimit(), because it shares ratelimiting state
|
||||
@ -197,6 +200,10 @@ extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold;
|
||||
extern asmlinkage void dump_stack(void) __cold;
|
||||
void printk_trigger_flush(void);
|
||||
void console_try_replay_all(void);
|
||||
void printk_legacy_allow_panic_sync(void);
|
||||
extern bool nbcon_device_try_acquire(struct console *con);
|
||||
extern void nbcon_device_release(struct console *con);
|
||||
void nbcon_atomic_flush_unsafe(void);
|
||||
#else
|
||||
static inline __printf(1, 0)
|
||||
int vprintk(const char *s, va_list args)
|
||||
@ -279,6 +286,24 @@ static inline void printk_trigger_flush(void)
|
||||
static inline void console_try_replay_all(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void printk_legacy_allow_panic_sync(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool nbcon_device_try_acquire(struct console *con)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void nbcon_device_release(struct console *con)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void nbcon_atomic_flush_unsafe(void)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool this_cpu_in_panic(void);
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/console.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/printk.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/tty.h>
|
||||
@ -590,6 +592,95 @@ struct uart_port {
|
||||
void *private_data; /* generic platform data pointer */
|
||||
};
|
||||
|
||||
/*
|
||||
* Only for console->device_lock()/_unlock() callbacks and internal
|
||||
* port lock wrapper synchronization.
|
||||
*/
|
||||
static inline void __uart_port_lock_irqsave(struct uart_port *up, unsigned long *flags)
|
||||
{
|
||||
spin_lock_irqsave(&up->lock, *flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only for console->device_lock()/_unlock() callbacks and internal
|
||||
* port lock wrapper synchronization.
|
||||
*/
|
||||
static inline void __uart_port_unlock_irqrestore(struct uart_port *up, unsigned long flags)
|
||||
{
|
||||
spin_unlock_irqrestore(&up->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* uart_port_set_cons - Safely set the @cons field for a uart
|
||||
* @up: The uart port to set
|
||||
* @con: The new console to set to
|
||||
*
|
||||
* This function must be used to set @up->cons. It uses the port lock to
|
||||
* synchronize with the port lock wrappers in order to ensure that the console
|
||||
* cannot change or disappear while another context is holding the port lock.
|
||||
*/
|
||||
static inline void uart_port_set_cons(struct uart_port *up, struct console *con)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
__uart_port_lock_irqsave(up, &flags);
|
||||
up->cons = con;
|
||||
__uart_port_unlock_irqrestore(up, flags);
|
||||
}
|
||||
|
||||
/* Only for internal port lock wrapper usage. */
|
||||
static inline bool __uart_port_using_nbcon(struct uart_port *up)
|
||||
{
|
||||
lockdep_assert_held_once(&up->lock);
|
||||
|
||||
if (likely(!uart_console(up)))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* @up->cons is only modified under the port lock. Therefore it is
|
||||
* certain that it cannot disappear here.
|
||||
*
|
||||
* @up->cons->node is added/removed from the console list under the
|
||||
* port lock. Therefore it is certain that the registration status
|
||||
* cannot change here, thus @up->cons->flags can be read directly.
|
||||
*/
|
||||
if (hlist_unhashed_lockless(&up->cons->node) ||
|
||||
!(up->cons->flags & CON_NBCON) ||
|
||||
!up->cons->write_atomic) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Only for internal port lock wrapper usage. */
|
||||
static inline bool __uart_port_nbcon_try_acquire(struct uart_port *up)
|
||||
{
|
||||
if (!__uart_port_using_nbcon(up))
|
||||
return true;
|
||||
|
||||
return nbcon_device_try_acquire(up->cons);
|
||||
}
|
||||
|
||||
/* Only for internal port lock wrapper usage. */
|
||||
static inline void __uart_port_nbcon_acquire(struct uart_port *up)
|
||||
{
|
||||
if (!__uart_port_using_nbcon(up))
|
||||
return;
|
||||
|
||||
while (!nbcon_device_try_acquire(up->cons))
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
/* Only for internal port lock wrapper usage. */
|
||||
static inline void __uart_port_nbcon_release(struct uart_port *up)
|
||||
{
|
||||
if (!__uart_port_using_nbcon(up))
|
||||
return;
|
||||
|
||||
nbcon_device_release(up->cons);
|
||||
}
|
||||
|
||||
/**
|
||||
* uart_port_lock - Lock the UART port
|
||||
* @up: Pointer to UART port structure
|
||||
@ -597,6 +688,7 @@ struct uart_port {
|
||||
static inline void uart_port_lock(struct uart_port *up)
|
||||
{
|
||||
spin_lock(&up->lock);
|
||||
__uart_port_nbcon_acquire(up);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -606,6 +698,7 @@ static inline void uart_port_lock(struct uart_port *up)
|
||||
static inline void uart_port_lock_irq(struct uart_port *up)
|
||||
{
|
||||
spin_lock_irq(&up->lock);
|
||||
__uart_port_nbcon_acquire(up);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -616,6 +709,7 @@ static inline void uart_port_lock_irq(struct uart_port *up)
|
||||
static inline void uart_port_lock_irqsave(struct uart_port *up, unsigned long *flags)
|
||||
{
|
||||
spin_lock_irqsave(&up->lock, *flags);
|
||||
__uart_port_nbcon_acquire(up);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -626,7 +720,15 @@ static inline void uart_port_lock_irqsave(struct uart_port *up, unsigned long *f
|
||||
*/
|
||||
static inline bool uart_port_trylock(struct uart_port *up)
|
||||
{
|
||||
return spin_trylock(&up->lock);
|
||||
if (!spin_trylock(&up->lock))
|
||||
return false;
|
||||
|
||||
if (!__uart_port_nbcon_try_acquire(up)) {
|
||||
spin_unlock(&up->lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -638,7 +740,15 @@ static inline bool uart_port_trylock(struct uart_port *up)
|
||||
*/
|
||||
static inline bool uart_port_trylock_irqsave(struct uart_port *up, unsigned long *flags)
|
||||
{
|
||||
return spin_trylock_irqsave(&up->lock, *flags);
|
||||
if (!spin_trylock_irqsave(&up->lock, *flags))
|
||||
return false;
|
||||
|
||||
if (!__uart_port_nbcon_try_acquire(up)) {
|
||||
spin_unlock_irqrestore(&up->lock, *flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -647,6 +757,7 @@ static inline bool uart_port_trylock_irqsave(struct uart_port *up, unsigned long
|
||||
*/
|
||||
static inline void uart_port_unlock(struct uart_port *up)
|
||||
{
|
||||
__uart_port_nbcon_release(up);
|
||||
spin_unlock(&up->lock);
|
||||
}
|
||||
|
||||
@ -656,6 +767,7 @@ static inline void uart_port_unlock(struct uart_port *up)
|
||||
*/
|
||||
static inline void uart_port_unlock_irq(struct uart_port *up)
|
||||
{
|
||||
__uart_port_nbcon_release(up);
|
||||
spin_unlock_irq(&up->lock);
|
||||
}
|
||||
|
||||
@ -666,6 +778,7 @@ static inline void uart_port_unlock_irq(struct uart_port *up)
|
||||
*/
|
||||
static inline void uart_port_unlock_irqrestore(struct uart_port *up, unsigned long flags)
|
||||
{
|
||||
__uart_port_nbcon_release(up);
|
||||
spin_unlock_irqrestore(&up->lock, flags);
|
||||
}
|
||||
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/context_tracking.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
|
||||
@ -573,8 +574,10 @@ static struct lock_trace *save_trace(void)
|
||||
if (!debug_locks_off_graph_unlock())
|
||||
return NULL;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
print_lockdep_off("BUG: MAX_STACK_TRACE_ENTRIES too low!");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@ -887,11 +890,13 @@ look_up_lock_class(const struct lockdep_map *lock, unsigned int subclass)
|
||||
if (unlikely(subclass >= MAX_LOCKDEP_SUBCLASSES)) {
|
||||
instrumentation_begin();
|
||||
debug_locks_off();
|
||||
nbcon_cpu_emergency_enter();
|
||||
printk(KERN_ERR
|
||||
"BUG: looking up invalid subclass: %u\n", subclass);
|
||||
printk(KERN_ERR
|
||||
"turning off the locking correctness validator.\n");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
instrumentation_end();
|
||||
return NULL;
|
||||
}
|
||||
@ -968,11 +973,13 @@ static bool assign_lock_key(struct lockdep_map *lock)
|
||||
else {
|
||||
/* Debug-check: all keys must be persistent! */
|
||||
debug_locks_off();
|
||||
nbcon_cpu_emergency_enter();
|
||||
pr_err("INFO: trying to register non-static key.\n");
|
||||
pr_err("The code is fine but needs lockdep annotation, or maybe\n");
|
||||
pr_err("you didn't initialize this object before use?\n");
|
||||
pr_err("turning off the locking correctness validator.\n");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1316,8 +1323,10 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
print_lockdep_off("BUG: MAX_LOCKDEP_KEYS too low!");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
return NULL;
|
||||
}
|
||||
nr_lock_classes++;
|
||||
@ -1349,11 +1358,13 @@ register_lock_class(struct lockdep_map *lock, unsigned int subclass, int force)
|
||||
if (verbose(class)) {
|
||||
graph_unlock();
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
printk("\nnew class %px: %s", class->key, class->name);
|
||||
if (class->name_version > 1)
|
||||
printk(KERN_CONT "#%d", class->name_version);
|
||||
printk(KERN_CONT "\n");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
|
||||
if (!graph_lock()) {
|
||||
return NULL;
|
||||
@ -1392,8 +1403,10 @@ static struct lock_list *alloc_list_entry(void)
|
||||
if (!debug_locks_off_graph_unlock())
|
||||
return NULL;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
print_lockdep_off("BUG: MAX_LOCKDEP_ENTRIES too low!");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
return NULL;
|
||||
}
|
||||
nr_list_entries++;
|
||||
@ -2039,6 +2052,8 @@ static noinline void print_circular_bug(struct lock_list *this,
|
||||
|
||||
depth = get_lock_depth(target);
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
print_circular_bug_header(target, depth, check_src, check_tgt);
|
||||
|
||||
parent = get_lock_parent(target);
|
||||
@ -2057,6 +2072,8 @@ static noinline void print_circular_bug(struct lock_list *this,
|
||||
|
||||
printk("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
static noinline void print_bfs_bug(int ret)
|
||||
@ -2569,6 +2586,8 @@ print_bad_irq_dependency(struct task_struct *curr,
|
||||
if (!debug_locks_off_graph_unlock() || debug_locks_silent)
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("=====================================================\n");
|
||||
pr_warn("WARNING: %s-safe -> %s-unsafe lock order detected\n",
|
||||
@ -2618,11 +2637,13 @@ print_bad_irq_dependency(struct task_struct *curr,
|
||||
pr_warn(" and %s-irq-unsafe lock:\n", irqclass);
|
||||
next_root->trace = save_trace();
|
||||
if (!next_root->trace)
|
||||
return;
|
||||
goto out;
|
||||
print_shortest_lock_dependencies(forwards_entry, next_root);
|
||||
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
out:
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
static const char *state_names[] = {
|
||||
@ -2987,6 +3008,8 @@ print_deadlock_bug(struct task_struct *curr, struct held_lock *prev,
|
||||
if (!debug_locks_off_graph_unlock() || debug_locks_silent)
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("============================================\n");
|
||||
pr_warn("WARNING: possible recursive locking detected\n");
|
||||
@ -3009,6 +3032,8 @@ print_deadlock_bug(struct task_struct *curr, struct held_lock *prev,
|
||||
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3606,6 +3631,8 @@ static void print_collision(struct task_struct *curr,
|
||||
struct held_lock *hlock_next,
|
||||
struct lock_chain *chain)
|
||||
{
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("============================\n");
|
||||
pr_warn("WARNING: chain_key collision\n");
|
||||
@ -3622,6 +3649,8 @@ static void print_collision(struct task_struct *curr,
|
||||
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -3712,8 +3741,10 @@ static inline int add_chain_cache(struct task_struct *curr,
|
||||
if (!debug_locks_off_graph_unlock())
|
||||
return 0;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
print_lockdep_off("BUG: MAX_LOCKDEP_CHAINS too low!");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
return 0;
|
||||
}
|
||||
chain->chain_key = chain_key;
|
||||
@ -3730,8 +3761,10 @@ static inline int add_chain_cache(struct task_struct *curr,
|
||||
if (!debug_locks_off_graph_unlock())
|
||||
return 0;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
print_lockdep_off("BUG: MAX_LOCKDEP_CHAIN_HLOCKS too low!");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3970,6 +4003,8 @@ print_usage_bug(struct task_struct *curr, struct held_lock *this,
|
||||
if (!debug_locks_off() || debug_locks_silent)
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("================================\n");
|
||||
pr_warn("WARNING: inconsistent lock state\n");
|
||||
@ -3998,6 +4033,8 @@ print_usage_bug(struct task_struct *curr, struct held_lock *this,
|
||||
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4032,6 +4069,8 @@ print_irq_inversion_bug(struct task_struct *curr,
|
||||
if (!debug_locks_off_graph_unlock() || debug_locks_silent)
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("========================================================\n");
|
||||
pr_warn("WARNING: possible irq lock inversion dependency detected\n");
|
||||
@ -4072,11 +4111,13 @@ print_irq_inversion_bug(struct task_struct *curr,
|
||||
pr_warn("\nthe shortest dependencies between 2nd lock and 1st lock:\n");
|
||||
root->trace = save_trace();
|
||||
if (!root->trace)
|
||||
return;
|
||||
goto out;
|
||||
print_shortest_lock_dependencies(other, root);
|
||||
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
out:
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4153,6 +4194,8 @@ void print_irqtrace_events(struct task_struct *curr)
|
||||
{
|
||||
const struct irqtrace_events *trace = &curr->irqtrace;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
printk("irq event stamp: %u\n", trace->irq_events);
|
||||
printk("hardirqs last enabled at (%u): [<%px>] %pS\n",
|
||||
trace->hardirq_enable_event, (void *)trace->hardirq_enable_ip,
|
||||
@ -4166,6 +4209,8 @@ void print_irqtrace_events(struct task_struct *curr)
|
||||
printk("softirqs last disabled at (%u): [<%px>] %pS\n",
|
||||
trace->softirq_disable_event, (void *)trace->softirq_disable_ip,
|
||||
(void *)trace->softirq_disable_ip);
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
static int HARDIRQ_verbose(struct lock_class *class)
|
||||
@ -4686,10 +4731,12 @@ unlock:
|
||||
* We must printk outside of the graph_lock:
|
||||
*/
|
||||
if (ret == 2) {
|
||||
nbcon_cpu_emergency_enter();
|
||||
printk("\nmarked lock as {%s}:\n", usage_str[new_bit]);
|
||||
print_lock(this);
|
||||
print_irqtrace_events(curr);
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -4730,6 +4777,8 @@ print_lock_invalid_wait_context(struct task_struct *curr,
|
||||
if (debug_locks_silent)
|
||||
return 0;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("=============================\n");
|
||||
pr_warn("[ BUG: Invalid wait context ]\n");
|
||||
@ -4749,6 +4798,8 @@ print_lock_invalid_wait_context(struct task_struct *curr,
|
||||
pr_warn("stack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4956,6 +5007,8 @@ print_lock_nested_lock_not_held(struct task_struct *curr,
|
||||
if (debug_locks_silent)
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("==================================\n");
|
||||
pr_warn("WARNING: Nested lock was not taken\n");
|
||||
@ -4976,6 +5029,8 @@ print_lock_nested_lock_not_held(struct task_struct *curr,
|
||||
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
static int __lock_is_held(const struct lockdep_map *lock, int read);
|
||||
@ -5024,11 +5079,13 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
|
||||
debug_class_ops_inc(class);
|
||||
|
||||
if (very_verbose(class)) {
|
||||
nbcon_cpu_emergency_enter();
|
||||
printk("\nacquire class [%px] %s", class->key, class->name);
|
||||
if (class->name_version > 1)
|
||||
printk(KERN_CONT "#%d", class->name_version);
|
||||
printk(KERN_CONT "\n");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -5155,6 +5212,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
|
||||
#endif
|
||||
if (unlikely(curr->lockdep_depth >= MAX_LOCK_DEPTH)) {
|
||||
debug_locks_off();
|
||||
nbcon_cpu_emergency_enter();
|
||||
print_lockdep_off("BUG: MAX_LOCK_DEPTH too low!");
|
||||
printk(KERN_DEBUG "depth: %i max: %lu!\n",
|
||||
curr->lockdep_depth, MAX_LOCK_DEPTH);
|
||||
@ -5162,6 +5220,7 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
|
||||
lockdep_print_held_locks(current);
|
||||
debug_show_all_locks();
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -5181,6 +5240,8 @@ static void print_unlock_imbalance_bug(struct task_struct *curr,
|
||||
if (debug_locks_silent)
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("=====================================\n");
|
||||
pr_warn("WARNING: bad unlock balance detected!\n");
|
||||
@ -5197,6 +5258,8 @@ static void print_unlock_imbalance_bug(struct task_struct *curr,
|
||||
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
static noinstr int match_held_lock(const struct held_lock *hlock,
|
||||
@ -5901,6 +5964,8 @@ static void print_lock_contention_bug(struct task_struct *curr,
|
||||
if (debug_locks_silent)
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("=================================\n");
|
||||
pr_warn("WARNING: bad contention detected!\n");
|
||||
@ -5917,6 +5982,8 @@ static void print_lock_contention_bug(struct task_struct *curr,
|
||||
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
static void
|
||||
@ -6536,6 +6603,8 @@ print_freed_lock_bug(struct task_struct *curr, const void *mem_from,
|
||||
if (debug_locks_silent)
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("=========================\n");
|
||||
pr_warn("WARNING: held lock freed!\n");
|
||||
@ -6548,6 +6617,8 @@ print_freed_lock_bug(struct task_struct *curr, const void *mem_from,
|
||||
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
static inline int not_in_range(const void* mem_from, unsigned long mem_len,
|
||||
@ -6594,6 +6665,8 @@ static void print_held_locks_bug(void)
|
||||
if (debug_locks_silent)
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
pr_warn("\n");
|
||||
pr_warn("====================================\n");
|
||||
pr_warn("WARNING: %s/%d still has locks held!\n",
|
||||
@ -6603,6 +6676,8 @@ static void print_held_locks_bug(void)
|
||||
lockdep_print_held_locks(current);
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
void debug_check_no_locks_held(void)
|
||||
@ -6660,6 +6735,7 @@ asmlinkage __visible void lockdep_sys_exit(void)
|
||||
if (unlikely(curr->lockdep_depth)) {
|
||||
if (!debug_locks_off())
|
||||
return;
|
||||
nbcon_cpu_emergency_enter();
|
||||
pr_warn("\n");
|
||||
pr_warn("================================================\n");
|
||||
pr_warn("WARNING: lock held when returning to user space!\n");
|
||||
@ -6668,6 +6744,7 @@ asmlinkage __visible void lockdep_sys_exit(void)
|
||||
pr_warn("%s/%d is leaving the kernel with locks still held!\n",
|
||||
curr->comm, curr->pid);
|
||||
lockdep_print_held_locks(curr);
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
/*
|
||||
@ -6684,6 +6761,7 @@ void lockdep_rcu_suspicious(const char *file, const int line, const char *s)
|
||||
bool rcu = warn_rcu_enter();
|
||||
|
||||
/* Note: the following can be executed concurrently, so be careful. */
|
||||
nbcon_cpu_emergency_enter();
|
||||
pr_warn("\n");
|
||||
pr_warn("=============================\n");
|
||||
pr_warn("WARNING: suspicious RCU usage\n");
|
||||
@ -6722,6 +6800,7 @@ void lockdep_rcu_suspicious(const char *file, const int line, const char *s)
|
||||
lockdep_print_held_locks(curr);
|
||||
pr_warn("\nstack backtrace:\n");
|
||||
dump_stack();
|
||||
nbcon_cpu_emergency_exit();
|
||||
warn_rcu_exit(rcu);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(lockdep_rcu_suspicious);
|
||||
|
@ -374,6 +374,8 @@ void panic(const char *fmt, ...)
|
||||
|
||||
panic_other_cpus_shutdown(_crash_kexec_post_notifiers);
|
||||
|
||||
printk_legacy_allow_panic_sync();
|
||||
|
||||
/*
|
||||
* Run any panic handlers, including those that might need to
|
||||
* add information to the kmsg dump output.
|
||||
@ -463,6 +465,7 @@ void panic(const char *fmt, ...)
|
||||
* Explicitly flush the kernel log buffer one last time.
|
||||
*/
|
||||
console_flush_on_panic(CONSOLE_FLUSH_PENDING);
|
||||
nbcon_atomic_flush_unsafe();
|
||||
|
||||
local_irq_enable();
|
||||
for (i = 0; ; i += PANIC_TIMER_STEP) {
|
||||
@ -682,6 +685,7 @@ bool oops_may_print(void)
|
||||
*/
|
||||
void oops_enter(void)
|
||||
{
|
||||
nbcon_cpu_emergency_enter();
|
||||
tracing_off();
|
||||
/* can't trust the integrity of the kernel anymore: */
|
||||
debug_locks_off();
|
||||
@ -704,6 +708,7 @@ void oops_exit(void)
|
||||
{
|
||||
do_oops_enter_exit();
|
||||
print_oops_end_marker();
|
||||
nbcon_cpu_emergency_exit();
|
||||
kmsg_dump(KMSG_DUMP_OOPS);
|
||||
}
|
||||
|
||||
@ -715,6 +720,8 @@ struct warn_args {
|
||||
void __warn(const char *file, int line, void *caller, unsigned taint,
|
||||
struct pt_regs *regs, struct warn_args *args)
|
||||
{
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
disable_trace_on_warning();
|
||||
|
||||
if (file)
|
||||
@ -750,6 +757,8 @@ void __warn(const char *file, int line, void *caller, unsigned taint,
|
||||
|
||||
/* Just a warning, don't kill lockdep. */
|
||||
add_taint(taint, LOCKDEP_STILL_OK);
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BUG
|
||||
|
@ -2,11 +2,12 @@
|
||||
/*
|
||||
* internal.h - printk internal definitions
|
||||
*/
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/console.h>
|
||||
#include "printk_ringbuffer.h"
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
|
||||
struct ctl_table;
|
||||
void __init printk_sysctl_init(void);
|
||||
int devkmsg_sysctl_set_loglvl(const struct ctl_table *table, int write,
|
||||
void *buffer, size_t *lenp, loff_t *ppos);
|
||||
@ -20,6 +21,19 @@ int devkmsg_sysctl_set_loglvl(const struct ctl_table *table, int write,
|
||||
(con->flags & CON_BOOT) ? "boot" : "", \
|
||||
con->name, con->index, ##__VA_ARGS__)
|
||||
|
||||
/*
|
||||
* Identify if legacy printing is forced in a dedicated kthread. If
|
||||
* true, all printing via console lock occurs within a dedicated
|
||||
* legacy printer thread. The only exception is on panic, after the
|
||||
* nbcon consoles have had their chance to print the panic messages
|
||||
* first.
|
||||
*/
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
# define force_legacy_kthread() (true)
|
||||
#else
|
||||
# define force_legacy_kthread() (false)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PRINTK
|
||||
|
||||
#ifdef CONFIG_PRINTK_CALLER
|
||||
@ -43,7 +57,11 @@ enum printk_info_flags {
|
||||
LOG_CONT = 8, /* text is a fragment of a continuation line */
|
||||
};
|
||||
|
||||
struct printk_ringbuffer;
|
||||
struct dev_printk_info;
|
||||
|
||||
extern struct printk_ringbuffer *prb;
|
||||
extern bool printk_kthreads_running;
|
||||
|
||||
__printf(4, 0)
|
||||
int vprintk_store(int facility, int level,
|
||||
@ -53,6 +71,9 @@ int vprintk_store(int facility, int level,
|
||||
__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);
|
||||
|
||||
bool printk_percpu_data_ready(void);
|
||||
|
||||
#define printk_safe_enter_irqsave(flags) \
|
||||
@ -68,15 +89,85 @@ bool printk_percpu_data_ready(void);
|
||||
} while (0)
|
||||
|
||||
void defer_console_output(void);
|
||||
bool is_printk_legacy_deferred(void);
|
||||
|
||||
u16 printk_parse_prefix(const char *text, int *level,
|
||||
enum printk_info_flags *flags);
|
||||
void console_lock_spinning_enable(void);
|
||||
int console_lock_spinning_disable_and_check(int cookie);
|
||||
|
||||
u64 nbcon_seq_read(struct console *con);
|
||||
void nbcon_seq_force(struct console *con, u64 seq);
|
||||
bool nbcon_alloc(struct console *con);
|
||||
void nbcon_init(struct console *con);
|
||||
void nbcon_free(struct console *con);
|
||||
enum nbcon_prio nbcon_get_default_prio(void);
|
||||
void nbcon_atomic_flush_pending(void);
|
||||
bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
|
||||
int cookie, bool use_atomic);
|
||||
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
|
||||
*/
|
||||
static inline void nbcon_kthread_wake(struct console *con)
|
||||
{
|
||||
/*
|
||||
* Guarantee any new records can be seen by tasks preparing to wait
|
||||
* before this context checks if the rcuwait is empty.
|
||||
*
|
||||
* The full memory barrier in rcuwait_wake_up() pairs with the full
|
||||
* memory barrier within set_current_state() of
|
||||
* ___rcuwait_wait_event(), which is called after prepare_to_rcuwait()
|
||||
* adds the waiter but before it has checked the wait condition.
|
||||
*
|
||||
* This pairs with nbcon_kthread_func:A.
|
||||
*/
|
||||
rcuwait_wake_up(&con->rcuwait); /* LMM(nbcon_kthread_wake:A) */
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
@ -84,6 +175,8 @@ void nbcon_free(struct console *con);
|
||||
#define PRINTK_MESSAGE_MAX 0
|
||||
#define PRINTKRB_RECORD_MAX 0
|
||||
|
||||
#define printk_kthreads_running (false)
|
||||
|
||||
/*
|
||||
* In !PRINTK builds we still export console_sem
|
||||
* semaphore and some of console functions (console_unlock()/etc.), so
|
||||
@ -93,14 +186,119 @@ void nbcon_free(struct console *con);
|
||||
#define printk_safe_exit_irqrestore(flags) local_irq_restore(flags)
|
||||
|
||||
static inline bool printk_percpu_data_ready(void) { return false; }
|
||||
static inline void defer_console_output(void) { }
|
||||
static inline bool is_printk_legacy_deferred(void) { return false; }
|
||||
static inline u64 nbcon_seq_read(struct console *con) { return 0; }
|
||||
static inline void nbcon_seq_force(struct console *con, u64 seq) { }
|
||||
static inline bool nbcon_alloc(struct console *con) { return false; }
|
||||
static inline void nbcon_init(struct console *con) { }
|
||||
static inline void nbcon_free(struct console *con) { }
|
||||
static inline enum nbcon_prio nbcon_get_default_prio(void) { return NBCON_PRIO_NONE; }
|
||||
static inline void nbcon_atomic_flush_pending(void) { }
|
||||
static inline bool nbcon_legacy_emit_next_record(struct console *con, bool *handover,
|
||||
int cookie, bool use_atomic) { return false; }
|
||||
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;
|
||||
extern bool have_nbcon_console;
|
||||
extern bool have_legacy_console;
|
||||
extern bool legacy_allow_panic_sync;
|
||||
|
||||
/**
|
||||
* struct console_flush_type - Define available console flush methods
|
||||
* @nbcon_atomic: Flush directly using nbcon_atomic() callback
|
||||
* @nbcon_offload: Offload flush to printer thread
|
||||
* @legacy_direct: Call the legacy loop in this context
|
||||
* @legacy_offload: Offload the legacy loop into IRQ or legacy thread
|
||||
*
|
||||
* Note that the legacy loop also flushes the nbcon consoles.
|
||||
*/
|
||||
struct console_flush_type {
|
||||
bool nbcon_atomic;
|
||||
bool nbcon_offload;
|
||||
bool legacy_direct;
|
||||
bool legacy_offload;
|
||||
};
|
||||
|
||||
/*
|
||||
* Identify which console flushing methods should be used in the context of
|
||||
* the caller.
|
||||
*/
|
||||
static inline void printk_get_console_flush_type(struct console_flush_type *ft)
|
||||
{
|
||||
memset(ft, 0, sizeof(*ft));
|
||||
|
||||
switch (nbcon_get_default_prio()) {
|
||||
case NBCON_PRIO_NORMAL:
|
||||
if (have_nbcon_console && !have_boot_console) {
|
||||
if (printk_kthreads_running)
|
||||
ft->nbcon_offload = true;
|
||||
else
|
||||
ft->nbcon_atomic = true;
|
||||
}
|
||||
|
||||
/* Legacy consoles are flushed directly when possible. */
|
||||
if (have_legacy_console || have_boot_console) {
|
||||
if (!is_printk_legacy_deferred())
|
||||
ft->legacy_direct = true;
|
||||
else
|
||||
ft->legacy_offload = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case NBCON_PRIO_EMERGENCY:
|
||||
if (have_nbcon_console && !have_boot_console)
|
||||
ft->nbcon_atomic = true;
|
||||
|
||||
/* Legacy consoles are flushed directly when possible. */
|
||||
if (have_legacy_console || have_boot_console) {
|
||||
if (!is_printk_legacy_deferred())
|
||||
ft->legacy_direct = true;
|
||||
else
|
||||
ft->legacy_offload = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case NBCON_PRIO_PANIC:
|
||||
/*
|
||||
* In panic, the nbcon consoles will directly print. But
|
||||
* only allowed if there are no boot consoles.
|
||||
*/
|
||||
if (have_nbcon_console && !have_boot_console)
|
||||
ft->nbcon_atomic = true;
|
||||
|
||||
if (have_legacy_console || have_boot_console) {
|
||||
/*
|
||||
* This is the same decision as NBCON_PRIO_NORMAL
|
||||
* except that offloading never occurs in panic.
|
||||
*
|
||||
* Note that console_flush_on_panic() will flush
|
||||
* legacy consoles anyway, even if unsafe.
|
||||
*/
|
||||
if (!is_printk_legacy_deferred())
|
||||
ft->legacy_direct = true;
|
||||
|
||||
/*
|
||||
* In panic, if nbcon atomic printing occurs,
|
||||
* the legacy consoles must remain silent until
|
||||
* explicitly allowed.
|
||||
*/
|
||||
if (ft->nbcon_atomic && !legacy_allow_panic_sync)
|
||||
ft->legacy_direct = false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
extern struct printk_buffers printk_shared_pbufs;
|
||||
|
||||
/**
|
||||
@ -135,4 +333,5 @@ bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
|
||||
|
||||
#ifdef CONFIG_PRINTK
|
||||
void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped);
|
||||
void console_prepend_replay(struct printk_message *pmsg);
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,10 @@
|
||||
#define _KERNEL_PRINTK_RINGBUFFER_H
|
||||
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Meta information about each stored message.
|
||||
@ -120,7 +123,7 @@ enum desc_state {
|
||||
|
||||
#define _DATA_SIZE(sz_bits) (1UL << (sz_bits))
|
||||
#define _DESCS_COUNT(ct_bits) (1U << (ct_bits))
|
||||
#define DESC_SV_BITS (sizeof(unsigned long) * 8)
|
||||
#define DESC_SV_BITS BITS_PER_LONG
|
||||
#define DESC_FLAGS_SHIFT (DESC_SV_BITS - 2)
|
||||
#define DESC_FLAGS_MASK (3UL << DESC_FLAGS_SHIFT)
|
||||
#define DESC_STATE(sv) (3UL & (sv >> DESC_FLAGS_SHIFT))
|
||||
@ -401,10 +404,12 @@ u64 prb_next_reserve_seq(struct printk_ringbuffer *rb);
|
||||
|
||||
#define __u64seq_to_ulseq(u64seq) (u64seq)
|
||||
#define __ulseq_to_u64seq(rb, ulseq) (ulseq)
|
||||
#define ULSEQ_MAX(rb) (-1)
|
||||
|
||||
#else /* CONFIG_64BIT */
|
||||
|
||||
#define __u64seq_to_ulseq(u64seq) ((u32)u64seq)
|
||||
#define ULSEQ_MAX(rb) __u64seq_to_ulseq(prb_first_seq(rb) + 0x80000000UL)
|
||||
|
||||
static inline u64 __ulseq_to_u64seq(struct printk_ringbuffer *rb, u32 ulseq)
|
||||
{
|
||||
|
@ -26,6 +26,29 @@ void __printk_safe_exit(void)
|
||||
this_cpu_dec(printk_context);
|
||||
}
|
||||
|
||||
void __printk_deferred_enter(void)
|
||||
{
|
||||
cant_migrate();
|
||||
__printk_safe_enter();
|
||||
}
|
||||
|
||||
void __printk_deferred_exit(void)
|
||||
{
|
||||
cant_migrate();
|
||||
__printk_safe_exit();
|
||||
}
|
||||
|
||||
bool is_printk_legacy_deferred(void)
|
||||
{
|
||||
/*
|
||||
* The per-CPU variable @printk_context can be read safely in any
|
||||
* context. CPU migration is always disabled when set.
|
||||
*/
|
||||
return (force_legacy_kthread() ||
|
||||
this_cpu_read(printk_context) ||
|
||||
in_nmi());
|
||||
}
|
||||
|
||||
asmlinkage int vprintk(const char *fmt, va_list args)
|
||||
{
|
||||
#ifdef CONFIG_KGDB_KDB
|
||||
@ -38,7 +61,7 @@ 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())
|
||||
if (is_printk_legacy_deferred())
|
||||
return vprintk_deferred(fmt, args);
|
||||
|
||||
/* No obstacles. */
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Authors: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/console.h>
|
||||
#include <linux/lockdep.h>
|
||||
|
||||
static void rcu_exp_handler(void *unused);
|
||||
@ -590,6 +591,9 @@ static void synchronize_rcu_expedited_wait(void)
|
||||
return;
|
||||
if (rcu_stall_is_suppressed())
|
||||
continue;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
j = jiffies;
|
||||
rcu_stall_notifier_call_chain(RCU_STALL_NOTIFY_EXP, (void *)(j - jiffies_start));
|
||||
trace_rcu_stall_warning(rcu_state.name, TPS("ExpeditedStall"));
|
||||
@ -643,6 +647,9 @@ static void synchronize_rcu_expedited_wait(void)
|
||||
rcu_exp_print_detail_task_stall_rnp(rnp);
|
||||
}
|
||||
jiffies_stall = 3 * rcu_exp_jiffies_till_stall_check() + 3;
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
|
||||
panic_on_rcu_stall();
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
* Author: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/console.h>
|
||||
#include <linux/kvm_para.h>
|
||||
#include <linux/rcu_notifier.h>
|
||||
|
||||
@ -605,6 +606,8 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps)
|
||||
if (rcu_stall_is_suppressed())
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
/*
|
||||
* OK, time to rat on our buddy...
|
||||
* See Documentation/RCU/stallwarn.rst for info on how to debug
|
||||
@ -657,6 +660,8 @@ static void print_other_cpu_stall(unsigned long gp_seq, unsigned long gps)
|
||||
rcu_check_gp_kthread_expired_fqs_timer();
|
||||
rcu_check_gp_kthread_starvation();
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
|
||||
panic_on_rcu_stall();
|
||||
|
||||
rcu_force_quiescent_state(); /* Kick them all. */
|
||||
@ -677,6 +682,8 @@ static void print_cpu_stall(unsigned long gps)
|
||||
if (rcu_stall_is_suppressed())
|
||||
return;
|
||||
|
||||
nbcon_cpu_emergency_enter();
|
||||
|
||||
/*
|
||||
* OK, time to rat on ourselves...
|
||||
* See Documentation/RCU/stallwarn.rst for info on how to debug
|
||||
@ -706,6 +713,8 @@ static void print_cpu_stall(unsigned long gps)
|
||||
jiffies + 3 * rcu_jiffies_till_stall_check() + 3);
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
|
||||
nbcon_cpu_emergency_exit();
|
||||
|
||||
panic_on_rcu_stall();
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user