[RFC v4 2/9] printk: add one function for storing log in proper format
From: Marcin Niesluchowski Preparation commit for future changes purpose. Separate code responsible for storing log message in proper format from operations on consoles by putting it in another function. Signed-off-by: Marcin Niesluchowski Signed-off-by: Paul Osmialowski --- kernel/printk/printk.c | 222 ++--- 1 file changed, 119 insertions(+), 103 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index c1b7a79..518cbdf 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -181,6 +181,27 @@ static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN); static char *log_buf = __log_buf; static u32 log_buf_len = __LOG_BUF_LEN; +/* + * 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 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 */ + bool flushed:1; /* buffer sealed and committed */ +} cont; + +static void cont_flush(enum log_flags flags); +static bool cont_add(int facility, int level, const char *text, size_t len); + /* Return log buffer address */ char *log_buf_addr_get(void) { @@ -332,6 +353,102 @@ static int log_store(int facility, int level, return msg->text_len; } +static int log_format_and_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 = 0; + enum log_flags lflags = 0; + int printed_len = 0; + + /* +* The printf needs to come first; we need the syslog +* prefix which might be passed-in as a parameter. +*/ + text_len = vscnprintf(text, sizeof(textbuf), fmt, args); + + /* mark and strip a trailing newline */ + if (text_len && text[text_len-1] == '\n') { + text_len--; + lflags |= LOG_NEWLINE; + } + + /* strip kernel syslog prefix and extract log level or control flags */ + if (facility == 0) { + int kern_level = printk_get_level(text); + + if (kern_level) { + const char *end_of_header = printk_skip_level(text); + + switch (kern_level) { + case '0' ... '7': + if (level == LOGLEVEL_DEFAULT) + level = kern_level - '0'; + /* fallthrough */ + case 'd': /* KERN_DEFAULT */ + lflags |= LOG_PREFIX; + } + /* +* No need to check length here because vscnprintf +* put '\0' at the end of the string. Only valid and +* newly printed level is detected. +*/ + text_len -= end_of_header - text; + text = (char *)end_of_header; + } + } + + if (level == LOGLEVEL_DEFAULT) + level = default_message_loglevel; + + if (dict) + lflags |= LOG_PREFIX|LOG_NEWLINE; + + if (!(lflags & LOG_NEWLINE)) { + /* +* 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)) + cont_flush(LOG_NEWLINE); + + /* buffer line if possible, otherwise store it right away */ + if (cont_add(facility, level, text, text_len)) + printed_len += text_len; + else + printed_len += log_store(facility, level, +lflags | LOG_CONT, 0, +dict, dictlen, text, text_len); + } else { + bool stored = false; + + /* +* If an earlier newline was missing and it was the same task, +* either merge it with the current buffer and flush, or if +* there was a race
[RFC v4 2/9] printk: add one function for storing log in proper format
From: Marcin NiesluchowskiPreparation commit for future changes purpose. Separate code responsible for storing log message in proper format from operations on consoles by putting it in another function. Signed-off-by: Marcin Niesluchowski Signed-off-by: Paul Osmialowski --- kernel/printk/printk.c | 222 ++--- 1 file changed, 119 insertions(+), 103 deletions(-) diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index c1b7a79..518cbdf 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -181,6 +181,27 @@ static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN); static char *log_buf = __log_buf; static u32 log_buf_len = __LOG_BUF_LEN; +/* + * 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 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 */ + bool flushed:1; /* buffer sealed and committed */ +} cont; + +static void cont_flush(enum log_flags flags); +static bool cont_add(int facility, int level, const char *text, size_t len); + /* Return log buffer address */ char *log_buf_addr_get(void) { @@ -332,6 +353,102 @@ static int log_store(int facility, int level, return msg->text_len; } +static int log_format_and_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 = 0; + enum log_flags lflags = 0; + int printed_len = 0; + + /* +* The printf needs to come first; we need the syslog +* prefix which might be passed-in as a parameter. +*/ + text_len = vscnprintf(text, sizeof(textbuf), fmt, args); + + /* mark and strip a trailing newline */ + if (text_len && text[text_len-1] == '\n') { + text_len--; + lflags |= LOG_NEWLINE; + } + + /* strip kernel syslog prefix and extract log level or control flags */ + if (facility == 0) { + int kern_level = printk_get_level(text); + + if (kern_level) { + const char *end_of_header = printk_skip_level(text); + + switch (kern_level) { + case '0' ... '7': + if (level == LOGLEVEL_DEFAULT) + level = kern_level - '0'; + /* fallthrough */ + case 'd': /* KERN_DEFAULT */ + lflags |= LOG_PREFIX; + } + /* +* No need to check length here because vscnprintf +* put '\0' at the end of the string. Only valid and +* newly printed level is detected. +*/ + text_len -= end_of_header - text; + text = (char *)end_of_header; + } + } + + if (level == LOGLEVEL_DEFAULT) + level = default_message_loglevel; + + if (dict) + lflags |= LOG_PREFIX|LOG_NEWLINE; + + if (!(lflags & LOG_NEWLINE)) { + /* +* 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)) + cont_flush(LOG_NEWLINE); + + /* buffer line if possible, otherwise store it right away */ + if (cont_add(facility, level, text, text_len)) + printed_len += text_len; + else + printed_len += log_store(facility, level, +lflags | LOG_CONT, 0, +dict, dictlen, text, text_len); + } else { + bool stored = false; + + /* +* If an earlier newline was missing and it was the same task, +* either merge it