Commit 2a80532c authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'printk-for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux

Pull printk updates from Petr Mladek:

 - Another preparation step for introducing printk kthreads. The main
   piece is a per-console lock with several features:

    - Support three priorities: normal, emergency, and panic. They will
      be defined by a context where the lock is taken. A context with a
      higher priority is allowed to take over the lock from a context
      with a lower one.

      The plan is to use the emergency context for Oops and WARN()
      messages, and also by watchdogs.

      The panic() context will be used on panic CPU.

    - The owner might enter/exit regions where it is not safe to take
      over the lock. It allows the take over the lock a safe way in the
      middle of a message.

      For example, serial drivers emit characters one by one. And the
      serial port is in a safe state in between.

      Only the final console_flush_in_panic() will be allowed to take
      over the lock even in the unsafe state (last chance, pray, and
      hope).

    - A higher priority context might busy wait with a timeout. The
      current owner is informed about the waiter and releases the lock
      on exit from the unsafe state.

    - The new lock is safe even in atomic contexts, including NMI.

   Another change is a safe manipulation of per-console sequence number
   counter under the new lock.

 - simple_strntoull() micro-optimization

 - Reduce pr_flush() pooling time.

 - Calm down false warning about possible buffer invalid access to
   console buffers when CONFIG_PRINTK is disabled.

[ .. and Thomas Gleixner wants to point out that while several of the
  commits are attributed to him, he only authored the early versions of
  said commits, and that John Ogness and Petr Mladek have been the ones
  who sorted out the details and really should be those who get the
  credit   - Linus ]

* tag 'printk-for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/printk/linux:
  vsprintf: uninline simple_strntoull(), reorder arguments
  printk: printk: Remove unnecessary statements'len = 0;'
  printk: Reduce pr_flush() pooling time
  printk: fix illegal pbufs access for !CONFIG_PRINTK
  printk: nbcon: Allow drivers to mark unsafe regions and check state
  printk: nbcon: Add emit function and callback function for atomic printing
  printk: nbcon: Add sequence handling
  printk: nbcon: Add ownership state functions
  printk: nbcon: Add buffer management
  printk: Make static printk buffers available to nbcon
  printk: nbcon: Add acquire/release logic
  printk: Add non-BKL (nbcon) console basic infrastructure
parents 00657bb3 2966bd36
...@@ -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,6 +168,111 @@ enum cons_flags { ...@@ -166,6 +168,111 @@ 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
*
* @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 { ...@@ -187,6 +294,11 @@ 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
*
* @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 { struct console {
char name[16]; char name[16];
...@@ -206,6 +318,13 @@ struct console { ...@@ -206,6 +318,13 @@ struct console {
unsigned long dropped; unsigned long dropped;
void *data; void *data;
struct hlist_node node; 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 #ifdef CONFIG_LOCKDEP
...@@ -332,6 +451,16 @@ static inline bool console_is_registered(const struct console *con) ...@@ -332,6 +451,16 @@ static inline bool console_is_registered(const struct console *con)
lockdep_assert_console_list_lock_held(); \ lockdep_assert_console_list_lock_held(); \
hlist_for_each_entry(con, &console_list, node) 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 int console_set_on_cmdline;
extern struct console *early_console; extern struct console *early_console;
......
# 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,8 @@ ...@@ -3,6 +3,8 @@
* internal.h - printk internal definitions * internal.h - printk internal definitions
*/ */
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/console.h>
#include "printk_ringbuffer.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);
...@@ -12,6 +14,12 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write, ...@@ -12,6 +14,12 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write,
#define printk_sysctl_init() do { } while (0) #define printk_sysctl_init() do { } while (0)
#endif #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
#ifdef CONFIG_PRINTK_CALLER #ifdef CONFIG_PRINTK_CALLER
...@@ -35,6 +43,8 @@ enum printk_info_flags { ...@@ -35,6 +43,8 @@ enum printk_info_flags {
LOG_CONT = 8, /* text is a fragment of a continuation line */ LOG_CONT = 8, /* text is a fragment of a continuation line */
}; };
extern struct printk_ringbuffer *prb;
__printf(4, 0) __printf(4, 0)
int vprintk_store(int facility, int level, int vprintk_store(int facility, int level,
const struct dev_printk_info *dev_info, const struct dev_printk_info *dev_info,
...@@ -61,6 +71,13 @@ void defer_console_output(void); ...@@ -61,6 +71,13 @@ 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);
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 #else
#define PRINTK_PREFIX_MAX 0 #define PRINTK_PREFIX_MAX 0
...@@ -76,8 +93,16 @@ u16 printk_parse_prefix(const char *text, int *level, ...@@ -76,8 +93,16 @@ 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 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 */ #endif /* CONFIG_PRINTK */
extern struct printk_buffers printk_shared_pbufs;
/** /**
* struct printk_buffers - Buffers to read/format/output printk messages. * struct printk_buffers - Buffers to read/format/output printk messages.
* @outbuf: After formatting, contains text to output. * @outbuf: After formatting, contains text to output.
...@@ -105,3 +130,9 @@ struct printk_message { ...@@ -105,3 +130,9 @@ struct printk_message {
}; };
bool other_cpu_in_panic(void); 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
This diff is collapsed.
This diff is collapsed.
...@@ -60,7 +60,8 @@ ...@@ -60,7 +60,8 @@
bool no_hash_pointers __ro_after_init; bool no_hash_pointers __ro_after_init;
EXPORT_SYMBOL_GPL(no_hash_pointers); EXPORT_SYMBOL_GPL(no_hash_pointers);
static noinline unsigned long long simple_strntoull(const char *startp, size_t max_chars, char **endp, unsigned int base) noinline
static unsigned long long simple_strntoull(const char *startp, char **endp, unsigned int base, size_t max_chars)
{ {
const char *cp; const char *cp;
unsigned long long result = 0ULL; unsigned long long result = 0ULL;
...@@ -95,7 +96,7 @@ static noinline unsigned long long simple_strntoull(const char *startp, size_t m ...@@ -95,7 +96,7 @@ static noinline unsigned long long simple_strntoull(const char *startp, size_t m
noinline noinline
unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base) unsigned long long simple_strtoull(const char *cp, char **endp, unsigned int base)
{ {
return simple_strntoull(cp, INT_MAX, endp, base); return simple_strntoull(cp, endp, base, INT_MAX);
} }
EXPORT_SYMBOL(simple_strtoull); EXPORT_SYMBOL(simple_strtoull);
...@@ -130,8 +131,8 @@ long simple_strtol(const char *cp, char **endp, unsigned int base) ...@@ -130,8 +131,8 @@ long simple_strtol(const char *cp, char **endp, unsigned int base)
} }
EXPORT_SYMBOL(simple_strtol); EXPORT_SYMBOL(simple_strtol);
static long long simple_strntoll(const char *cp, size_t max_chars, char **endp, noinline
unsigned int base) static long long simple_strntoll(const char *cp, char **endp, unsigned int base, size_t max_chars)
{ {
/* /*
* simple_strntoull() safely handles receiving max_chars==0 in the * simple_strntoull() safely handles receiving max_chars==0 in the
...@@ -140,9 +141,9 @@ static long long simple_strntoll(const char *cp, size_t max_chars, char **endp, ...@@ -140,9 +141,9 @@ static long long simple_strntoll(const char *cp, size_t max_chars, char **endp,
* and the content of *cp is irrelevant. * and the content of *cp is irrelevant.
*/ */
if (*cp == '-' && max_chars > 0) if (*cp == '-' && max_chars > 0)
return -simple_strntoull(cp + 1, max_chars - 1, endp, base); return -simple_strntoull(cp + 1, endp, base, max_chars - 1);
return simple_strntoull(cp, max_chars, endp, base); return simple_strntoull(cp, endp, base, max_chars);
} }
/** /**
...@@ -155,7 +156,7 @@ static long long simple_strntoll(const char *cp, size_t max_chars, char **endp, ...@@ -155,7 +156,7 @@ static long long simple_strntoll(const char *cp, size_t max_chars, char **endp,
*/ */
long long simple_strtoll(const char *cp, char **endp, unsigned int base) long long simple_strtoll(const char *cp, char **endp, unsigned int base)
{ {
return simple_strntoll(cp, INT_MAX, endp, base); return simple_strntoll(cp, endp, base, INT_MAX);
} }
EXPORT_SYMBOL(simple_strtoll); EXPORT_SYMBOL(simple_strtoll);
...@@ -3648,13 +3649,11 @@ int vsscanf(const char *buf, const char *fmt, va_list args) ...@@ -3648,13 +3649,11 @@ int vsscanf(const char *buf, const char *fmt, va_list args)
break; break;
if (is_sign) if (is_sign)
val.s = simple_strntoll(str, val.s = simple_strntoll(str, &next, base,
field_width >= 0 ? field_width : INT_MAX, field_width >= 0 ? field_width : INT_MAX);
&next, base);
else else
val.u = simple_strntoull(str, val.u = simple_strntoull(str, &next, base,
field_width >= 0 ? field_width : INT_MAX, field_width >= 0 ? field_width : INT_MAX);
&next, base);
switch (qualifier) { switch (qualifier) {
case 'H': /* that's 'hh' in format */ case 'H': /* that's 'hh' in format */
......
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