Commit 2966bd36 authored by Petr Mladek's avatar Petr Mladek
Browse files

Merge branch 'rework/nbcon-base' into for-linus

parents 86098bcd 98a04655
Loading
Loading
Loading
Loading
+129 −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,6 +168,111 @@ 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
 *
 * @req_prio:		The priority of a handover request
 * @prio:		The priority of the current owner
 * @unsafe:		Console is busy in a non takeover region
 * @unsafe_takeover:	A hostile takeover in an unsafe state happened in the
 *			past. The console cannot be safe until re-initialized.
 * @cpu:		The CPU on which the owner runs
 *
 * To be used for reading and preparing of the value stored in the nbcon
 * state variable @console::nbcon_state.
 *
 * The @prio and @req_prio fields are particularly important to allow
 * spin-waiting to timeout and give up without the risk of a waiter being
 * assigned the lock after giving up.
 */
struct nbcon_state {
	union {
		unsigned int	atom;
		struct {
			unsigned int prio		:  2;
			unsigned int req_prio		:  2;
			unsigned int unsafe		:  1;
			unsigned int unsafe_takeover	:  1;
			unsigned int cpu		: 24;
		};
	};
};

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

/**
 * nbcon_prio - console owner priority for nbcon consoles
 * @NBCON_PRIO_NONE:		Unused
 * @NBCON_PRIO_NORMAL:		Normal (non-emergency) usage
 * @NBCON_PRIO_EMERGENCY:	Emergency output (WARN/OOPS...)
 * @NBCON_PRIO_PANIC:		Panic output
 * @NBCON_PRIO_MAX:		The number of priority levels
 *
 * A higher priority context can takeover the console when it is
 * in the safe state. The final attempt to flush consoles in panic()
 * can be allowed to do so even in an unsafe state (Hope and pray).
 */
enum nbcon_prio {
	NBCON_PRIO_NONE = 0,
	NBCON_PRIO_NORMAL,
	NBCON_PRIO_EMERGENCY,
	NBCON_PRIO_PANIC,
	NBCON_PRIO_MAX,
};

struct console;
struct printk_buffers;

/**
 * struct nbcon_context - Context for console acquire/release
 * @console:			The associated console
 * @spinwait_max_us:		Limit for spin-wait acquire
 * @prio:			Priority of the context
 * @allow_unsafe_takeover:	Allow performing takeover even if unsafe. Can
 *				be used only with NBCON_PRIO_PANIC @prio. It
 *				might cause a system freeze when the console
 *				is used later.
 * @backlog:			Ringbuffer has pending records
 * @pbufs:			Pointer to the text buffer for this context
 * @seq:			The sequence number to print for this context
 */
struct nbcon_context {
	/* members set by caller */
	struct console		*console;
	unsigned int		spinwait_max_us;
	enum nbcon_prio		prio;
	unsigned int		allow_unsafe_takeover	: 1;

	/* members set by emit */
	unsigned int		backlog			: 1;

	/* members set by acquire */
	struct printk_buffers	*pbufs;
	u64			seq;
};

/**
 * struct nbcon_write_context - Context handed to the nbcon write callbacks
 * @ctxt:		The core console context
 * @outbuf:		Pointer to the text buffer for output
 * @len:		Length to write
 * @unsafe_takeover:	If a hostile takeover in an unsafe state has occurred
 */
struct nbcon_write_context {
	struct nbcon_context	__private ctxt;
	char			*outbuf;
	unsigned int		len;
	bool			unsafe_takeover;
};

/**
@@ -187,6 +294,11 @@ enum cons_flags {
 * @dropped:		Number of unreported dropped ringbuffer records
 * @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
 * @pbufs:		Pointer to nbcon private buffer
 */
struct console {
	char			name[16];
@@ -206,6 +318,13 @@ struct console {
	unsigned long		dropped;
	void			*data;
	struct hlist_node	node;

	/* nbcon console specific members */
	bool			(*write_atomic)(struct console *con,
						struct nbcon_write_context *wctxt);
	atomic_t		__private nbcon_state;
	atomic_long_t		__private nbcon_seq;
	struct printk_buffers	*pbufs;
};

#ifdef CONFIG_LOCKDEP
@@ -332,6 +451,16 @@ static inline bool console_is_registered(const struct console *con)
	lockdep_assert_console_list_lock_held();			\
	hlist_for_each_entry(con, &console_list, node)

#ifdef CONFIG_PRINTK
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);
#else
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; }
#endif

extern int console_set_on_cmdline;
extern struct console *early_console;

+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

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

#if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL)
void __init printk_sysctl_init(void);
@@ -12,6 +14,12 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write,
#define printk_sysctl_init() do { } while (0)
#endif

#define con_printk(lvl, con, 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__)

#ifdef CONFIG_PRINTK

#ifdef CONFIG_PRINTK_CALLER
@@ -35,6 +43,8 @@ enum printk_info_flags {
	LOG_CONT	= 8,	/* text is a fragment of a continuation line */
};

extern struct printk_ringbuffer *prb;

__printf(4, 0)
int vprintk_store(int facility, int level,
		  const struct dev_printk_info *dev_info,
@@ -61,6 +71,13 @@ void defer_console_output(void);

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

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

#else

#define PRINTK_PREFIX_MAX	0
@@ -76,8 +93,16 @@ 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 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) { }

#endif /* CONFIG_PRINTK */

extern struct printk_buffers printk_shared_pbufs;

/**
 * struct printk_buffers - Buffers to read/format/output printk messages.
 * @outbuf:	After formatting, contains text to output.
@@ -105,3 +130,9 @@ struct printk_message {
};

bool other_cpu_in_panic(void);
bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
			     bool is_extended, bool may_supress);

#ifdef CONFIG_PRINTK
void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped);
#endif

kernel/printk/nbcon.c

0 → 100644
+1029 −0

File added.

Preview size limit exceeded, changes collapsed.

+73 −49
Original line number Diff line number Diff line
@@ -102,12 +102,6 @@ DEFINE_STATIC_SRCU(console_srcu);
 */
int __read_mostly suppress_printk;

/*
 * During panic, heavy printk by other CPUs can delay the
 * panic and risk deadlock on console resources.
 */
static int __read_mostly suppress_panic_printk;

#ifdef CONFIG_LOCKDEP
static struct lockdep_map console_lock_dep_map = {
	.name = "console_lock"
@@ -445,6 +439,12 @@ static int console_msg_format = MSG_FORMAT_DEFAULT;
static DEFINE_MUTEX(syslog_lock);

#ifdef CONFIG_PRINTK
/*
 * During panic, heavy printk by other CPUs can delay the
 * panic and risk deadlock on console resources.
 */
static int __read_mostly suppress_panic_printk;

DECLARE_WAIT_QUEUE_HEAD(log_wait);
/* All 3 protected by @syslog_lock. */
/* the next printk record to read by syslog(READ) or /proc/kmsg */
@@ -494,7 +494,7 @@ _DEFINE_PRINTKRB(printk_rb_static, CONFIG_LOG_BUF_SHIFT - PRB_AVGBITS,

static struct printk_ringbuffer printk_rb_dynamic;

static struct printk_ringbuffer *prb = &printk_rb_static;
struct printk_ringbuffer *prb = &printk_rb_static;

/*
 * We cannot access per-CPU data (e.g. per-CPU flush irq_work) before
@@ -698,9 +698,6 @@ static ssize_t msg_print_ext_body(char *buf, size_t size,
	return len;
}

static bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
				    bool is_extended, bool may_supress);

/* /dev/kmsg - userspace message inject/listen interface */
struct devkmsg_user {
	atomic64_t seq;
@@ -2348,22 +2345,6 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre

static u64 syslog_seq;

static size_t record_print_text(const struct printk_record *r,
				bool syslog, bool time)
{
	return 0;
}
static ssize_t info_print_ext_header(char *buf, size_t size,
				     struct printk_info *info)
{
	return 0;
}
static ssize_t msg_print_ext_body(char *buf, size_t size,
				  char *text, size_t text_len,
				  struct dev_printk_info *dev_info) { return 0; }
static void console_lock_spinning_enable(void) { }
static int console_lock_spinning_disable_and_check(int cookie) { return 0; }
static bool suppress_message_printing(int level) { return false; }
static bool pr_flush(int timeout_ms, bool reset_on_progress) { return true; }
static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress) { return true; }

@@ -2717,6 +2698,8 @@ static void __console_unlock(void)
	up_console_sem();
}

#ifdef CONFIG_PRINTK

/*
 * Prepend the message in @pmsg->pbufs->outbuf with a "dropped message". This
 * is achieved by shifting the existing message over and inserting the dropped
@@ -2731,8 +2714,7 @@ static void __console_unlock(void)
 *
 * If @pmsg->pbufs->outbuf is modified, @pmsg->outbuf_len is updated.
 */
#ifdef CONFIG_PRINTK
static void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped)
void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped)
{
	struct printk_buffers *pbufs = pmsg->pbufs;
	const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf);
@@ -2763,9 +2745,6 @@ static void console_prepend_dropped(struct printk_message *pmsg, unsigned long d
	memcpy(outbuf, scratchbuf, len);
	pmsg->outbuf_len += len;
}
#else
#define console_prepend_dropped(pmsg, dropped)
#endif /* CONFIG_PRINTK */

/*
 * Read and format the specified record (or a later record if the specified
@@ -2786,7 +2765,7 @@ static void console_prepend_dropped(struct printk_message *pmsg, unsigned long d
 * of @pmsg are valid. (See the documentation of struct printk_message
 * for information about the @pmsg fields.)
 */
static bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
			     bool is_extended, bool may_suppress)
{
	static int panic_console_dropped;
@@ -2845,6 +2824,13 @@ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
	return true;
}

/*
 * Used as the printk buffers for non-panic, serialized console printing.
 * This is for legacy (!CON_NBCON) as well as all boot (CON_BOOT) consoles.
 * Its usage requires the console_lock held.
 */
struct printk_buffers printk_shared_pbufs;

/*
 * Print one record for the given console. The record printed is whatever
 * record is the next available record for the given console.
@@ -2862,12 +2848,10 @@ static bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
 */
static bool console_emit_next_record(struct console *con, bool *handover, int cookie)
{
	static struct printk_buffers pbufs;

	bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED;
	char *outbuf = &pbufs.outbuf[0];
	char *outbuf = &printk_shared_pbufs.outbuf[0];
	struct printk_message pmsg = {
		.pbufs = &pbufs,
		.pbufs = &printk_shared_pbufs,
	};
	unsigned long flags;

@@ -2918,6 +2902,16 @@ static bool console_emit_next_record(struct console *con, bool *handover, int co
	return true;
}

#else

static bool console_emit_next_record(struct console *con, bool *handover, int cookie)
{
	*handover = false;
	return false;
}

#endif /* CONFIG_PRINTK */

/*
 * Print out all remaining records to all consoles.
 *
@@ -3162,6 +3156,7 @@ void console_flush_on_panic(enum con_flush_mode mode)

	if (mode == CONSOLE_REPLAY_ALL) {
		struct console *c;
		short flags;
		int cookie;
		u64 seq;

@@ -3169,12 +3164,18 @@ void console_flush_on_panic(enum con_flush_mode mode)

		cookie = console_srcu_read_lock();
		for_each_console_srcu(c) {
			flags = console_srcu_read_flags(c);

			if (flags & CON_NBCON) {
				nbcon_seq_force(c, seq);
			} else {
				/*
			 * This is an unsynchronized assignment, but the
			 * kernel is in "hope and pray" mode anyway.
				 * This is an unsynchronized assignment. On
				 * panic legacy consoles are only best effort.
				 */
				c->seq = seq;
			}
		}
		console_srcu_read_unlock(cookie);
	}

@@ -3325,11 +3326,6 @@ static void try_enable_default_console(struct console *newcon)
		newcon->flags |= CON_CONSDEV;
}

#define con_printk(lvl, con, fmt, ...)			\
	printk(lvl pr_fmt("%sconsole [%s%d] " fmt),	\
	       (con->flags & CON_BOOT) ? "boot" : "",	\
	       con->name, con->index, ##__VA_ARGS__)

static void console_init_seq(struct console *newcon, bool bootcon_registered)
{
	struct console *con;
@@ -3443,6 +3439,15 @@ void register_console(struct console *newcon)
		goto unlock;
	}

	if (newcon->flags & CON_NBCON) {
		/*
		 * Ensure the nbcon console buffers can be allocated
		 * before modifying any global data.
		 */
		if (!nbcon_alloc(newcon))
			goto unlock;
	}

	/*
	 * See if we want to enable this console driver by default.
	 *
@@ -3470,8 +3475,11 @@ void register_console(struct console *newcon)
		err = try_enable_preferred_console(newcon, false);

	/* printk() messages are not printed to the Braille console. */
	if (err || newcon->flags & CON_BRL)
	if (err || newcon->flags & CON_BRL) {
		if (newcon->flags & CON_NBCON)
			nbcon_free(newcon);
		goto unlock;
	}

	/*
	 * If we have a bootconsole, and are switching to a real console,
@@ -3487,6 +3495,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.
@@ -3578,6 +3589,9 @@ static int unregister_console_locked(struct console *console)
	 */
	synchronize_srcu(&console_srcu);

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

	console_sysfs_notify();

	if (console->exit)
@@ -3732,6 +3746,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
	struct console *c;
	u64 last_diff = 0;
	u64 printk_seq;
	short flags;
	int cookie;
	u64 diff;
	u64 seq;
@@ -3762,6 +3777,9 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
		for_each_console_srcu(c) {
			if (con && con != c)
				continue;

			flags = console_srcu_read_flags(c);

			/*
			 * If consoles are not usable, it cannot be expected
			 * that they make forward progress, so only increment
@@ -3769,7 +3787,13 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
			 */
			if (!console_is_usable(c))
				continue;

			if (flags & CON_NBCON) {
				printk_seq = nbcon_seq_read(c);
			} else {
				printk_seq = c->seq;
			}

			if (printk_seq < seq)
				diff += seq - printk_seq;
		}