printk: Add non-BKL (nbcon) console basic infrastructure
The current console/printk subsystem is protected by a Big Kernel Lock, (aka console_lock) which has ill defined semantics and is more or less stateless. This puts severe limitations on the console subsystem and makes forced takeover and output in emergency and panic situations a fragile endeavour that is based on try and pray. The goal of non-BKL (nbcon) consoles is to break out of the console lock jail and to provide a new infrastructure that avoids the pitfalls and also allows console drivers to be gradually converted over. The proposed infrastructure aims for the following properties: - Per console locking instead of global locking - Per console state that allows to make informed decisions - Stateful handover and takeover As a first step, state is added to struct console. The per console state is an atomic_t using a 32bit bit field. Reserve state bits, which will be populated later in the series. Wire it up into the console register/unregister functionality. It was decided to use a bitfield because using a plain u32 with mask/shift operations resulted in uncomprehensible code. Co-developed-by: John Ogness <john.ogness@linutronix.de> Signed-off-by: John Ogness <john.ogness@linutronix.de> Signed-off-by: Thomas Gleixner (Intel) <tglx@linutronix.de> Reviewed-by: Petr Mladek <pmladek@suse.com> Signed-off-by: Petr Mladek <pmladek@suse.com> Link: https://lore.kernel.org/r/20230916192007.608398-2-john.ogness@linutronix.de
This commit is contained in:
parent
01a46efcd8
commit
6b93bb41f6
|
@ -156,6 +156,8 @@ static inline int con_debug_leave(void)
|
||||||
* /dev/kmesg which requires a larger output buffer.
|
* /dev/kmesg which requires a larger output buffer.
|
||||||
* @CON_SUSPENDED: Indicates if a console is suspended. If true, the
|
* @CON_SUSPENDED: Indicates if a console is suspended. If true, the
|
||||||
* printing callbacks must not be called.
|
* printing callbacks must not be called.
|
||||||
|
* @CON_NBCON: Console can operate outside of the legacy style console_lock
|
||||||
|
* constraints.
|
||||||
*/
|
*/
|
||||||
enum cons_flags {
|
enum cons_flags {
|
||||||
CON_PRINTBUFFER = BIT(0),
|
CON_PRINTBUFFER = BIT(0),
|
||||||
|
@ -166,8 +168,32 @@ enum cons_flags {
|
||||||
CON_BRL = BIT(5),
|
CON_BRL = BIT(5),
|
||||||
CON_EXTENDED = BIT(6),
|
CON_EXTENDED = BIT(6),
|
||||||
CON_SUSPENDED = BIT(7),
|
CON_SUSPENDED = BIT(7),
|
||||||
|
CON_NBCON = BIT(8),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct nbcon_state - console state for nbcon consoles
|
||||||
|
* @atom: Compound of the state fields for atomic operations
|
||||||
|
*
|
||||||
|
* To be used for reading and preparing of the value stored in the nbcon
|
||||||
|
* state variable @console::nbcon_state.
|
||||||
|
*/
|
||||||
|
struct nbcon_state {
|
||||||
|
union {
|
||||||
|
unsigned int atom;
|
||||||
|
struct {
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The nbcon_state struct is used to easily create and interpret values that
|
||||||
|
* are stored in the @console::nbcon_state variable. Ensure this struct stays
|
||||||
|
* within the size boundaries of the atomic variable's underlying type in
|
||||||
|
* order to avoid any accidental truncation.
|
||||||
|
*/
|
||||||
|
static_assert(sizeof(struct nbcon_state) <= sizeof(int));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct console - The console descriptor structure
|
* struct console - The console descriptor structure
|
||||||
* @name: The name of the console driver
|
* @name: The name of the console driver
|
||||||
|
@ -187,6 +213,8 @@ enum cons_flags {
|
||||||
* @dropped: Number of unreported dropped ringbuffer records
|
* @dropped: Number of unreported dropped ringbuffer records
|
||||||
* @data: Driver private data
|
* @data: Driver private data
|
||||||
* @node: hlist node for the console list
|
* @node: hlist node for the console list
|
||||||
|
*
|
||||||
|
* @nbcon_state: State for nbcon consoles
|
||||||
*/
|
*/
|
||||||
struct console {
|
struct console {
|
||||||
char name[16];
|
char name[16];
|
||||||
|
@ -206,6 +234,9 @@ struct console {
|
||||||
unsigned long dropped;
|
unsigned long dropped;
|
||||||
void *data;
|
void *data;
|
||||||
struct hlist_node node;
|
struct hlist_node node;
|
||||||
|
|
||||||
|
/* nbcon console specific members */
|
||||||
|
atomic_t __private nbcon_state;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_LOCKDEP
|
#ifdef CONFIG_LOCKDEP
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# SPDX-License-Identifier: GPL-2.0-only
|
# SPDX-License-Identifier: GPL-2.0-only
|
||||||
obj-y = printk.o
|
obj-y = printk.o
|
||||||
obj-$(CONFIG_PRINTK) += printk_safe.o
|
obj-$(CONFIG_PRINTK) += printk_safe.o nbcon.o
|
||||||
obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o
|
obj-$(CONFIG_A11Y_BRAILLE_CONSOLE) += braille.o
|
||||||
obj-$(CONFIG_PRINTK_INDEX) += index.o
|
obj-$(CONFIG_PRINTK_INDEX) += index.o
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
* internal.h - printk internal definitions
|
* internal.h - printk internal definitions
|
||||||
*/
|
*/
|
||||||
#include <linux/percpu.h>
|
#include <linux/percpu.h>
|
||||||
|
#include <linux/console.h>
|
||||||
|
|
||||||
#if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
|
#if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
|
||||||
void __init printk_sysctl_init(void);
|
void __init printk_sysctl_init(void);
|
||||||
|
@ -61,6 +62,10 @@ void defer_console_output(void);
|
||||||
|
|
||||||
u16 printk_parse_prefix(const char *text, int *level,
|
u16 printk_parse_prefix(const char *text, int *level,
|
||||||
enum printk_info_flags *flags);
|
enum printk_info_flags *flags);
|
||||||
|
|
||||||
|
void nbcon_init(struct console *con);
|
||||||
|
void nbcon_cleanup(struct console *con);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#define PRINTK_PREFIX_MAX 0
|
#define PRINTK_PREFIX_MAX 0
|
||||||
|
@ -76,6 +81,9 @@ u16 printk_parse_prefix(const char *text, int *level,
|
||||||
#define printk_safe_exit_irqrestore(flags) local_irq_restore(flags)
|
#define printk_safe_exit_irqrestore(flags) local_irq_restore(flags)
|
||||||
|
|
||||||
static inline bool printk_percpu_data_ready(void) { return false; }
|
static inline bool printk_percpu_data_ready(void) { return false; }
|
||||||
|
static inline void nbcon_init(struct console *con) { }
|
||||||
|
static inline void nbcon_cleanup(struct console *con) { }
|
||||||
|
|
||||||
#endif /* CONFIG_PRINTK */
|
#endif /* CONFIG_PRINTK */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
// Copyright (C) 2022 Linutronix GmbH, John Ogness
|
||||||
|
// Copyright (C) 2022 Intel, Thomas Gleixner
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/console.h>
|
||||||
|
#include "internal.h"
|
||||||
|
/*
|
||||||
|
* Printk console printing implementation for consoles which does not depend
|
||||||
|
* on the legacy style console_lock mechanism.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nbcon_state_set - Helper function to set the console state
|
||||||
|
* @con: Console to update
|
||||||
|
* @new: The new state to write
|
||||||
|
*
|
||||||
|
* Only to be used when the console is not yet or no longer visible in the
|
||||||
|
* system. Otherwise use nbcon_state_try_cmpxchg().
|
||||||
|
*/
|
||||||
|
static inline void nbcon_state_set(struct console *con, struct nbcon_state *new)
|
||||||
|
{
|
||||||
|
atomic_set(&ACCESS_PRIVATE(con, nbcon_state), new->atom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nbcon_state_read - Helper function to read the console state
|
||||||
|
* @con: Console to read
|
||||||
|
* @state: The state to store the result
|
||||||
|
*/
|
||||||
|
static inline void nbcon_state_read(struct console *con, struct nbcon_state *state)
|
||||||
|
{
|
||||||
|
state->atom = atomic_read(&ACCESS_PRIVATE(con, nbcon_state));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nbcon_state_try_cmpxchg() - Helper function for atomic_try_cmpxchg() on console state
|
||||||
|
* @con: Console to update
|
||||||
|
* @cur: Old/expected state
|
||||||
|
* @new: New state
|
||||||
|
*
|
||||||
|
* Return: True on success. False on fail and @cur is updated.
|
||||||
|
*/
|
||||||
|
static inline bool nbcon_state_try_cmpxchg(struct console *con, struct nbcon_state *cur,
|
||||||
|
struct nbcon_state *new)
|
||||||
|
{
|
||||||
|
return atomic_try_cmpxchg(&ACCESS_PRIVATE(con, nbcon_state), &cur->atom, new->atom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nbcon_init - Initialize the nbcon console specific data
|
||||||
|
* @con: Console to initialize
|
||||||
|
*/
|
||||||
|
void nbcon_init(struct console *con)
|
||||||
|
{
|
||||||
|
struct nbcon_state state = { };
|
||||||
|
|
||||||
|
nbcon_state_set(con, &state);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nbcon_cleanup - Cleanup the nbcon console specific data
|
||||||
|
* @con: Console to cleanup
|
||||||
|
*/
|
||||||
|
void nbcon_cleanup(struct console *con)
|
||||||
|
{
|
||||||
|
struct nbcon_state state = { };
|
||||||
|
|
||||||
|
nbcon_state_set(con, &state);
|
||||||
|
}
|
|
@ -3326,9 +3326,10 @@ static void try_enable_default_console(struct console *newcon)
|
||||||
newcon->flags |= CON_CONSDEV;
|
newcon->flags |= CON_CONSDEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define con_printk(lvl, con, fmt, ...) \
|
#define con_printk(lvl, con, fmt, ...) \
|
||||||
printk(lvl pr_fmt("%sconsole [%s%d] " fmt), \
|
printk(lvl pr_fmt("%s%sconsole [%s%d] " fmt), \
|
||||||
(con->flags & CON_BOOT) ? "boot" : "", \
|
(con->flags & CON_NBCON) ? "" : "legacy ", \
|
||||||
|
(con->flags & CON_BOOT) ? "boot" : "", \
|
||||||
con->name, con->index, ##__VA_ARGS__)
|
con->name, con->index, ##__VA_ARGS__)
|
||||||
|
|
||||||
static void console_init_seq(struct console *newcon, bool bootcon_registered)
|
static void console_init_seq(struct console *newcon, bool bootcon_registered)
|
||||||
|
@ -3488,6 +3489,9 @@ void register_console(struct console *newcon)
|
||||||
newcon->dropped = 0;
|
newcon->dropped = 0;
|
||||||
console_init_seq(newcon, bootcon_registered);
|
console_init_seq(newcon, bootcon_registered);
|
||||||
|
|
||||||
|
if (newcon->flags & CON_NBCON)
|
||||||
|
nbcon_init(newcon);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Put this console in the list - keep the
|
* Put this console in the list - keep the
|
||||||
* preferred driver at the head of the list.
|
* preferred driver at the head of the list.
|
||||||
|
@ -3579,6 +3583,9 @@ static int unregister_console_locked(struct console *console)
|
||||||
*/
|
*/
|
||||||
synchronize_srcu(&console_srcu);
|
synchronize_srcu(&console_srcu);
|
||||||
|
|
||||||
|
if (console->flags & CON_NBCON)
|
||||||
|
nbcon_cleanup(console);
|
||||||
|
|
||||||
console_sysfs_notify();
|
console_sysfs_notify();
|
||||||
|
|
||||||
if (console->exit)
|
if (console->exit)
|
||||||
|
|
Loading…
Reference in New Issue