Merge branch 'rework/nbcon-base' into for-linus
This commit is contained in:
commit
2966bd3698
|
@ -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,6 +1,6 @@
|
|||
# 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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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 @@ out:
|
|||
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,8 +2765,8 @@ 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 is_extended, bool may_suppress)
|
||||
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 @@ out:
|
|||
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 @@ out:
|
|||
*/
|
||||
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 @@ skip:
|
|||
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,11 +3164,17 @@ void console_flush_on_panic(enum con_flush_mode mode)
|
|||
|
||||
cookie = console_srcu_read_lock();
|
||||
for_each_console_srcu(c) {
|
||||
/*
|
||||
* This is an unsynchronized assignment, but the
|
||||
* kernel is in "hope and pray" mode anyway.
|
||||
*/
|
||||
c->seq = seq;
|
||||
flags = console_srcu_read_flags(c);
|
||||
|
||||
if (flags & CON_NBCON) {
|
||||
nbcon_seq_force(c, seq);
|
||||
} else {
|
||||
/*
|
||||
* 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;
|
||||
printk_seq = c->seq;
|
||||
|
||||
if (flags & CON_NBCON) {
|
||||
printk_seq = nbcon_seq_read(c);
|
||||
} else {
|
||||
printk_seq = c->seq;
|
||||
}
|
||||
|
||||
if (printk_seq < seq)
|
||||
diff += seq - printk_seq;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue