Skip to content

Commit 2830eec

Browse files
jognesspmladek
authored andcommitted
printk: introduce printk_get_next_message() and printk_message
Code for performing the console output is intermixed with code that is formatting the output for that console. Introduce a new helper function printk_get_next_message() to handle the reading and formatting of the printk text. The helper does not require any locking so that in the future it can be used for other printing contexts as well. This also introduces a new struct printk_message to wrap the struct printk_buffers, adding metadata about its contents. This allows users of printk_get_next_message() to receive all relevant information about the message that was read and formatted. Why is struct printk_message a wrapper struct? It is intentional that a wrapper struct is introduced instead of adding the metadata directly to struct printk_buffers. The upcoming atomic consoles support multiple printing contexts per CPU. This means that while a CPU is formatting a message, it can be interrupted and the interrupting context may also format a (possibly different) message. Since the printk buffers are rather large, there will only be one struct printk_buffers per CPU and it must be shared by the possible contexts of that CPU. If the metadata was part of struct printk_buffers, interrupting contexts would clobber the metadata being prepared by the interrupted context. This could be handled by robustifying the message formatting functions to cope with metadata unexpectedly changing. However, this would require significant amounts of extra data copying, also adding significant complexity to the code. Instead, the metadata can live on the stack of the formatting context and the message formatting functions do not need to be concerned about the metadata changing underneath them. Note that the message formatting functions can handle unexpected text buffer changes. So it is perfectly OK if a shared text buffer is clobbered by an interrupting context. The atomic console implementation will recognize the interruption and avoid printing the (probably garbage) text buffer. Signed-off-by: John Ogness <[email protected]> Reviewed-by: Petr Mladek <[email protected]> Signed-off-by: Petr Mladek <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent daaab5b commit 2830eec

File tree

2 files changed

+96
-35
lines changed

2 files changed

+96
-35
lines changed

kernel/printk/internal.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,19 @@ struct printk_buffers {
9292
char outbuf[CONSOLE_EXT_LOG_MAX];
9393
char scratchbuf[LOG_LINE_MAX];
9494
};
95+
96+
/**
97+
* struct printk_message - Container for a prepared printk message.
98+
* @pbufs: printk buffers used to prepare the message.
99+
* @outbuf_len: The length of prepared text in @pbufs->outbuf to output. This
100+
* does not count the terminator. A value of 0 means there is
101+
* nothing to output and this record should be skipped.
102+
* @seq: The sequence number of the record used for @pbufs->outbuf.
103+
* @dropped: The number of dropped records from reading @seq.
104+
*/
105+
struct printk_message {
106+
struct printk_buffers *pbufs;
107+
unsigned int outbuf_len;
108+
u64 seq;
109+
unsigned long dropped;
110+
};

kernel/printk/printk.c

Lines changed: 80 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2725,35 +2725,35 @@ static void __console_unlock(void)
27252725
}
27262726

27272727
/*
2728-
* Print one record for the given console. The record printed is whatever
2729-
* record is the next available record for the given console.
2728+
* Read and format the specified record (or a later record if the specified
2729+
* record is not available).
27302730
*
2731-
* @handover will be set to true if a printk waiter has taken over the
2732-
* console_lock, in which case the caller is no longer holding both the
2733-
* console_lock and the SRCU read lock. Otherwise it is set to false.
2731+
* @pmsg will contain the formatted result. @pmsg->pbufs must point to a
2732+
* struct printk_buffers.
27342733
*
2735-
* @cookie is the cookie from the SRCU read lock.
2734+
* @seq is the record to read and format. If it is not available, the next
2735+
* valid record is read.
27362736
*
2737-
* Returns false if the given console has no next record to print, otherwise
2738-
* true.
2737+
* @is_extended specifies if the message should be formatted for extended
2738+
* console output.
27392739
*
2740-
* Requires the console_lock and the SRCU read lock.
2740+
* Returns false if no record is available. Otherwise true and all fields
2741+
* of @pmsg are valid. (See the documentation of struct printk_message
2742+
* for information about the @pmsg fields.)
27412743
*/
2742-
static bool console_emit_next_record(struct console *con, bool *handover, int cookie)
2744+
static bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
2745+
bool is_extended)
27432746
{
2744-
static char dropped_text[DROPPED_TEXT_MAX];
2745-
static struct printk_buffers pbufs;
27462747
static int panic_console_dropped;
27472748

2748-
bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED;
2749-
const size_t scratchbuf_sz = sizeof(pbufs.scratchbuf);
2750-
const size_t outbuf_sz = sizeof(pbufs.outbuf);
2751-
char *scratchbuf = &pbufs.scratchbuf[0];
2752-
char *outbuf = &pbufs.outbuf[0];
2749+
struct printk_buffers *pbufs = pmsg->pbufs;
2750+
const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf);
2751+
const size_t outbuf_sz = sizeof(pbufs->outbuf);
2752+
char *scratchbuf = &pbufs->scratchbuf[0];
2753+
char *outbuf = &pbufs->outbuf[0];
27532754
struct printk_info info;
27542755
struct printk_record r;
2755-
unsigned long flags;
2756-
size_t len;
2756+
size_t len = 0;
27572757

27582758
/*
27592759
* Formatting extended messages requires a separate buffer, so use the
@@ -2767,25 +2767,26 @@ static bool console_emit_next_record(struct console *con, bool *handover, int co
27672767
else
27682768
prb_rec_init_rd(&r, &info, outbuf, outbuf_sz);
27692769

2770-
*handover = false;
2771-
2772-
if (!prb_read_valid(prb, con->seq, &r))
2770+
if (!prb_read_valid(prb, seq, &r))
27732771
return false;
27742772

2775-
if (con->seq != r.info->seq) {
2776-
con->dropped += r.info->seq - con->seq;
2777-
con->seq = r.info->seq;
2778-
if (panic_in_progress() && panic_console_dropped++ > 10) {
2779-
suppress_panic_printk = 1;
2780-
pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n");
2781-
}
2773+
pmsg->seq = r.info->seq;
2774+
pmsg->dropped = r.info->seq - seq;
2775+
2776+
/*
2777+
* Check for dropped messages in panic here so that printk
2778+
* suppression can occur as early as possible if necessary.
2779+
*/
2780+
if (pmsg->dropped &&
2781+
panic_in_progress() &&
2782+
panic_console_dropped++ > 10) {
2783+
suppress_panic_printk = 1;
2784+
pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n");
27822785
}
27832786

27842787
/* Skip record that has level above the console loglevel. */
2785-
if (suppress_message_printing(r.info->level)) {
2786-
con->seq++;
2787-
goto skip;
2788-
}
2788+
if (suppress_message_printing(r.info->level))
2789+
goto out;
27892790

27902791
if (is_extended) {
27912792
len = info_print_ext_header(outbuf, outbuf_sz, r.info);
@@ -2794,6 +2795,50 @@ static bool console_emit_next_record(struct console *con, bool *handover, int co
27942795
} else {
27952796
len = record_print_text(&r, console_msg_format & MSG_FORMAT_SYSLOG, printk_time);
27962797
}
2798+
out:
2799+
pmsg->outbuf_len = len;
2800+
return true;
2801+
}
2802+
2803+
/*
2804+
* Print one record for the given console. The record printed is whatever
2805+
* record is the next available record for the given console.
2806+
*
2807+
* @handover will be set to true if a printk waiter has taken over the
2808+
* console_lock, in which case the caller is no longer holding both the
2809+
* console_lock and the SRCU read lock. Otherwise it is set to false.
2810+
*
2811+
* @cookie is the cookie from the SRCU read lock.
2812+
*
2813+
* Returns false if the given console has no next record to print, otherwise
2814+
* true.
2815+
*
2816+
* Requires the console_lock and the SRCU read lock.
2817+
*/
2818+
static bool console_emit_next_record(struct console *con, bool *handover, int cookie)
2819+
{
2820+
static char dropped_text[DROPPED_TEXT_MAX];
2821+
static struct printk_buffers pbufs;
2822+
2823+
bool is_extended = console_srcu_read_flags(con) & CON_EXTENDED;
2824+
char *outbuf = &pbufs.outbuf[0];
2825+
struct printk_message pmsg = {
2826+
.pbufs = &pbufs,
2827+
};
2828+
unsigned long flags;
2829+
2830+
*handover = false;
2831+
2832+
if (!printk_get_next_message(&pmsg, con->seq, is_extended))
2833+
return false;
2834+
2835+
con->dropped += pmsg.dropped;
2836+
2837+
/* Skip messages of formatted length 0. */
2838+
if (pmsg.outbuf_len == 0) {
2839+
con->seq = pmsg.seq + 1;
2840+
goto skip;
2841+
}
27972842

27982843
/*
27992844
* While actively printing out messages, if another printk()
@@ -2809,11 +2854,11 @@ static bool console_emit_next_record(struct console *con, bool *handover, int co
28092854
console_lock_spinning_enable();
28102855

28112856
stop_critical_timings(); /* don't trace print latency */
2812-
call_console_driver(con, outbuf, len,
2857+
call_console_driver(con, outbuf, pmsg.outbuf_len,
28132858
is_extended ? NULL : dropped_text);
28142859
start_critical_timings();
28152860

2816-
con->seq++;
2861+
con->seq = pmsg.seq + 1;
28172862

28182863
*handover = console_lock_spinning_disable_and_check(cookie);
28192864
printk_safe_exit_irqrestore(flags);

0 commit comments

Comments
 (0)