[PATCH POC 1/4] printk: Prepeparation for fake cont buffers

From: Petr Mladek
Date: Fri Jul 15 2016 - 12:59:38 EST


Parts of the line can be premilitary flushed to the console
by console_cont_flush(). When this happen, the cont buffer
could not be used for new lines until the rest of the original
line is flushed. This might cause unnecessary fragmentation
of continuous lines.

This patch is a preparation step to create fake cont buffers
using the information already stored into the main ring buffer.

Most of the existing elements are newly stored via a pointer to
struct printk_log. Also the buffer is changed to a pointer.

The information about the number of characters pushed to the
console is moved to a global variable because it is a global
state.

The patch does not change the existing logic and behavior
any way.

Signed-off-by: Petr Mladek <pmladek@xxxxxxxx>
---
kernel/printk/printk.c | 90 ++++++++++++++++++++++++++++----------------------
1 file changed, 50 insertions(+), 40 deletions(-)

diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index 4ae2797ec10a..71e96aae98e7 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -1511,55 +1511,63 @@ static inline void printk_delay(void)
}
}

+
/*
* Continuation lines are buffered, and not committed to the record buffer
* until the line is complete, or a race forces it. The line fragments
* though, are printed immediately to the consoles to ensure everything has
* reached the console in case of a kernel crash.
*/
-static struct cont {
- char buf[LOG_LINE_MAX];
- size_t len; /* length == 0 means unused buffer */
- size_t cons; /* bytes written to console */
+struct cont {
struct task_struct *owner; /* task of first print*/
- u64 ts_nsec; /* time of first print */
- u8 level; /* log level of first message */
- u8 facility; /* log facility of first message */
- enum log_flags flags; /* prefix, newline flags */
+ struct printk_log *msg; /* more info about the stored text */
+ char *buf; /* buffer to the stored text */
bool flushed:1; /* buffer sealed and committed */
-} cont;
+};
+
+/* bytes written to console */
+static size_t cont_console_len;
+
+static struct printk_log cont_msg;
+static char cont_buf[LOG_LINE_MAX];
+
+static struct cont cont = {
+ .msg = &cont_msg,
+ .buf = cont_buf,
+};

static void cont_flush(enum log_flags flags)
{
if (cont.flushed)
return;
- if (cont.len == 0)
+ if (cont.msg->text_len == 0)
return;

- if (cont.cons) {
+ if (cont_console_len) {
/*
* If a fragment of this line was directly flushed to the
* console; wait for the console to pick up the rest of the
* line. LOG_NOCONS suppresses a duplicated output.
*/
- log_store(cont.facility, cont.level, flags | LOG_NOCONS,
- cont.ts_nsec, NULL, 0, cont.buf, cont.len);
- cont.flags = flags;
+ log_store(cont.msg->facility, cont.msg->level,
+ flags | LOG_NOCONS, cont.msg->ts_nsec,
+ NULL, 0, cont.buf, cont.msg->text_len);
+ cont.msg->flags = flags;
cont.flushed = true;
} else {
/*
* If no fragment of this line ever reached the console,
* just submit it to the store and free the buffer.
*/
- log_store(cont.facility, cont.level, flags, 0,
- NULL, 0, cont.buf, cont.len);
- cont.len = 0;
+ log_store(cont.msg->facility, cont.msg->level, flags, 0,
+ NULL, 0, cont.buf, cont.msg->text_len);
+ cont.msg->text_len = 0;
}
}

static bool cont_add(int facility, int level, const char *text, size_t len)
{
- if (cont.len && cont.flushed)
+ if (cont.msg->text_len && cont.flushed)
return false;

/*
@@ -1567,25 +1575,26 @@ static bool cont_add(int facility, int level, const char *text, size_t len)
* continuation. See nr_ext_console_drivers definition. Also, if
* the line gets too long, split it up in separate records.
*/
- if (nr_ext_console_drivers || cont.len + len > sizeof(cont.buf)) {
+ if (nr_ext_console_drivers ||
+ cont.msg->text_len + len > sizeof(cont_buf)) {
cont_flush(LOG_CONT);
return false;
}

- if (!cont.len) {
- cont.facility = facility;
- cont.level = level;
+ if (!cont.msg->text_len) {
cont.owner = current;
- cont.ts_nsec = local_clock();
- cont.flags = 0;
- cont.cons = 0;
+ cont.msg->facility = facility;
+ cont.msg->level = level;
+ cont.msg->ts_nsec = local_clock();
+ cont.msg->flags = 0;
cont.flushed = false;
+ cont_console_len = 0;
}

- memcpy(cont.buf + cont.len, text, len);
- cont.len += len;
+ memcpy(cont.buf + cont.msg->text_len, text, len);
+ cont.msg->text_len += len;

- if (cont.len > (sizeof(cont.buf) * 80) / 100)
+ if (cont.msg->text_len > (sizeof(cont_buf) * 80) / 100)
cont_flush(LOG_CONT);

return true;
@@ -1596,25 +1605,25 @@ static size_t cont_print_text(char *text, size_t size)
size_t textlen = 0;
size_t len;

- if (cont.cons == 0 && (console_prev & LOG_NEWLINE)) {
- textlen += print_time(cont.ts_nsec, text);
+ if (cont_console_len == 0 && (console_prev & LOG_NEWLINE)) {
+ textlen += print_time(cont.msg->ts_nsec, text);
size -= textlen;
}

- len = cont.len - cont.cons;
+ len = cont.msg->text_len - cont_console_len;
if (len > 0) {
if (len+1 > size)
len = size-1;
- memcpy(text + textlen, cont.buf + cont.cons, len);
+ memcpy(text + textlen, cont.buf + cont_console_len, len);
textlen += len;
- cont.cons = cont.len;
+ cont_console_len = cont.msg->text_len;
}

if (cont.flushed) {
- if (cont.flags & LOG_NEWLINE)
+ if (cont.msg->flags & LOG_NEWLINE)
text[textlen++] = '\n';
/* got everything, release buffer */
- cont.len = 0;
+ cont.msg->text_len = 0;
}
return textlen;
}
@@ -1738,7 +1747,8 @@ asmlinkage int vprintk_emit(int facility, int level,
* Flush the conflicting buffer. An earlier newline was missing,
* or another task also prints continuation lines.
*/
- if (cont.len && (lflags & LOG_PREFIX || cont.owner != current))
+ if (cont.msg->text_len &&
+ (lflags & LOG_PREFIX || cont.owner != current))
cont_flush(LOG_NEWLINE);

/* buffer line if possible, otherwise store it right away */
@@ -1759,7 +1769,7 @@ asmlinkage int vprintk_emit(int facility, int level,
* If the preceding printk was from a different task and missed
* a newline, flush and append the newline.
*/
- if (cont.len) {
+ if (cont.msg->text_len) {
if (cont.owner == current && !(lflags & LOG_PREFIX))
stored = cont_add(facility, level, text,
text_len);
@@ -2201,7 +2211,7 @@ static void console_cont_flush(char *text, size_t size)

raw_spin_lock_irqsave(&logbuf_lock, flags);

- if (!cont.len)
+ if (!cont.msg->text_len)
goto out;

/*
@@ -2209,13 +2219,13 @@ static void console_cont_flush(char *text, size_t size)
* busy. The earlier ones need to be printed before this one, we
* did not flush any fragment so far, so just let it queue up.
*/
- if (console_seq < log_next_seq && !cont.cons)
+ if (console_seq < log_next_seq && !cont_console_len)
goto out;

len = cont_print_text(text, size);
raw_spin_unlock(&logbuf_lock);
stop_critical_timings();
- call_console_drivers(cont.level, NULL, 0, text, len);
+ call_console_drivers(cont.msg->level, NULL, 0, text, len);
start_critical_timings();
local_irq_restore(flags);
return;
--
1.8.5.6