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

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
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -156,6 +156,8 @@ static inline int con_debug_leave(void)
 *			/dev/kmesg which requires a larger output buffer.
 * @CON_SUSPENDED:	Indicates if a console is suspended. If true, the
 *			printing callbacks must not be called.
 * @CON_NBCON:		Console can operate outside of the legacy style console_lock
 *			constraints.
 */
enum cons_flags {
	CON_PRINTBUFFER		= BIT(0),
@@ -166,8 +168,32 @@ enum cons_flags {
	CON_BRL			= BIT(5),
	CON_EXTENDED		= BIT(6),
	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
 * @name:		The name of the console driver
@@ -187,6 +213,8 @@ enum cons_flags {
 * @dropped:		Number of unreported dropped ringbuffer records
 * @data:		Driver private data
 * @node:		hlist node for the console list
 *
 * @nbcon_state:	State for nbcon consoles
 */
struct console {
	char			name[16];
@@ -206,6 +234,9 @@ struct console {
	unsigned long		dropped;
	void			*data;
	struct hlist_node	node;

	/* nbcon console specific members */
	atomic_t		__private nbcon_state;
};

#ifdef CONFIG_LOCKDEP
+1 −1
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0-only
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_PRINTK_INDEX)	+= index.o

+8 −0
Original line number Diff line number Diff line
@@ -3,6 +3,7 @@
 * internal.h - printk internal definitions
 */
#include <linux/percpu.h>
#include <linux/console.h>

#if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
void __init printk_sysctl_init(void);
@@ -61,6 +62,10 @@ void defer_console_output(void);

u16 printk_parse_prefix(const char *text, int *level,
			enum printk_info_flags *flags);

void nbcon_init(struct console *con);
void nbcon_cleanup(struct console *con);

#else

#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)

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 */

/**

kernel/printk/nbcon.c

0 → 100644
+70 −0
Original line number Diff line number Diff line
// 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);
}
+10 −3
Original line number Diff line number Diff line
@@ -3327,7 +3327,8 @@ static void try_enable_default_console(struct console *newcon)
}

#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_NBCON) ? "" : "legacy ",	\
	       (con->flags & CON_BOOT) ? "boot" : "",		\
	       con->name, con->index, ##__VA_ARGS__)

@@ -3488,6 +3489,9 @@ void register_console(struct console *newcon)
	newcon->dropped = 0;
	console_init_seq(newcon, bootcon_registered);

	if (newcon->flags & CON_NBCON)
		nbcon_init(newcon);

	/*
	 * Put this console in the list - keep the
	 * preferred driver at the head of the list.
@@ -3579,6 +3583,9 @@ static int unregister_console_locked(struct console *console)
	 */
	synchronize_srcu(&console_srcu);

	if (console->flags & CON_NBCON)
		nbcon_cleanup(console);

	console_sysfs_notify();

	if (console->exit)