[RFC v4 2/9] printk: add one function for storing log in proper format

2015-10-19 Thread Paul Osmialowski
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

2015-10-19 Thread Paul Osmialowski
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