Skip to content

Commit

Permalink
Merge tag 'printk-for-4.19' of git://git.kernel.org/pub/scm/linux/ker…
Browse files Browse the repository at this point in the history
…nel/git/pmladek/printk

Pull printk updates from Petr Mladek:

 - Different vendors have a different expectation about a console
   quietness. Make it configurable to reduce bike-shedding about the
   upstream default

 - Decide about the message visibility when the message is stored. It
   avoids races caused by a delayed console handling

 - Always store printk() messages into the per-CPU buffers again in NMI.
   The only exception is when flushing trace log in panic(). There the
   risk of loosing messages is worth an eventual reordering

 - Handle invalid %pO printf modifiers correctly

 - Better handle %p printf modifier tests before crng is initialized

 - Some clean up

* tag 'printk-for-4.19' of git://git.kernel.org/pub/scm/linux/kernel/git/pmladek/printk:
  lib/vsprintf: Do not handle %pO[^F] as %px
  printk: Fix warning about unused suppress_message_printing
  printk/nmi: Prevent deadlock when accessing the main log buffer in NMI
  printk: Create helper function to queue deferred console handling
  printk: Split the code for storing a message into the log buffer
  printk: Clean up syslog_print_all()
  printk: Remove unnecessary kmalloc() from syslog during clear
  printk: Make CONSOLE_LOGLEVEL_QUIET configurable
  printk: make sure to print log on console.
  lib/test_printf.c: accept "ptrval" as valid result for plain 'p' tests
  • Loading branch information
torvalds committed Aug 15, 2018
2 parents 8c32685 + 9f68cb5 commit b125d90
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 112 deletions.
10 changes: 7 additions & 3 deletions include/linux/printk.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ static inline const char *printk_skip_headers(const char *buffer)
/* We show everything that is MORE important than this.. */
#define CONSOLE_LOGLEVEL_SILENT 0 /* Mum's the word */
#define CONSOLE_LOGLEVEL_MIN 1 /* Minimum loglevel we let people use */
#define CONSOLE_LOGLEVEL_QUIET 4 /* Shhh ..., when booted with "quiet" */
#define CONSOLE_LOGLEVEL_DEBUG 10 /* issue debug messages */
#define CONSOLE_LOGLEVEL_MOTORMOUTH 15 /* You can't shut this one up */

/*
* Default used to be hard-coded at 7, we're now allowing it to be set from
* kernel config.
* Default used to be hard-coded at 7, quiet used to be hardcoded at 4,
* we're now allowing both to be set from kernel config.
*/
#define CONSOLE_LOGLEVEL_DEFAULT CONFIG_CONSOLE_LOGLEVEL_DEFAULT
#define CONSOLE_LOGLEVEL_QUIET CONFIG_CONSOLE_LOGLEVEL_QUIET

extern int console_printk[];

Expand Down Expand Up @@ -148,9 +148,13 @@ void early_printk(const char *s, ...) { }
#ifdef CONFIG_PRINTK_NMI
extern void printk_nmi_enter(void);
extern void printk_nmi_exit(void);
extern void printk_nmi_direct_enter(void);
extern void printk_nmi_direct_exit(void);
#else
static inline void printk_nmi_enter(void) { }
static inline void printk_nmi_exit(void) { }
static inline void printk_nmi_direct_enter(void) { }
static inline void printk_nmi_direct_exit(void) { }
#endif /* PRINTK_NMI */

#ifdef CONFIG_PRINTK
Expand Down
9 changes: 8 additions & 1 deletion kernel/printk/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@
#ifdef CONFIG_PRINTK

#define PRINTK_SAFE_CONTEXT_MASK 0x3fffffff
#define PRINTK_NMI_DEFERRED_CONTEXT_MASK 0x40000000
#define PRINTK_NMI_DIRECT_CONTEXT_MASK 0x40000000
#define PRINTK_NMI_CONTEXT_MASK 0x80000000

extern raw_spinlock_t logbuf_lock;

__printf(5, 0)
int vprintk_store(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args);

__printf(1, 0) int vprintk_default(const char *fmt, va_list args);
__printf(1, 0) int vprintk_deferred(const char *fmt, va_list args);
__printf(1, 0) int vprintk_func(const char *fmt, va_list args);
Expand Down Expand Up @@ -54,6 +59,8 @@ void __printk_safe_exit(void);
local_irq_enable(); \
} while (0)

void defer_console_output(void);

#else

__printf(1, 0) int vprintk_func(const char *fmt, va_list args) { return 0; }
Expand Down
181 changes: 100 additions & 81 deletions kernel/printk/printk.c
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ static int console_msg_format = MSG_FORMAT_DEFAULT;
*/

enum log_flags {
LOG_NOCONS = 1, /* already flushed, do not print to console */
LOG_NOCONS = 1, /* suppress print, do not print to console */
LOG_NEWLINE = 2, /* text ended with a newline */
LOG_PREFIX = 4, /* text started with a prefix */
LOG_CONT = 8, /* text is a fragment of a continuation line */
Expand Down Expand Up @@ -1352,71 +1352,68 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
{
char *text;
int len = 0;
u64 next_seq;
u64 seq;
u32 idx;

text = kmalloc(LOG_LINE_MAX + PREFIX_MAX, GFP_KERNEL);
if (!text)
return -ENOMEM;

logbuf_lock_irq();
if (buf) {
u64 next_seq;
u64 seq;
u32 idx;
/*
* Find first record that fits, including all following records,
* into the user-provided buffer for this dump.
*/
seq = clear_seq;
idx = clear_idx;
while (seq < log_next_seq) {
struct printk_log *msg = log_from_idx(idx);

/*
* Find first record that fits, including all following records,
* into the user-provided buffer for this dump.
*/
seq = clear_seq;
idx = clear_idx;
while (seq < log_next_seq) {
struct printk_log *msg = log_from_idx(idx);

len += msg_print_text(msg, true, NULL, 0);
idx = log_next(idx);
seq++;
}
len += msg_print_text(msg, true, NULL, 0);
idx = log_next(idx);
seq++;
}

/* move first record forward until length fits into the buffer */
seq = clear_seq;
idx = clear_idx;
while (len > size && seq < log_next_seq) {
struct printk_log *msg = log_from_idx(idx);
/* move first record forward until length fits into the buffer */
seq = clear_seq;
idx = clear_idx;
while (len > size && seq < log_next_seq) {
struct printk_log *msg = log_from_idx(idx);

len -= msg_print_text(msg, true, NULL, 0);
idx = log_next(idx);
seq++;
}
len -= msg_print_text(msg, true, NULL, 0);
idx = log_next(idx);
seq++;
}

/* last message fitting into this dump */
next_seq = log_next_seq;
/* last message fitting into this dump */
next_seq = log_next_seq;

len = 0;
while (len >= 0 && seq < next_seq) {
struct printk_log *msg = log_from_idx(idx);
int textlen;
len = 0;
while (len >= 0 && seq < next_seq) {
struct printk_log *msg = log_from_idx(idx);
int textlen;

textlen = msg_print_text(msg, true, text,
LOG_LINE_MAX + PREFIX_MAX);
if (textlen < 0) {
len = textlen;
break;
}
idx = log_next(idx);
seq++;
textlen = msg_print_text(msg, true, text,
LOG_LINE_MAX + PREFIX_MAX);
if (textlen < 0) {
len = textlen;
break;
}
idx = log_next(idx);
seq++;

logbuf_unlock_irq();
if (copy_to_user(buf + len, text, textlen))
len = -EFAULT;
else
len += textlen;
logbuf_lock_irq();

if (seq < log_first_seq) {
/* messages are gone, move to next one */
seq = log_first_seq;
idx = log_first_idx;
}
logbuf_unlock_irq();
if (copy_to_user(buf + len, text, textlen))
len = -EFAULT;
else
len += textlen;
logbuf_lock_irq();

if (seq < log_first_seq) {
/* messages are gone, move to next one */
seq = log_first_seq;
idx = log_first_idx;
}
}

Expand All @@ -1430,6 +1427,14 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
return len;
}

static void syslog_clear(void)
{
logbuf_lock_irq();
clear_seq = log_next_seq;
clear_idx = log_next_idx;
logbuf_unlock_irq();
}

int do_syslog(int type, char __user *buf, int len, int source)
{
bool clear = false;
Expand Down Expand Up @@ -1474,7 +1479,7 @@ int do_syslog(int type, char __user *buf, int len, int source)
break;
/* Clear ring buffer */
case SYSLOG_ACTION_CLEAR:
syslog_print_all(NULL, 0, true);
syslog_clear();
break;
/* Disable logging to console */
case SYSLOG_ACTION_CONSOLE_OFF:
Expand Down Expand Up @@ -1824,28 +1829,16 @@ static size_t log_output(int facility, int level, enum log_flags lflags, const c
return log_store(facility, level, lflags, 0, dict, dictlen, text, text_len);
}

asmlinkage int vprintk_emit(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args)
/* Must be called under logbuf_lock. */
int vprintk_store(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args)
{
static char textbuf[LOG_LINE_MAX];
char *text = textbuf;
size_t text_len;
enum log_flags lflags = 0;
unsigned long flags;
int printed_len;
bool in_sched = false;

if (level == LOGLEVEL_SCHED) {
level = LOGLEVEL_DEFAULT;
in_sched = true;
}

boot_delay_msec(level);
printk_delay();

/* This stops the holder of console_sem just where we want him */
logbuf_lock_irqsave(flags);
/*
* The printf needs to come first; we need the syslog
* prefix which might be passed-in as a parameter.
Expand Down Expand Up @@ -1886,8 +1879,32 @@ asmlinkage int vprintk_emit(int facility, int level,
if (dict)
lflags |= LOG_PREFIX|LOG_NEWLINE;

printed_len = log_output(facility, level, lflags, dict, dictlen, text, text_len);
if (suppress_message_printing(level))
lflags |= LOG_NOCONS;

return log_output(facility, level, lflags,
dict, dictlen, text, text_len);
}

asmlinkage int vprintk_emit(int facility, int level,
const char *dict, size_t dictlen,
const char *fmt, va_list args)
{
int printed_len;
bool in_sched = false;
unsigned long flags;

if (level == LOGLEVEL_SCHED) {
level = LOGLEVEL_DEFAULT;
in_sched = true;
}

boot_delay_msec(level);
printk_delay();

/* This stops the holder of console_sem just where we want him */
logbuf_lock_irqsave(flags);
printed_len = vprintk_store(facility, level, dict, dictlen, fmt, args);
logbuf_unlock_irqrestore(flags);

/* If called from the scheduler, we can not call up(). */
Expand Down Expand Up @@ -2013,7 +2030,6 @@ static void call_console_drivers(const char *ext_text, size_t ext_len,
const char *text, size_t len) {}
static size_t msg_print_text(const struct printk_log *msg,
bool syslog, char *buf, size_t size) { return 0; }
static bool suppress_message_printing(int level) { return false; }

#endif /* CONFIG_PRINTK */

Expand Down Expand Up @@ -2349,11 +2365,10 @@ void console_unlock(void)
break;

msg = log_from_idx(console_idx);
if (suppress_message_printing(msg->level)) {
if (msg->flags & LOG_NOCONS) {
/*
* Skip record we have buffered and already printed
* directly to the console when we received it, and
* record that has level above the console loglevel.
* Skip record if !ignore_loglevel, and
* record has level above the console loglevel.
*/
console_idx = log_next(console_idx);
console_seq++;
Expand Down Expand Up @@ -2878,16 +2893,20 @@ void wake_up_klogd(void)
preempt_enable();
}

int vprintk_deferred(const char *fmt, va_list args)
void defer_console_output(void)
{
int r;

r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);

preempt_disable();
__this_cpu_or(printk_pending, PRINTK_PENDING_OUTPUT);
irq_work_queue(this_cpu_ptr(&wake_up_klogd_work));
preempt_enable();
}

int vprintk_deferred(const char *fmt, va_list args)
{
int r;

r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, 0, fmt, args);
defer_console_output();

return r;
}
Expand Down
Loading

0 comments on commit b125d90

Please sign in to comment.