Commit 6b93bb41 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Petr Mladek

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: default avatarJohn Ogness <john.ogness@linutronix.de>
Signed-off-by: default avatarJohn Ogness <john.ogness@linutronix.de>
Signed-off-by: default avatarThomas Gleixner (Intel) <tglx@linutronix.de>
Reviewed-by: default avatarPetr Mladek <pmladek@suse.com>
Signed-off-by: default avatarPetr Mladek <pmladek@suse.com>
Link: https://lore.kernel.org/r/20230916192007.608398-2-john.ogness@linutronix.de
parent 01a46efc
...@@ -156,6 +156,8 @@ static inline int con_debug_leave(void) ...@@ -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 { ...@@ -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 { ...@@ -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 { ...@@ -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
......
# 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 @@ ...@@ -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); ...@@ -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, ...@@ -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 */
/** /**
......
// 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) ...@@ -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) ...@@ -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) ...@@ -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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment