packaging: install license for rpm package instead of license package
[profile/mobile/platform/kernel/linux-3.10-sc7730.git] / kernel / printk_kmsg.c
index c7023f7..00515b8 100644 (file)
 #include <linux/poll.h>
 #include <linux/irq_work.h>
 #include <linux/utsname.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/kref.h>
+#include <linux/kdev_t.h>
 
 #include <asm/uaccess.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/printk.h>
 
+#ifdef CONFIG_PRINTK
+#include <uapi/linux/kmsg_ioctl.h>
+#endif
+
 #ifdef CONFIG_DEBUG_LL
 extern void printascii(char *);
 #endif
@@ -226,43 +234,54 @@ struct log {
 #endif
 };
 
+struct log_buffer {
+#ifdef CONFIG_PRINTK
+       struct list_head list;  /* kmsg as head of the list */
+       char *buf;              /* cyclic log buffer */
+       u32 len;                /* buffer length */
+       wait_queue_head_t wait; /* wait queue for kmsg buffer */
+       struct kref refcount;   /* refcount for kmsg_sys buffers */
+#endif
 /*
- * The logbuf_lock protects kmsg buffer, indices, counters. It is also
- * used in interesting ways to provide interlocking in console_unlock();
+ * The lock protects kmsg buffer, indices, counters. This can be taken within
+ * the scheduler's rq lock. It must be released before calling console_unlock()
+ * or anything else that might wake up a process.
  */
-static DEFINE_RAW_SPINLOCK(logbuf_lock);
+       raw_spinlock_t lock;
+       u64 first_seq;          /* sequence number of the first record stored */
+       u32 first_idx;          /* index of the first record stored */
+/* sequence number of the next record to store */
+       u64 next_seq;
+#ifdef CONFIG_PRINTK
+       u32 next_idx;           /* index of the next record to store */
+/* sequence number of the next record to read after last 'clear' command */
+       u64 clear_seq;
+/* index of the next record to read after last 'clear' command */
+       u32 clear_idx;
+       int mode;               /* mode of device */
+       int minor;              /* minor representing buffer device */
+#endif
+};
 
 #ifdef CONFIG_PRINTK
-DECLARE_WAIT_QUEUE_HEAD(log_wait);
 /* the next printk record to read by syslog(READ) or /proc/kmsg */
 static u64 syslog_seq;
 static u32 syslog_idx;
 static enum log_flags syslog_prev;
 static size_t syslog_partial;
 
-/* index and sequence number of the first record stored in the buffer */
-static u64 log_first_seq;
-static u32 log_first_idx;
-
-/* index and sequence number of the next record to store in the buffer */
-static u64 log_next_seq;
-static u32 log_next_idx;
-
 /* the next printk record to write to the console */
 static u64 console_seq;
 static u32 console_idx;
 static enum log_flags console_prev;
 
-/* the next printk record to read after the last 'clear' command */
-static u64 clear_seq;
-static u32 clear_idx;
-
 #ifdef CONFIG_PRINTK_PROCESS
 #define PREFIX_MAX             48
 #else
 #define PREFIX_MAX             32
 #endif
 #define LOG_LINE_MAX           1024 - PREFIX_MAX
+#define KMSG_NUM_MAX           255
 
 /* record buffer */
 #if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS)
@@ -270,12 +289,41 @@ static u32 clear_idx;
 #else
 #define LOG_ALIGN __alignof__(struct log)
 #endif
-#define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
-static char __log_buf[__LOG_BUF_LEN] __aligned(LOG_ALIGN);
-static char *log_buf = __log_buf;
-static u32 log_buf_len = __LOG_BUF_LEN;
+#define __LOG_BUF_K_LEN (1 << CONFIG_LOG_BUF_SHIFT)
+static char __log_buf_k[__LOG_BUF_K_LEN] __aligned(LOG_ALIGN);
+
+static struct log_buffer log_buf = {
+       .list           = LIST_HEAD_INIT(log_buf.list),
+       .buf            = __log_buf_k,
+       .len            = __LOG_BUF_K_LEN,
+       .lock           = __RAW_SPIN_LOCK_UNLOCKED(log_buf.lock),
+       .wait           = __WAIT_QUEUE_HEAD_INITIALIZER(log_buf.wait),
+       .refcount       = { .refcount = { .counter = 0 } },
+       .first_seq      = 0,
+       .first_idx      = 0,
+       .next_seq       = 0,
+       .next_idx       = 0,
+       .clear_seq      = 0,
+       .clear_idx      = 0,
+       .mode           = 0,
+       .minor          = 0,
+};
+
+wait_queue_head_t *log_wait = &log_buf.wait;
+
+/* Return log buffer address */
+char *log_buf_addr_get(void)
+{
+       return log_buf.buf;
+}
+
+/* Return log buffer size */
+u32 log_buf_len_get(void)
+{
+       return log_buf.len;
+}
 
-/* cpu currently holding logbuf_lock */
+/* cpu currently holding log_buf.lock */
 static volatile unsigned int logbuf_cpu = UINT_MAX;
 
 /* human readable text of the record */
@@ -291,23 +339,23 @@ static char *log_dict(const struct log *msg)
 }
 
 /* get record by index; idx must point to valid msg */
-static struct log *log_from_idx(u32 idx)
+static struct log *log_from_idx(struct log_buffer *log_b, u32 idx)
 {
-       struct log *msg = (struct log *)(log_buf + idx);
+       struct log *msg = (struct log *)(log_b->buf + idx);
 
        /*
         * A length == 0 record is the end of buffer marker. Wrap around and
         * read the message at the start of the buffer.
         */
        if (!msg->len)
-               return (struct log *)log_buf;
+               return (struct log *)log_b->buf;
        return msg;
 }
 
 /* get next record; idx must point to valid msg */
-static u32 log_next(u32 idx)
+static u32 log_next(struct log_buffer *log_b, u32 idx)
 {
-       struct log *msg = (struct log *)(log_buf + idx);
+       struct log *msg = (struct log *)(log_b->buf + idx);
 
        /* length == 0 indicates the end of the buffer; wrap */
        /*
@@ -316,14 +364,94 @@ static u32 log_next(u32 idx)
         * return the one after that.
         */
        if (!msg->len) {
-               msg = (struct log *)log_buf;
+               msg = (struct log *)log_b->buf;
                return msg->len;
        }
        return idx + msg->len;
 }
 
+/*
+ * Check whether there is enough free space for the given message.
+ *
+ * The same values of first_idx and next_idx mean that the buffer
+ * is either empty or full.
+ *
+ * If the buffer is empty, we must respect the position of the indexes.
+ * They cannot be reset to the beginning of the buffer.
+ */
+static int logbuf_has_space(struct log_buffer *log_b, u32 msg_size, bool empty)
+{
+       u32 free;
+
+       if (log_b->next_idx > log_b->first_idx || empty)
+               free = max(log_b->len - log_b->next_idx, log_b->first_idx);
+       else
+               free = log_b->first_idx - log_b->next_idx;
+
+       /*
+        * We need space also for an empty header that signalizes wrapping
+        * of the buffer.
+        */
+       return free >= msg_size + sizeof(struct log);
+}
+
+static int log_make_free_space(struct log_buffer *log_b, u32 msg_size)
+{
+       while (log_b->first_seq < log_b->next_seq) {
+               if (logbuf_has_space(log_b, msg_size, false))
+                       return 0;
+               /* drop old messages until we have enough contiguous space */
+               log_b->first_idx = log_next(log_b, log_b->first_idx);
+               log_b->first_seq++;
+       }
+
+       /* sequence numbers are equal, so the log buffer is empty */
+       if (logbuf_has_space(log_b, msg_size, true))
+               return 0;
+
+       return -ENOMEM;
+}
+
+/* compute the message size including the padding bytes */
+static u32 msg_used_size(u16 text_len, u16 dict_len, u32 *pad_len)
+{
+       u32 size;
+
+       size = sizeof(struct log) + text_len + dict_len;
+       *pad_len = (-size) & (LOG_ALIGN - 1);
+       size += *pad_len;
+
+       return size;
+}
+
+/*
+ * Define how much of the log buffer we could take at maximum. The value
+ * must be greater than two. Note that only half of the buffer is available
+ * when the index points to the middle.
+ */
+#define MAX_LOG_TAKE_PART 4
+static const char trunc_msg[] = "<truncated>";
+
+
+static u32 truncate_msg(struct log_buffer *log_b,
+                                               u16 *text_len,
+                                               u16 *dict_len, u32 *pad_len)
+{
+       /*
+        * The message should not take the whole buffer. Otherwise, it might
+        * get removed too soon.
+        */
+       u32 max_text_len = log_b->len / MAX_LOG_TAKE_PART;
+       if (*text_len > max_text_len)
+               *text_len = max_text_len;
+       /* disable the "dict" completely */
+       *dict_len = 0;
+       /* compute the size again, count also the warning message */
+       return msg_used_size(*text_len + strlen(trunc_msg), 0, pad_len);
+}
+
 #ifdef CONFIG_SEC_LOG
-static char initial_log_buf[__LOG_BUF_LEN];
+static char initial_log_buf[__LOG_BUF_K_LEN];
 static unsigned int initial_log_idx = 0;
 static void (*log_text_hook)(char *text, size_t size);
 static char *seclog_buf;
@@ -334,14 +462,14 @@ void register_log_text_hook(void (*f)(char *text, size_t size), char * buf,
        unsigned *position, size_t bufsize)
 {
        unsigned long flags;
-       raw_spin_lock_irqsave(&logbuf_lock, flags);
+       raw_spin_lock_irqsave(&log_buf.lock, flags);
        if (buf && bufsize) {
                seclog_buf = buf;
                seclog_ptr = position;
                seclog_size = bufsize;
                log_text_hook = f;
        }
-       raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+       raw_spin_unlock_irqrestore(&log_buf.lock, flags);
 }
 EXPORT_SYMBOL(register_log_text_hook);
 static size_t msg_print_text(const struct log *msg, enum log_flags prev,
@@ -349,7 +477,8 @@ static size_t msg_print_text(const struct log *msg, enum log_flags prev,
 
 #endif
 /* insert record into the buffer, discard old ones, update heads */
-static void log_store(int facility, int level,
+static int  log_store(struct log_buffer *log_b,
+                     int facility, int level,
                      enum log_flags flags, u64 ts_nsec,
                      const char *dict, u16 dict_len,
                      const char *text, u16 text_len, int cpu)
@@ -358,38 +487,29 @@ static void log_store(int facility, int level,
        u32 size, pad_len;
 
        /* number of '\0' padding bytes to next message */
-       size = sizeof(struct log) + text_len + dict_len;
-       pad_len = (-size) & (LOG_ALIGN - 1);
-       size += pad_len;
+       size = msg_used_size(text_len, dict_len, &pad_len);
 
-       while (log_first_seq < log_next_seq) {
-               u32 free;
-
-               if (log_next_idx > log_first_idx)
-                       free = max(log_buf_len - log_next_idx, log_first_idx);
-               else
-                       free = log_first_idx - log_next_idx;
-
-               if (free > size + sizeof(struct log))
-                       break;
-
-               /* drop old messages until we have enough contiuous space */
-               log_first_idx = log_next(log_first_idx);
-               log_first_seq++;
+       if (log_make_free_space(log_b, size)) {
+               /* truncate the message if it is too long for empty buffer */
+               size = truncate_msg(log_b, &text_len, &dict_len, &pad_len);
+               /* survive when the log buffer is too small for trunc_msg */
+               if (log_make_free_space(log_b, size))
+                       return 0;
        }
 
-       if (log_next_idx + size + sizeof(struct log) >= log_buf_len) {
+       if (log_b->next_idx + size + sizeof(struct log) > log_b->len) {
                /*
                 * This message + an additional empty header does not fit
                 * at the end of the buffer. Add an empty header with len == 0
                 * to signify a wrap around.
                 */
-               memset(log_buf + log_next_idx, 0, sizeof(struct log));
-               log_next_idx = 0;
+               memset(log_b->buf + log_b->next_idx, 0,
+                      sizeof(struct log));
+               log_b->next_idx = 0;
        }
 
        /* fill message */
-       msg = (struct log *)(log_buf + log_next_idx);
+       msg = (struct log *)(log_b->buf + log_b->next_idx);
        memcpy(log_text(msg), text, text_len);
        msg->text_len = text_len;
        memcpy(log_dict(msg), dict, dict_len);
@@ -426,7 +546,7 @@ static void log_store(int facility, int level,
                        sec_text, 1024);
 
                log_text_hook(sec_text, size);
-       } else if (initial_log_idx < (__LOG_BUF_LEN)) {
+       } else if (initial_log_idx < (__LOG_BUF_K_LEN)) {
                /* Storing of kernel boot logs prior to log_text_hook()
                 * registration
                 */
@@ -437,8 +557,237 @@ static void log_store(int facility, int level,
        }
 #endif
        /* insert message */
-       log_next_idx += msg->len;
-       log_next_seq++;
+       log_b->next_idx += msg->len;
+       log_b->next_seq++;
+
+       return msg->text_len;
+}
+
+static bool printk_time = IS_ENABLED(CONFIG_PRINTK_TIME);
+module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR);
+
+static size_t print_time(u64 ts, char *buf)
+{
+       unsigned long rem_nsec;
+
+       if (!printk_time)
+               return 0;
+
+       rem_nsec = do_div(ts, 1000000000);
+
+       if (!buf)
+               return snprintf(NULL, 0, "[%5lu.000000] ", (unsigned long)ts);
+
+       return sprintf(buf, "[%5lu.%06lu] ",
+                      (unsigned long)ts, rem_nsec / 1000);
+}
+
+/*
+ * 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 */
+       int cpu;
+} cont;
+
+static void cont_flush(enum log_flags flags)
+{
+       if (cont.flushed)
+               return;
+       if (cont.len == 0)
+               return;
+
+       if (cont.cons) {
+               /*
+                * 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(&log_buf, cont.facility, cont.level,
+                         flags | LOG_NOCONS, cont.ts_nsec, NULL, 0,
+                         cont.buf, cont.len, cont.cpu);
+               cont.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(&log_buf, cont.facility, cont.level, flags, 0,
+                         NULL, 0, cont.buf, cont.len, cont.cpu);
+               cont.len = 0;
+       }
+}
+
+static bool cont_add(int facility, int level, const char *text, size_t len)
+{
+       if (cont.len && cont.flushed)
+               return false;
+
+       if (cont.len + len > sizeof(cont.buf)) {
+               /* the line gets too long, split it up in separate records */
+               cont_flush(LOG_CONT);
+               return false;
+       }
+
+       if (!cont.len) {
+               cont.facility = facility;
+               cont.level = level;
+               cont.owner = current;
+               cont.ts_nsec = local_clock();
+               cont.flags = 0;
+               cont.cons = 0;
+               cont.flushed = false;
+       }
+
+       memcpy(cont.buf + cont.len, text, len);
+       cont.len += len;
+
+       if (cont.len > (sizeof(cont.buf) * 80) / 100)
+               cont_flush(LOG_CONT);
+
+       return true;
+}
+
+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);
+               size -= textlen;
+       }
+
+       len = cont.len - cont.cons;
+       if (len > 0) {
+               if (len+1 > size)
+                       len = size-1;
+               memcpy(text + textlen, cont.buf + cont.cons, len);
+               textlen += len;
+               cont.cons = cont.len;
+       }
+
+       if (cont.flushed) {
+               if (cont.flags & LOG_NEWLINE)
+                       text[textlen++] = '\n';
+               /* got everything, release buffer */
+               cont.len = 0;
+       }
+       return textlen;
+}
+
+static int log_format_and_store(struct log_buffer *log_b,
+                               int facility, int level,
+                               const char *dict, size_t dictlen,
+                               const char *fmt, int cpu, 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 == DEFAULT_MESSAGE_LOGLEVEL)
+                                       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 == DEFAULT_MESSAGE_LOGLEVEL)
+               level = default_message_loglevel;
+
+       if (dict)
+               lflags |= LOG_PREFIX|LOG_NEWLINE;
+
+       if (log_b != &log_buf)
+               return log_store(log_b, facility, level, lflags, 0,
+                                dict, dictlen, text, text_len, cpu);
+
+       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(log_b, facility, level,
+                                                lflags | LOG_CONT, 0,
+                                                dict, dictlen, text,
+                                                text_len, cpu);
+       } 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 with interrupts (prefix == true) then just
+                * flush it out and store this line separately.
+                * If the preceding printk was from a different task and missed
+                * a newline, flush and append the newline.
+                */
+               if (cont.len) {
+                       if (cont.owner == current && !(lflags & LOG_PREFIX))
+                               stored = cont_add(facility, level, text,
+                                                 text_len);
+                       cont_flush(LOG_NEWLINE);
+               }
+
+               if (stored)
+                       printed_len += text_len;
+               else
+                       printed_len += log_store(log_b, facility, level,
+                                                lflags, 0, dict, dictlen,
+                                                text, text_len, cpu);
+       }
+       return printed_len;
 }
 
 #ifdef CONFIG_SECURITY_DMESG_RESTRICT
@@ -502,6 +851,70 @@ struct devkmsg_user {
        char buf[CONSOLE_EXT_LOG_MAX];
 };
 
+void log_buf_release(struct kref *ref)
+{
+       struct log_buffer *log_b = container_of(ref, struct log_buffer,
+                                               refcount);
+
+       kfree(log_b->buf);
+       kfree(log_b);
+}
+
+#define MAX_PID_LEN    20
+#define MAX_TID_LEN    20
+/*
+ * Fromat below describes dict appended to message written from userspace:
+ * "_PID=<pid>\0_TID=<tid>\0_COMM=<comm>"
+ * KMSG_DICT_MAX_LEN definition represents maximal length of this dict.
+ */
+#define KMSG_DICT_MAX_LEN      (5 + MAX_PID_LEN + 1 + \
+                                5 + MAX_TID_LEN + 1 + \
+                                6 + TASK_COMM_LEN)
+
+static size_t set_kmsg_dict(char *buf)
+{
+       size_t len;
+
+       len = sprintf(buf, "_PID=%d", task_tgid_nr(current)) + 1;
+       len += sprintf(buf + len, "_TID=%d", task_pid_nr(current)) + 1;
+       memcpy(buf + len, "_COMM=", 6);
+       len += 6;
+       get_task_comm(buf + len, current);
+       while (buf[len] != '\0')
+               len++;
+       return len;
+}
+
+static int kmsg_sys_write(int minor, int level,
+                         const char *dict, size_t dictlen,
+                         const char *fmt, ...)
+{
+       va_list args;
+       int ret = -ENXIO;
+       struct log_buffer *log_b;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(log_b, &log_buf.list, list) {
+               if (log_b->minor != minor)
+                       continue;
+
+               raw_spin_lock(&log_b->lock);
+
+               va_start(args, fmt);
+               log_format_and_store(log_b, 1 /* LOG_USER */, level,
+                                    dict, dictlen, fmt, smp_processor_id(), args);
+               va_end(args);
+               wake_up_interruptible(&log_b->wait);
+
+               raw_spin_unlock(&log_b->lock);
+
+               ret = 0;
+               break;
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
 static ssize_t devkmsg_writev(struct kiocb *iocb, const struct iovec *iv,
                              unsigned long count, loff_t pos)
 {
@@ -510,7 +923,10 @@ static ssize_t devkmsg_writev(struct kiocb *iocb, const struct iovec *iv,
        int level = default_message_loglevel;
        int facility = 1;       /* LOG_USER */
        size_t len = iov_length(iv, count);
+       char dict[KMSG_DICT_MAX_LEN];
+       size_t dictlen;
        ssize_t ret = len;
+       int minor = iminor(iocb->ki_filp->f_inode);
 
        if (len > LOG_LINE_MAX)
                return -EINVAL;
@@ -552,14 +968,24 @@ static ssize_t devkmsg_writev(struct kiocb *iocb, const struct iovec *iv,
        }
        line[len] = '\0';
 
-       printk_emit(facility, level, NULL, 0, "%s", line);
+       dictlen = set_kmsg_dict(dict);
+
+       if (minor == log_buf.minor) {
+               printk_emit(facility, level, dict, dictlen, "%s", line);
+       } else {
+               int error = kmsg_sys_write(minor, level, dict, dictlen, "%s", line);
+
+               if (error)
+                       ret = error;
+       }
+
 out:
        kfree(buf);
        return ret;
 }
 
-static ssize_t devkmsg_read(struct file *file, char __user *buf,
-                           size_t count, loff_t *ppos)
+static ssize_t kmsg_read(struct log_buffer *log_b, struct file *file,
+                        char __user *buf, size_t count, loff_t *ppos)
 {
        struct devkmsg_user *user = file->private_data;
        struct log *msg;
@@ -569,9 +995,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
        char cont = '-';
        size_t len;
        ssize_t ret;
-
-       if (!user)
-               return -EBADF;
+       const int prime = (log_b == &log_buf);
 
        p = user->buf;
        e = user->buf + sizeof(user->buf);
@@ -579,32 +1003,43 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
        ret = mutex_lock_interruptible(&user->lock);
        if (ret)
                return ret;
-       raw_spin_lock_irq(&logbuf_lock);
-       while (user->seq == log_next_seq) {
+       raw_spin_lock_irq(&log_b->lock);
+       while (user->seq == log_b->next_seq) {
                if (file->f_flags & O_NONBLOCK) {
                        ret = -EAGAIN;
-                       raw_spin_unlock_irq(&logbuf_lock);
+                       raw_spin_unlock_irq(&log_b->lock);
                        goto out;
                }
 
-               raw_spin_unlock_irq(&logbuf_lock);
-               ret = wait_event_interruptible(log_wait,
-                                              user->seq != log_next_seq);
+               raw_spin_unlock_irq(&log_b->lock);
+
+               if (log_b == &log_buf) {
+                       ret = wait_event_interruptible(log_b->wait,
+                                               user->seq != log_b->next_seq);
+               } else {
+                       kref_get(&log_b->refcount);
+                       ret = wait_event_interruptible(log_b->wait,
+                                               user->seq != log_b->next_seq);
+                       if (log_b->minor == -1)
+                               ret = -ENXIO;
+                       if (kref_put(&log_b->refcount, log_buf_release))
+                               ret = -ENXIO;
+               }
                if (ret)
                        goto out;
-               raw_spin_lock_irq(&logbuf_lock);
+               raw_spin_lock_irq(&log_b->lock);
        }
 
-       if (user->seq < log_first_seq) {
+       if (user->seq < log_b->first_seq) {
                /* our last seen message is gone, return error and reset */
-               user->idx = log_first_idx;
-               user->seq = log_first_seq;
+               user->idx = log_b->first_idx;
+               user->seq = log_b->first_seq;
                ret = -EPIPE;
-               raw_spin_unlock_irq(&logbuf_lock);
+               raw_spin_unlock_irq(&log_b->lock);
                goto out;
        }
 
-       msg = log_from_idx(user->idx);
+       msg = log_from_idx(log_b, user->idx);
        ts_usec = msg->ts_nsec;
        do_div(ts_usec, 1000);
 
@@ -627,15 +1062,23 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
                       user->seq, ts_usec, cont);
        user->prev = msg->flags;
 
-       /* escape non-printable characters */
        for (i = 0; i < msg->text_len; i++) {
                unsigned char c = log_text(msg)[i];
 
-               if (c < ' ' || c >= 127 || c == '\\')
+               if (prime && (c < ' ' || c >= 127 || c == '\\'))
                        p += scnprintf(p, e - p, "\\x%02x", c);
                else
                        append_char(&p, e, c);
        }
+
+       /*
+        * The \0 is delimits the text part, while the newline is for formatting
+        * when catting the device directly. We cannot use \n for delimiting due
+        * to security: else one could forge dictionary tags through the message
+        * such as "text\n _PID=123"
+        */
+       if (!prime)
+               append_char(&p, e, '\0');
        append_char(&p, e, '\n');
 
        if (msg->dict_len) {
@@ -655,7 +1098,7 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
                                continue;
                        }
 
-                       if (c < ' ' || c >= 127 || c == '\\') {
+                       if (prime && (c < ' ' || c >= 127 || c == '\\')) {
                                p += scnprintf(p, e - p, "\\x%02x", c);
                                continue;
                        }
@@ -665,9 +1108,9 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
                append_char(&p, e, '\n');
        }
 
-       user->idx = log_next(user->idx);
+       user->idx = log_next(log_b, user->idx);
        user->seq++;
-       raw_spin_unlock_irq(&logbuf_lock);
+       raw_spin_unlock_irq(&log_b->lock);
 
        len = p - user->buf;
        if (len > count) {
@@ -683,118 +1126,523 @@ static ssize_t devkmsg_read(struct file *file, char __user *buf,
 out:
        mutex_unlock(&user->lock);
        return ret;
+
 }
 
-static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
+static ssize_t devkmsg_read(struct file *file, char __user *buf,
+                           size_t count, loff_t *ppos)
 {
        struct devkmsg_user *user = file->private_data;
-       loff_t ret = 0;
+       ssize_t ret = -ENXIO;
+       int minor = iminor(file->f_inode);
+       struct log_buffer *log_b;
+       int found = 0;
 
        if (!user)
                return -EBADF;
-       if (offset)
-               return -ESPIPE;
 
-       raw_spin_lock_irq(&logbuf_lock);
+       if (minor == log_buf.minor)
+               return kmsg_read(&log_buf, file, buf, count, ppos);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(log_b, &log_buf.list, list) {
+               if (log_b->minor == minor) {
+                       found = 1;
+                       kref_get(&log_b->refcount);
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       if(found){
+               ret = kmsg_read(log_b, file, buf, count, ppos);
+               kref_put(&log_b->refcount, log_buf_release);
+       }
+       return ret;
+}
+
+static loff_t kmsg_llseek(struct log_buffer *log_b, struct file *file,
+                         int whence)
+{
+       struct devkmsg_user *user = file->private_data;
+       loff_t ret = 0;
+
+       raw_spin_lock_irq(&log_b->lock);
        switch (whence) {
        case SEEK_SET:
                /* the first record */
-               user->idx = log_first_idx;
-               user->seq = log_first_seq;
+               user->idx = log_b->first_idx;
+               user->seq = log_b->first_seq;
                break;
        case SEEK_DATA:
                /*
                 * The first record after the last SYSLOG_ACTION_CLEAR,
-                * like issued by 'dmesg -c'. Reading /dev/kmsg itself
-                * changes no global state, and does not clear anything.
+                * like issued by 'dmesg -c' or KMSG_CMD_CLEAR ioctl
+                * command. Reading /dev/kmsg itself changes no global
+                * state, and does not clear anything.
                 */
-               user->idx = clear_idx;
-               user->seq = clear_seq;
+               user->idx = log_b->clear_idx;
+               user->seq = log_b->clear_seq;
                break;
        case SEEK_END:
                /* after the last record */
-               user->idx = log_next_idx;
-               user->seq = log_next_seq;
+               user->idx = log_b->next_idx;
+               user->seq = log_b->next_seq;
                break;
        default:
                ret = -EINVAL;
        }
-       raw_spin_unlock_irq(&logbuf_lock);
+       raw_spin_unlock_irq(&log_b->lock);
+       return ret;
+}
+
+static loff_t devkmsg_llseek(struct file *file, loff_t offset, int whence)
+{
+       struct devkmsg_user *user = file->private_data;
+       loff_t ret = -ENXIO;
+       int minor = iminor(file->f_inode);
+       struct log_buffer *log_b;
+
+       if (!user)
+               return -EBADF;
+       if (offset)
+               return -ESPIPE;
+
+       if (minor == log_buf.minor)
+               return kmsg_llseek(&log_buf, file, whence);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(log_b, &log_buf.list, list) {
+               if (log_b->minor == minor) {
+                       ret = kmsg_llseek(log_b, file, whence);
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
+static unsigned int kmsg_poll(struct log_buffer *log_b,
+                             struct file *file, poll_table *wait)
+{
+       struct devkmsg_user *user = file->private_data;
+       int ret = 0;
+
+       poll_wait(file, &log_b->wait, wait);
+
+       raw_spin_lock_irq(&log_b->lock);
+       if (user->seq < log_b->next_seq) {
+               /* return error when data has vanished underneath us */
+               if (user->seq < log_b->first_seq)
+                       ret = POLLIN|POLLRDNORM|POLLERR|POLLPRI;
+               else
+                       ret = POLLIN|POLLRDNORM;
+       }
+       raw_spin_unlock_irq(&log_b->lock);
+
        return ret;
 }
 
-static unsigned int devkmsg_poll(struct file *file, poll_table *wait)
+static unsigned int devkmsg_poll(struct file *file, poll_table *wait)
+{
+       struct devkmsg_user *user = file->private_data;
+       int ret = POLLERR|POLLNVAL;
+       int minor = iminor(file->f_inode);
+       struct log_buffer *log_b;
+
+       if (!user)
+               return POLLERR|POLLNVAL;
+
+       if (minor == log_buf.minor)
+               return kmsg_poll(&log_buf, file, wait);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(log_b, &log_buf.list, list) {
+               if (log_b->minor == minor) {
+                       kref_get(&log_b->refcount);
+                       rcu_read_unlock();
+
+                       ret = kmsg_poll(log_b, file, wait);
+
+                       if (kref_put(&log_b->refcount, log_buf_release))
+                               return POLLERR|POLLNVAL;
+                       return ret;
+               }
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
+static int kmsg_open(struct log_buffer *log_b, struct file *file)
+{
+       struct devkmsg_user *user;
+
+       user = kmalloc(sizeof(struct devkmsg_user), GFP_KERNEL);
+       if (!user)
+               return -ENOMEM;
+
+       mutex_init(&user->lock);
+
+       raw_spin_lock_irq(&log_b->lock);
+       user->idx = log_b->first_idx;
+       user->seq = log_b->first_seq;
+       raw_spin_unlock_irq(&log_b->lock);
+
+       file->private_data = user;
+       return 0;
+}
+
+static int devkmsg_open(struct inode *inode, struct file *file)
+{
+       int ret = -ENXIO;
+       int minor = iminor(file->f_inode);
+       struct log_buffer *log_b;
+       int found = 0;
+
+       /* write-only does not need any file context */
+       if ((file->f_flags & O_ACCMODE) == O_WRONLY)
+               return 0;
+
+       if (minor == log_buf.minor) {
+               ret = check_syslog_permissions(SYSLOG_ACTION_READ_ALL,
+                                              SYSLOG_FROM_READER);
+               if (ret)
+                       return ret;
+
+               return kmsg_open(&log_buf, file);
+       }
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(log_b, &log_buf.list, list) {
+               if (log_b->minor == minor) {
+                       found = 1;
+                       kref_get(&log_b->refcount);
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       if(found){
+               ret = kmsg_open(log_b, file);
+               kref_put(&log_b->refcount, log_buf_release);
+       }
+       return ret;
+}
+
+static long kmsg_ioctl(struct log_buffer *log_b, unsigned int cmd,
+                      unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       static const u32 read_size_max = CONSOLE_EXT_LOG_MAX;
+
+       switch (cmd) {
+       case KMSG_CMD_GET_BUF_SIZE:
+               if (copy_to_user(argp, &log_b->len, sizeof(u32)))
+                       return -EFAULT;
+               break;
+       case KMSG_CMD_GET_READ_SIZE_MAX:
+               if (copy_to_user(argp, &read_size_max, sizeof(u32)))
+                       return -EFAULT;
+               break;
+       case KMSG_CMD_CLEAR:
+               if (!capable(CAP_SYSLOG))
+                       return -EPERM;
+               raw_spin_lock_irq(&log_b->lock);
+               log_b->clear_seq = log_b->next_seq;
+               log_b->clear_idx = log_b->next_idx;
+               raw_spin_unlock_irq(&log_b->lock);
+               break;
+       default:
+               return -ENOTTY;
+       }
+       return 0;
+}
+
+static long devkmsg_ioctl(struct file *file, unsigned int cmd,
+                         unsigned long arg)
+{
+       long ret = -ENXIO;
+       int minor = iminor(file->f_inode);
+       struct log_buffer *log_b;
+
+       if (minor == log_buf.minor)
+               return kmsg_ioctl(&log_buf, cmd, arg);
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(log_b, &log_buf.list, list) {
+               if (log_b->minor == minor) {
+                       ret = kmsg_ioctl(log_b, cmd, arg);
+                       break;
+               }
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
+static int devkmsg_release(struct inode *inode, struct file *file)
+{
+       struct devkmsg_user *user = file->private_data;
+
+       if (!user)
+               return 0;
+
+       mutex_destroy(&user->lock);
+       kfree(user);
+       return 0;
+}
+
+const struct file_operations kmsg_fops = {
+       .open = devkmsg_open,
+       .read = devkmsg_read,
+       .aio_write = devkmsg_writev,
+       .llseek = devkmsg_llseek,
+       .poll = devkmsg_poll,
+       .unlocked_ioctl = devkmsg_ioctl,
+       .compat_ioctl = devkmsg_ioctl,
+       .release = devkmsg_release,
+};
+
+#define MAX_MINOR_LEN  20
+
+static int kmsg_open_ext(struct inode *inode, struct file *file)
+{
+       return kmsg_fops.open(inode, file);
+}
+
+static ssize_t kmsg_writev_ext(struct kiocb *iocb, const struct iovec *iov, unsigned long count, loff_t pos)
+{
+       return kmsg_fops.aio_write(iocb, iov, count, pos);
+}
+
+static ssize_t kmsg_read_ext(struct file *file, char __user *buf,
+                            size_t count, loff_t *ppos)
+{
+       return kmsg_fops.read(file, buf, count, ppos);
+}
+
+static loff_t kmsg_llseek_ext(struct file *file, loff_t offset, int whence)
+{
+       return kmsg_fops.llseek(file, offset, whence);
+}
+
+static unsigned int kmsg_poll_ext(struct file *file,
+                                 struct poll_table_struct *wait)
+{
+       return kmsg_fops.poll(file, wait);
+}
+
+static long kmsg_ioctl_buffers(struct file *file, unsigned int cmd,
+                              unsigned long arg)
+{
+       void __user *argp = (void __user *)arg;
+       size_t size;
+       umode_t mode;
+       char name[4 + MAX_MINOR_LEN + 1];
+       struct device *dev;
+       int minor;
+
+       if (iminor(file->f_inode) != log_buf.minor)
+               return -ENOTTY;
+
+       switch (cmd) {
+       case KMSG_CMD_BUFFER_ADD:
+               if (copy_from_user(&size, argp, sizeof(size)))
+                       return -EFAULT;
+               argp += sizeof(size);
+               if (copy_from_user(&mode, argp, sizeof(mode)))
+                       return -EFAULT;
+               argp += sizeof(mode);
+               minor = kmsg_sys_buffer_add(size, mode);
+               if (minor < 0)
+                       return minor;
+               sprintf(name, "kmsg%d", minor);
+               dev = device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
+                                   NULL, name);
+               if (IS_ERR(dev)) {
+                       kmsg_sys_buffer_del(minor);
+                       return PTR_ERR(dev);
+               }
+               if (copy_to_user(argp, &minor, sizeof(minor))) {
+                       device_destroy(mem_class, MKDEV(MEM_MAJOR, minor));
+                       kmsg_sys_buffer_del(minor);
+                       return -EFAULT;
+               }
+               return 0;
+       case KMSG_CMD_BUFFER_DEL:
+               if (copy_from_user(&minor, argp, sizeof(minor)))
+                       return -EFAULT;
+               if (minor <= log_buf.minor)
+                       return -EINVAL;
+               device_destroy(mem_class, MKDEV(MEM_MAJOR, minor));
+               kmsg_sys_buffer_del(minor);
+               return 0;
+       }
+       return -ENOTTY;
+}
+
+static long kmsg_unlocked_ioctl_ext(struct file *file, unsigned int cmd,
+                                   unsigned long arg)
+{
+       long ret = kmsg_ioctl_buffers(file, cmd, arg);
+
+       if (ret == -ENOTTY)
+               return kmsg_fops.unlocked_ioctl(file, cmd, arg);
+       return ret;
+}
+
+static long kmsg_compat_ioctl_ext(struct file *file, unsigned int cmd,
+                                 unsigned long arg)
+{
+       long ret = kmsg_ioctl_buffers(file, cmd, arg);
+
+       if (ret == -ENOTTY)
+               return kmsg_fops.compat_ioctl(file, cmd, arg);
+       return ret;
+}
+
+static int kmsg_release_ext(struct inode *inode, struct file *file)
+{
+       return kmsg_fops.release(inode, file);
+}
+
+const struct file_operations kmsg_fops_ext = {
+       .open           = kmsg_open_ext,
+       .read           = kmsg_read_ext,
+       .aio_write      = kmsg_writev_ext,
+       .llseek         = kmsg_llseek_ext,
+       .poll           = kmsg_poll_ext,
+       .unlocked_ioctl = kmsg_unlocked_ioctl_ext,
+       .compat_ioctl   = kmsg_compat_ioctl_ext,
+       .release        = kmsg_release_ext,
+};
+
+/* Should be used for device registration */
+struct device *init_kmsg(int minor, umode_t mode)
+{
+       log_buf.minor = minor;
+       log_buf.mode = mode;
+       return device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
+                       NULL, "kmsg");
+}
+
+int kmsg_memory_open(struct inode *inode, struct file *filp)
+{
+       filp->f_op = &kmsg_fops;
+
+       return kmsg_fops.open(inode, filp);
+}
+
+int kmsg_memory_open_ext(struct inode *inode, struct file *filp)
+{
+       filp->f_op = &kmsg_fops_ext;
+
+       return kmsg_fops_ext.open(inode, filp);
+}
+
+int kmsg_mode(int minor, umode_t *mode)
 {
-       struct devkmsg_user *user = file->private_data;
-       int ret = 0;
+       int ret = -ENXIO;
+       struct log_buffer *log_b;
 
-       if (!user)
-               return POLLERR|POLLNVAL;
-
-       poll_wait(file, &log_wait, wait);
+       if (minor == log_buf.minor) {
+               *mode = log_buf.mode;
+               return 0;
+       }
 
-       raw_spin_lock_irq(&logbuf_lock);
-       if (user->seq < log_next_seq) {
-               /* return error when data has vanished underneath us */
-               if (user->seq < log_first_seq)
-                       ret = POLLIN|POLLRDNORM|POLLERR|POLLPRI;
-               else
-                       ret = POLLIN|POLLRDNORM;
+       rcu_read_lock();
+       list_for_each_entry_rcu(log_b, &log_buf.list, list) {
+               if (log_b->minor == minor) {
+                       *mode = log_b->mode;
+                       ret = 0;
+                       break;
+               }
        }
-       raw_spin_unlock_irq(&logbuf_lock);
+       rcu_read_unlock();
 
        return ret;
 }
 
-static int devkmsg_open(struct inode *inode, struct file *file)
+static DEFINE_SPINLOCK(kmsg_sys_list_lock);
+
+int kmsg_sys_buffer_add(size_t size, umode_t mode)
 {
-       struct devkmsg_user *user;
-       int err;
+       unsigned long flags;
+       int minor = log_buf.minor;
+       struct log_buffer *log_b;
+       struct log_buffer *log_b_new;
 
-       /* write-only does not need any file context */
-       if ((file->f_flags & O_ACCMODE) == O_WRONLY)
-               return 0;
+       if (size < LOG_LINE_MAX + PREFIX_MAX)
+               return -EINVAL;
 
-       err = check_syslog_permissions(SYSLOG_ACTION_READ_ALL,
-                                      SYSLOG_FROM_READER);
-       if (err)
-               return err;
+       log_b_new = kzalloc(sizeof(struct log_buffer), GFP_KERNEL);
+       if (!log_b_new)
+               return -ENOMEM;
 
-       user = kmalloc(sizeof(struct devkmsg_user), GFP_KERNEL);
-       if (!user)
+       log_b_new->buf = kmalloc(size, GFP_KERNEL);
+       if (!log_b_new->buf) {
+               kfree(log_b_new);
                return -ENOMEM;
+       }
 
-       mutex_init(&user->lock);
+       log_b_new->len = size;
+       log_b_new->lock = __RAW_SPIN_LOCK_UNLOCKED(log_b_new->lock);
+       init_waitqueue_head(&log_b_new->wait);
+       kref_init(&log_b_new->refcount);
+       log_b_new->mode = mode;
 
-       raw_spin_lock_irq(&logbuf_lock);
-       user->idx = log_first_idx;
-       user->seq = log_first_seq;
-       raw_spin_unlock_irq(&logbuf_lock);
+       kref_get(&log_b_new->refcount);
 
-       file->private_data = user;
-       return 0;
+       spin_lock_irqsave(&kmsg_sys_list_lock, flags);
+
+       list_for_each_entry(log_b, &log_buf.list, list) {
+               if (log_b->minor - minor > 1)
+                       break;
+
+               minor = log_b->minor;
+       }
+
+       if (!(minor & MINORMASK) || (minor & MINORMASK) >= KMSG_NUM_MAX) {
+               kref_put(&log_b->refcount, log_buf_release);
+               spin_unlock_irqrestore(&kmsg_sys_list_lock, flags);
+               return -ERANGE;
+       }
+
+       minor += 1;
+       log_b_new->minor = minor;
+
+       list_add_tail_rcu(&log_b_new->list, &log_b->list);
+
+       spin_unlock_irqrestore(&kmsg_sys_list_lock, flags);
+
+       return minor;
 }
 
-static int devkmsg_release(struct inode *inode, struct file *file)
+void kmsg_sys_buffer_del(int minor)
 {
-       struct devkmsg_user *user = file->private_data;
+       unsigned long flags;
+       struct log_buffer *log_b;
 
-       if (!user)
-               return 0;
+       spin_lock_irqsave(&kmsg_sys_list_lock, flags);
 
-       mutex_destroy(&user->lock);
-       kfree(user);
-       return 0;
-}
+       list_for_each_entry(log_b, &log_buf.list, list) {
+               if (log_b->minor == minor)
+                       break;
+       }
 
-const struct file_operations kmsg_fops = {
-       .open = devkmsg_open,
-       .read = devkmsg_read,
-       .aio_write = devkmsg_writev,
-       .llseek = devkmsg_llseek,
-       .poll = devkmsg_poll,
-       .release = devkmsg_release,
-};
+       if (log_b == &log_buf) {
+               spin_unlock_irqrestore(&kmsg_sys_list_lock, flags);
+               return;
+       }
+
+       list_del_rcu(&log_b->list);
+
+       spin_unlock_irqrestore(&kmsg_sys_list_lock, flags);
+
+       log_b->minor = -1;
+       wake_up_interruptible(&log_b->wait);
+
+       kref_put(&log_b->refcount, log_buf_release);
+}
 
 #ifdef CONFIG_KEXEC
 /*
@@ -808,9 +1656,11 @@ const struct file_operations kmsg_fops = {
 void log_buf_kexec_setup(void)
 {
        VMCOREINFO_SYMBOL(log_buf);
-       VMCOREINFO_SYMBOL(log_buf_len);
-       VMCOREINFO_SYMBOL(log_first_idx);
-       VMCOREINFO_SYMBOL(log_next_idx);
+       VMCOREINFO_STRUCT_SIZE(log_buffer);
+       VMCOREINFO_OFFSET(log_buffer, buf);
+       VMCOREINFO_OFFSET(log_buffer, len);
+       VMCOREINFO_OFFSET(log_buffer, first_idx);
+       VMCOREINFO_OFFSET(log_buffer, next_idx);
        /*
         * Export struct log size and field offsets. User space tools can
         * parse it and detect any changes to structure down the line.
@@ -823,7 +1673,7 @@ void log_buf_kexec_setup(void)
 }
 #endif
 
-/* requested log_buf_len from kernel cmdline */
+/* requested log_buf.len from kernel cmdline */
 static unsigned long __initdata new_log_buf_len;
 
 /* save requested log_buf_len since it's too early to process it */
@@ -833,7 +1683,7 @@ static int __init log_buf_len_setup(char *str)
 
        if (size)
                size = roundup_pow_of_two(size);
-       if (size > log_buf_len)
+       if (size > log_buf.len)
                new_log_buf_len = size;
 
        return 0;
@@ -861,22 +1711,22 @@ void __init setup_log_buf(int early)
        }
 
        if (unlikely(!new_log_buf)) {
-               pr_err("log_buf_len: %ld bytes not available\n",
+               pr_err("log_buf.len: %ld bytes not available\n",
                        new_log_buf_len);
                return;
        }
 
-       raw_spin_lock_irqsave(&logbuf_lock, flags);
-       log_buf_len = new_log_buf_len;
-       log_buf = new_log_buf;
+       raw_spin_lock_irqsave(&log_buf.lock, flags);
+       log_buf.len = new_log_buf_len;
+       log_buf.buf = new_log_buf;
        new_log_buf_len = 0;
-       free = __LOG_BUF_LEN - log_next_idx;
-       memcpy(log_buf, __log_buf, __LOG_BUF_LEN);
-       raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+       free = __LOG_BUF_K_LEN - log_buf.next_idx;
+       memcpy(log_buf.buf, __log_buf_k, __LOG_BUF_K_LEN);
+       raw_spin_unlock_irqrestore(&log_buf.lock, flags);
 
-       pr_info("log_buf_len: %d\n", log_buf_len);
+       pr_info("log_buf.len: %d\n", log_buf.len);
        pr_info("early log buf free: %d(%d%%)\n",
-               free, (free * 100) / __LOG_BUF_LEN);
+               free, (free * 100) / __LOG_BUF_K_LEN);
 }
 
 static bool __read_mostly ignore_loglevel;
@@ -949,29 +1799,6 @@ static inline void boot_delay_msec(int level)
 }
 #endif
 
-#if defined(CONFIG_PRINTK_TIME)
-static bool printk_time = 1;
-#else
-static bool printk_time;
-#endif
-module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR);
-
-static size_t print_time(u64 ts, char *buf)
-{
-       unsigned long rem_nsec;
-
-       if (!printk_time)
-               return 0;
-
-       rem_nsec = do_div(ts, 1000000000);
-
-       if (!buf)
-               return snprintf(NULL, 0, "[%5lu.000000] ", (unsigned long)ts);
-
-       return sprintf(buf, "[%5lu.%06lu] ",
-                      (unsigned long)ts, rem_nsec / 1000);
-}
-
 #ifdef CONFIG_PRINTK_PROCESS
 static size_t print_process(const struct log *msg, char *buf)
 {
@@ -1085,26 +1912,26 @@ static int syslog_print(char __user *buf, int size)
                size_t n;
                size_t skip;
 
-               raw_spin_lock_irq(&logbuf_lock);
-               if (syslog_seq < log_first_seq) {
+               raw_spin_lock_irq(&log_buf.lock);
+               if (syslog_seq < log_buf.first_seq) {
                        /* messages are gone, move to first one */
-                       syslog_seq = log_first_seq;
-                       syslog_idx = log_first_idx;
+                       syslog_seq = log_buf.first_seq;
+                       syslog_idx = log_buf.first_idx;
                        syslog_prev = 0;
                        syslog_partial = 0;
                }
-               if (syslog_seq == log_next_seq) {
-                       raw_spin_unlock_irq(&logbuf_lock);
+               if (syslog_seq == log_buf.next_seq) {
+                       raw_spin_unlock_irq(&log_buf.lock);
                        break;
                }
 
                skip = syslog_partial;
-               msg = log_from_idx(syslog_idx);
+               msg = log_from_idx(&log_buf, syslog_idx);
                n = msg_print_text(msg, syslog_prev, true, text,
                                   LOG_LINE_MAX + PREFIX_MAX);
                if (n - syslog_partial <= size) {
                        /* message fits into buffer, move forward */
-                       syslog_idx = log_next(syslog_idx);
+                       syslog_idx = log_next(&log_buf, syslog_idx);
                        syslog_seq++;
                        syslog_prev = msg->flags;
                        n -= syslog_partial;
@@ -1115,7 +1942,7 @@ static int syslog_print(char __user *buf, int size)
                        syslog_partial += n;
                } else
                        n = 0;
-               raw_spin_unlock_irq(&logbuf_lock);
+               raw_spin_unlock_irq(&log_buf.lock);
 
                if (!n)
                        break;
@@ -1144,55 +1971,55 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
        if (!text)
                return -ENOMEM;
 
-       raw_spin_lock_irq(&logbuf_lock);
+       raw_spin_lock_irq(&log_buf.lock);
        if (buf) {
                u64 next_seq;
                u64 seq;
                u32 idx;
                enum log_flags prev;
 
-               if (clear_seq < log_first_seq) {
+               if (log_buf.clear_seq < log_buf.first_seq) {
                        /* messages are gone, move to first available one */
-                       clear_seq = log_first_seq;
-                       clear_idx = log_first_idx;
+                       log_buf.clear_seq = log_buf.first_seq;
+                       log_buf.clear_idx = log_buf.first_idx;
                }
 
                /*
                 * Find first record that fits, including all following records,
                 * into the user-provided buffer for this dump.
                 */
-               seq = clear_seq;
-               idx = clear_idx;
+               seq = log_buf.clear_seq;
+               idx = log_buf.clear_idx;
                prev = 0;
-               while (seq < log_next_seq) {
-                       struct log *msg = log_from_idx(idx);
+               while (seq < log_buf.next_seq) {
+                       struct log *msg = log_from_idx(&log_buf, idx);
 
                        len += msg_print_text(msg, prev, true, NULL, 0);
                        prev = msg->flags;
-                       idx = log_next(idx);
+                       idx = log_next(&log_buf, idx);
                        seq++;
                }
 
                /* move first record forward until length fits into the buffer */
-               seq = clear_seq;
-               idx = clear_idx;
+               seq = log_buf.clear_seq;
+               idx = log_buf.clear_idx;
                prev = 0;
-               while (len > size && seq < log_next_seq) {
-                       struct log *msg = log_from_idx(idx);
+               while (len > size && seq < log_buf.next_seq) {
+                       struct log *msg = log_from_idx(&log_buf, idx);
 
                        len -= msg_print_text(msg, prev, true, NULL, 0);
                        prev = msg->flags;
-                       idx = log_next(idx);
+                       idx = log_next(&log_buf, idx);
                        seq++;
                }
 
                /* last message fitting into this dump */
-               next_seq = log_next_seq;
+               next_seq = log_buf.next_seq;
 
                len = 0;
                prev = 0;
                while (len >= 0 && seq < next_seq) {
-                       struct log *msg = log_from_idx(idx);
+                       struct log *msg = log_from_idx(&log_buf, idx);
                        int textlen;
 
                        textlen = msg_print_text(msg, prev, true, text,
@@ -1201,31 +2028,31 @@ static int syslog_print_all(char __user *buf, int size, bool clear)
                                len = textlen;
                                break;
                        }
-                       idx = log_next(idx);
+                       idx = log_next(&log_buf, idx);
                        seq++;
                        prev = msg->flags;
 
-                       raw_spin_unlock_irq(&logbuf_lock);
+                       raw_spin_unlock_irq(&log_buf.lock);
                        if (copy_to_user(buf + len, text, textlen))
                                len = -EFAULT;
                        else
                                len += textlen;
-                       raw_spin_lock_irq(&logbuf_lock);
+                       raw_spin_lock_irq(&log_buf.lock);
 
-                       if (seq < log_first_seq) {
+                       if (seq < log_buf.first_seq) {
                                /* messages are gone, move to next one */
-                               seq = log_first_seq;
-                               idx = log_first_idx;
+                               seq = log_buf.first_seq;
+                               idx = log_buf.first_idx;
                                prev = 0;
                        }
                }
        }
 
        if (clear) {
-               clear_seq = log_next_seq;
-               clear_idx = log_next_idx;
+               log_buf.clear_seq = log_buf.next_seq;
+               log_buf.clear_idx = log_buf.next_idx;
        }
-       raw_spin_unlock_irq(&logbuf_lock);
+       raw_spin_unlock_irq(&log_buf.lock);
 
        kfree(text);
        return len;
@@ -1261,8 +2088,8 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
                        error = -EFAULT;
                        goto out;
                }
-               error = wait_event_interruptible(log_wait,
-                                                syslog_seq != log_next_seq);
+               error = wait_event_interruptible(log_buf.wait,
+                                               syslog_seq != log_buf.next_seq);
                if (error)
                        goto out;
                error = syslog_print(buf, len);
@@ -1316,11 +2143,11 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
                break;
        /* Number of chars in the log buffer */
        case SYSLOG_ACTION_SIZE_UNREAD:
-               raw_spin_lock_irq(&logbuf_lock);
-               if (syslog_seq < log_first_seq) {
+               raw_spin_lock_irq(&log_buf.lock);
+               if (syslog_seq < log_buf.first_seq) {
                        /* messages are gone, move to first one */
-                       syslog_seq = log_first_seq;
-                       syslog_idx = log_first_idx;
+                       syslog_seq = log_buf.first_seq;
+                       syslog_idx = log_buf.first_idx;
                        syslog_prev = 0;
                        syslog_partial = 0;
                }
@@ -1330,28 +2157,28 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
                         * for pending data, not the size; return the count of
                         * records, not the length.
                         */
-                       error = log_next_idx - syslog_idx;
+                       error = log_buf.next_idx - syslog_idx;
                } else {
                        u64 seq = syslog_seq;
                        u32 idx = syslog_idx;
                        enum log_flags prev = syslog_prev;
 
                        error = 0;
-                       while (seq < log_next_seq) {
-                               struct log *msg = log_from_idx(idx);
+                       while (seq < log_buf.next_seq) {
+                               struct log *msg = log_from_idx(&log_buf, idx);
 
                                error += msg_print_text(msg, prev, true, NULL, 0);
-                               idx = log_next(idx);
+                               idx = log_next(&log_buf, idx);
                                seq++;
                                prev = msg->flags;
                        }
                        error -= syslog_partial;
                }
-               raw_spin_unlock_irq(&logbuf_lock);
+               raw_spin_unlock_irq(&log_buf.lock);
                break;
        /* Size of the log buffer */
        case SYSLOG_ACTION_SIZE_BUFFER:
-               error = log_buf_len;
+               error = log_buf.len;
                break;
        default:
                error = -EINVAL;
@@ -1413,7 +2240,7 @@ static void zap_locks(void)
 
        debug_locks_off();
        /* If a crash is occurring, make sure we can't deadlock */
-       raw_spin_lock_init(&logbuf_lock);
+       raw_spin_lock_init(&log_buf.lock);
        /* And make sure that we print immediately */
        sema_init(&console_sem, 1);
 }
@@ -1449,12 +2276,12 @@ static inline int can_use_console(unsigned int cpu)
  * console_lock held, and 'console_locked' set) if it
  * is successful, false otherwise.
  *
- * This gets called with the 'logbuf_lock' spinlock held and
+ * This gets called with the 'log_buf.lock' spinlock held and
  * interrupts disabled. It should return with 'lockbuf_lock'
  * released but interrupts still disabled.
  */
 static int console_trylock_for_printk(unsigned int cpu)
-       __releases(&logbuf_lock)
+       __releases(&log_buf.lock)
 {
        int retval = 0, wake = 0;
 
@@ -1474,7 +2301,7 @@ static int console_trylock_for_printk(unsigned int cpu)
                }
        }
        logbuf_cpu = UINT_MAX;
-       raw_spin_unlock(&logbuf_lock);
+       raw_spin_unlock(&log_buf.lock);
        if (wake)
                up(&console_sem);
        return retval;
@@ -1494,124 +2321,11 @@ 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 task_struct *owner;      /* task of first print*/
-       u64 ts_nsec;                    /* time of first print */
-       u8 level;                       /* log level of first message */
-       u8 facility;                    /* log level of first message */
-       enum log_flags flags;           /* prefix, newline flags */
-       bool flushed:1;                 /* buffer sealed and committed */
-       int cpu;
-} cont;
-
-static void cont_flush(enum log_flags flags)
-{
-       if (cont.flushed)
-               return;
-       if (cont.len == 0)
-               return;
-
-       if (cont.cons) {
-               /*
-                * 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.cpu);
-               cont.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.cpu);
-               cont.len = 0;
-       }
-}
-
-static bool cont_add(int facility, int level, const char *text, size_t len)
-{
-       if (cont.len && cont.flushed)
-               return false;
-
-       if (cont.len + len > sizeof(cont.buf)) {
-               /* the line gets too long, split it up in separate records */
-               cont_flush(LOG_CONT);
-               return false;
-       }
-
-       if (!cont.len) {
-               cont.facility = facility;
-               cont.level = level;
-               cont.owner = current;
-               cont.ts_nsec = local_clock();
-               cont.flags = 0;
-               cont.cons = 0;
-               cont.flushed = false;
-       }
-
-       memcpy(cont.buf + cont.len, text, len);
-       cont.len += len;
-
-       if (cont.len > (sizeof(cont.buf) * 80) / 100)
-               cont_flush(LOG_CONT);
-
-       return true;
-}
-
-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);
-#ifdef CONFIG_PRINTK_PROCESS
-               *(text+textlen) = ' ';
-               textlen += print_process(NULL, NULL);
-#endif
-               size -= textlen;
-       }
-
-       len = cont.len - cont.cons;
-       if (len > 0) {
-               if (len+1 > size)
-                       len = size-1;
-               memcpy(text + textlen, cont.buf + cont.cons, len);
-               textlen += len;
-               cont.cons = cont.len;
-       }
-
-       if (cont.flushed) {
-               if (cont.flags & LOG_NEWLINE)
-                       text[textlen++] = '\n';
-               /* got everything, release buffer */
-               cont.len = 0;
-       }
-       return textlen;
-}
-
 asmlinkage int vprintk_emit(int facility, int level,
                            const char *dict, size_t dictlen,
                            const char *fmt, va_list args)
 {
        static int recursion_bug;
-       static char textbuf[LOG_LINE_MAX];
-       char *text = textbuf;
-       size_t text_len;
-       enum log_flags lflags = 0;
        unsigned long flags;
        int this_cpu;
        int printed_len = 0;
@@ -1642,7 +2356,7 @@ asmlinkage int vprintk_emit(int facility, int level,
        }
 
        lockdep_off();
-       raw_spin_lock(&logbuf_lock);
+       raw_spin_lock(&log_buf.lock);
        logbuf_cpu = this_cpu;
 
        if (recursion_bug) {
@@ -1650,93 +2364,21 @@ asmlinkage int vprintk_emit(int facility, int level,
                        "BUG: recent printk recursion!";
 
                recursion_bug = 0;
-               printed_len += strlen(recursion_msg);
                /* emit KERN_CRIT message */
-               log_store(0, 2, LOG_PREFIX|LOG_NEWLINE, 0,
-                         NULL, 0, recursion_msg, printed_len, logbuf_cpu);
-       }
-
-       /*
-        * 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);
-
-#ifdef CONFIG_DEBUG_LL
-       printascii(text);
-#endif
-
-       /* 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);
+               printed_len += log_store(&log_buf, 0, 2, LOG_PREFIX|LOG_NEWLINE, 0,
+                       NULL, 0, recursion_msg, strlen(recursion_msg), this_cpu);
 
-               if (kern_level) {
-                       const char *end_of_header = printk_skip_level(text);
-                       switch (kern_level) {
-                       case '0' ... '7':
-                               if (level == -1)
-                                       level = kern_level - '0';
-                       case 'd':       /* KERN_DEFAULT */
-                               lflags |= LOG_PREFIX;
-                       case 'c':       /* KERN_CONT */
-                               break;
-                       }
-                       text_len -= end_of_header - text;
-                       text = (char *)end_of_header;
-               }
        }
 
-       if (level == -1)
-               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))
-                       log_store(facility, level, lflags | LOG_CONT, 0,
-                                 dict, dictlen, text, text_len, logbuf_cpu);
-       } 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 with interrupts (prefix == true) then just
-                * flush it out and store this line separately.
-                */
-               if (cont.len && cont.owner == current) {
-                       if (!(lflags & LOG_PREFIX))
-                               stored = cont_add(facility, level, text, text_len);
-                       cont_flush(LOG_NEWLINE);
-               }
-
-               if (!stored)
-                       log_store(facility, level, lflags, 0,
-                                 dict, dictlen, text, text_len, logbuf_cpu);
-       }
-       printed_len += text_len;
+       printed_len += log_format_and_store(&log_buf, facility, level, dict, dictlen,
+                                           fmt, this_cpu, args);
 
        /*
         * Try to acquire and then immediately release the console semaphore.
         * The release will print out buffers and wake up /dev/kmsg and syslog()
         * users.
         *
-        * The console_trylock_for_printk() function will release 'logbuf_lock'
+        * The console_trylock_for_printk() function will release 'log_buf.lock'
         * regardless of whether it actually gets the console semaphore or not.
         */
        if (console_trylock_for_printk(this_cpu))
@@ -1817,15 +2459,19 @@ EXPORT_SYMBOL(printk);
 
 #define LOG_LINE_MAX           0
 #define PREFIX_MAX             0
-#define LOG_LINE_MAX 0
+
+static struct log_buffer log_buf = {
+       .lock           = __RAW_SPIN_LOCK_UNLOCKED(log_buf.lock),
+       .first_seq      = 0,
+       .first_idx      = 0,
+       .next_seq       = 0,
+};
+
 static u64 syslog_seq;
 static u32 syslog_idx;
 static u64 console_seq;
 static u32 console_idx;
 static enum log_flags syslog_prev;
-static u64 log_first_seq;
-static u32 log_first_idx;
-static u64 log_next_seq;
 static enum log_flags console_prev;
 static struct cont {
        size_t len;
@@ -1833,8 +2479,8 @@ static struct cont {
        u8 level;
        bool flushed:1;
 } cont;
-static struct log *log_from_idx(u32 idx) { return NULL; }
-static u32 log_next(u32 idx) { return 0; }
+static struct log *log_from_idx(struct log_buffer *log_b, u32 idx) { return NULL; }
+static u32 log_next(struct log_buffer *log_b, u32 idx) { return 0; }
 static void call_console_drivers(int level, const char *text, size_t len) {}
 static size_t msg_print_text(const struct log *msg, enum log_flags prev,
                             bool syslog, char *buf, size_t size) { return 0; }
@@ -2103,7 +2749,7 @@ static void console_cont_flush(char *text, size_t size)
        unsigned long flags;
        size_t len;
 
-       raw_spin_lock_irqsave(&logbuf_lock, flags);
+       raw_spin_lock_irqsave(&log_buf.lock, flags);
 
        if (!cont.len)
                goto out;
@@ -2113,18 +2759,18 @@ 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_buf.next_seq && !cont.cons)
                goto out;
 
        len = cont_print_text(text, size);
-       raw_spin_unlock(&logbuf_lock);
+       raw_spin_unlock(&log_buf.lock);
        stop_critical_timings();
        call_console_drivers(cont.level, text, len);
        start_critical_timings();
        local_irq_restore(flags);
        return;
 out:
-       raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+       raw_spin_unlock_irqrestore(&log_buf.lock, flags);
 }
 
 /**
@@ -2164,29 +2810,29 @@ again:
                size_t len;
                int level;
 
-               raw_spin_lock_irqsave(&logbuf_lock, flags);
-               if (seen_seq != log_next_seq) {
+               raw_spin_lock_irqsave(&log_buf.lock, flags);
+               if (seen_seq != log_buf.next_seq) {
                        wake_klogd = true;
-                       seen_seq = log_next_seq;
+                       seen_seq = log_buf.next_seq;
                }
 
-               if (console_seq < log_first_seq) {
+               if (console_seq < log_buf.first_seq) {
                        /* messages are gone, move to first one */
-                       console_seq = log_first_seq;
-                       console_idx = log_first_idx;
+                       console_seq = log_buf.first_seq;
+                       console_idx = log_buf.first_idx;
                        console_prev = 0;
                }
 skip:
-               if (console_seq == log_next_seq)
+               if (console_seq == log_buf.next_seq)
                        break;
 
-               msg = log_from_idx(console_idx);
+               msg = log_from_idx(&log_buf, console_idx);
                if (msg->flags & LOG_NOCONS) {
                        /*
                         * Skip record we have buffered and already printed
                         * directly to the console when we received it.
                         */
-                       console_idx = log_next(console_idx);
+                       console_idx = log_next(&log_buf, console_idx);
                        console_seq++;
                        /*
                         * We will get here again when we register a new
@@ -2201,10 +2847,10 @@ skip:
                level = msg->level;
                len = msg_print_text(msg, console_prev, false,
                                     text, sizeof(text));
-               console_idx = log_next(console_idx);
+               console_idx = log_next(&log_buf, console_idx);
                console_seq++;
                console_prev = msg->flags;
-               raw_spin_unlock(&logbuf_lock);
+               raw_spin_unlock(&log_buf.lock);
 
                stop_critical_timings();        /* don't trace print latency */
                call_console_drivers(level, text, len);
@@ -2218,7 +2864,7 @@ skip:
        if (unlikely(exclusive_console))
                exclusive_console = NULL;
 
-       raw_spin_unlock(&logbuf_lock);
+       raw_spin_unlock(&log_buf.lock);
 
        up(&console_sem);
 
@@ -2228,9 +2874,9 @@ skip:
         * there's a new owner and the console_unlock() from them will do the
         * flush, no worries.
         */
-       raw_spin_lock(&logbuf_lock);
-       retry = console_seq != log_next_seq;
-       raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+       raw_spin_lock(&log_buf.lock);
+       retry = console_seq != log_buf.next_seq;
+       raw_spin_unlock_irqrestore(&log_buf.lock, flags);
 
        if (retry && console_trylock())
                goto again;
@@ -2464,11 +3110,11 @@ void register_console(struct console *newcon)
                 * console_unlock(); will print out the buffered messages
                 * for us.
                 */
-               raw_spin_lock_irqsave(&logbuf_lock, flags);
+               raw_spin_lock_irqsave(&log_buf.lock, flags);
                console_seq = syslog_seq;
                console_idx = syslog_idx;
                console_prev = syslog_prev;
-               raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+               raw_spin_unlock_irqrestore(&log_buf.lock, flags);
                /*
                 * We're about to replay the log buffer.  Only do this to the
                 * just-registered console to avoid excessive message spam to
@@ -2581,7 +3227,7 @@ static void wake_up_klogd_work_func(struct irq_work *irq_work)
        }
 
        if (pending & PRINTK_PENDING_WAKEUP)
-               wake_up_interruptible(&log_wait);
+               wake_up_interruptible(&log_buf.wait);
 }
 
 static DEFINE_PER_CPU(struct irq_work, wake_up_klogd_work) = {
@@ -2592,7 +3238,7 @@ static DEFINE_PER_CPU(struct irq_work, wake_up_klogd_work) = {
 void wake_up_klogd(void)
 {
        preempt_disable();
-       if (waitqueue_active(&log_wait)) {
+       if (waitqueue_active(&log_buf.wait)) {
                this_cpu_or(printk_pending, PRINTK_PENDING_WAKEUP);
                irq_work_queue(&__get_cpu_var(wake_up_klogd_work));
        }
@@ -2742,12 +3388,12 @@ void kmsg_dump(enum kmsg_dump_reason reason)
                /* initialize iterator with data about the stored records */
                dumper->active = true;
 
-               raw_spin_lock_irqsave(&logbuf_lock, flags);
-               dumper->cur_seq = clear_seq;
-               dumper->cur_idx = clear_idx;
-               dumper->next_seq = log_next_seq;
-               dumper->next_idx = log_next_idx;
-               raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+               raw_spin_lock_irqsave(&log_buf.lock, flags);
+               dumper->cur_seq = log_buf.clear_seq;
+               dumper->cur_idx = log_buf.clear_idx;
+               dumper->next_seq = log_buf.next_seq;
+               dumper->next_idx = log_buf.next_idx;
+               raw_spin_unlock_irqrestore(&log_buf.lock, flags);
 
                /* invoke dumper which will iterate over records */
                dumper->dump(dumper, reason);
@@ -2787,20 +3433,20 @@ bool kmsg_dump_get_line_nolock(struct kmsg_dumper *dumper, bool syslog,
        if (!dumper->active)
                goto out;
 
-       if (dumper->cur_seq < log_first_seq) {
+       if (dumper->cur_seq < log_buf.first_seq) {
                /* messages are gone, move to first available one */
-               dumper->cur_seq = log_first_seq;
-               dumper->cur_idx = log_first_idx;
+               dumper->cur_seq = log_buf.first_seq;
+               dumper->cur_idx = log_buf.first_idx;
        }
 
        /* last entry */
-       if (dumper->cur_seq >= log_next_seq)
+       if (dumper->cur_seq >= log_buf.next_seq)
                goto out;
 
-       msg = log_from_idx(dumper->cur_idx);
+       msg = log_from_idx(&log_buf, dumper->cur_idx);
        l = msg_print_text(msg, 0, syslog, line, size);
 
-       dumper->cur_idx = log_next(dumper->cur_idx);
+       dumper->cur_idx = log_next(&log_buf, dumper->cur_idx);
        dumper->cur_seq++;
        ret = true;
 out:
@@ -2832,9 +3478,9 @@ bool kmsg_dump_get_line(struct kmsg_dumper *dumper, bool syslog,
        unsigned long flags;
        bool ret;
 
-       raw_spin_lock_irqsave(&logbuf_lock, flags);
+       raw_spin_lock_irqsave(&log_buf.lock, flags);
        ret = kmsg_dump_get_line_nolock(dumper, syslog, line, size, len);
-       raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+       raw_spin_unlock_irqrestore(&log_buf.lock, flags);
 
        return ret;
 }
@@ -2874,16 +3520,16 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
        if (!dumper->active)
                goto out;
 
-       raw_spin_lock_irqsave(&logbuf_lock, flags);
-       if (dumper->cur_seq < log_first_seq) {
+       raw_spin_lock_irqsave(&log_buf.lock, flags);
+       if (dumper->cur_seq < log_buf.first_seq) {
                /* messages are gone, move to first available one */
-               dumper->cur_seq = log_first_seq;
-               dumper->cur_idx = log_first_idx;
+               dumper->cur_seq = log_buf.first_seq;
+               dumper->cur_idx = log_buf.first_idx;
        }
 
        /* last entry */
        if (dumper->cur_seq >= dumper->next_seq) {
-               raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+               raw_spin_unlock_irqrestore(&log_buf.lock, flags);
                goto out;
        }
 
@@ -2892,10 +3538,10 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
        idx = dumper->cur_idx;
        prev = 0;
        while (seq < dumper->next_seq) {
-               struct log *msg = log_from_idx(idx);
+               struct log *msg = log_from_idx(&log_buf, idx);
 
                l += msg_print_text(msg, prev, true, NULL, 0);
-               idx = log_next(idx);
+               idx = log_next(&log_buf, idx);
                seq++;
                prev = msg->flags;
        }
@@ -2905,10 +3551,10 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
        idx = dumper->cur_idx;
        prev = 0;
        while (l > size && seq < dumper->next_seq) {
-               struct log *msg = log_from_idx(idx);
+               struct log *msg = log_from_idx(&log_buf, idx);
 
                l -= msg_print_text(msg, prev, true, NULL, 0);
-               idx = log_next(idx);
+               idx = log_next(&log_buf, idx);
                seq++;
                prev = msg->flags;
        }
@@ -2920,10 +3566,10 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
        l = 0;
        prev = 0;
        while (seq < dumper->next_seq) {
-               struct log *msg = log_from_idx(idx);
+               struct log *msg = log_from_idx(&log_buf, idx);
 
                l += msg_print_text(msg, prev, syslog, buf + l, size - l);
-               idx = log_next(idx);
+               idx = log_next(&log_buf, idx);
                seq++;
                prev = msg->flags;
        }
@@ -2931,7 +3577,7 @@ bool kmsg_dump_get_buffer(struct kmsg_dumper *dumper, bool syslog,
        dumper->next_seq = next_seq;
        dumper->next_idx = next_idx;
        ret = true;
-       raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+       raw_spin_unlock_irqrestore(&log_buf.lock, flags);
 out:
        if (len)
                *len = l;
@@ -2951,10 +3597,10 @@ EXPORT_SYMBOL_GPL(kmsg_dump_get_buffer);
  */
 void kmsg_dump_rewind_nolock(struct kmsg_dumper *dumper)
 {
-       dumper->cur_seq = clear_seq;
-       dumper->cur_idx = clear_idx;
-       dumper->next_seq = log_next_seq;
-       dumper->next_idx = log_next_idx;
+       dumper->cur_seq = log_buf.clear_seq;
+       dumper->cur_idx = log_buf.clear_idx;
+       dumper->next_seq = log_buf.next_seq;
+       dumper->next_idx = log_buf.next_idx;
 }
 
 /**
@@ -2969,9 +3615,9 @@ void kmsg_dump_rewind(struct kmsg_dumper *dumper)
 {
        unsigned long flags;
 
-       raw_spin_lock_irqsave(&logbuf_lock, flags);
+       raw_spin_lock_irqsave(&log_buf.lock, flags);
        kmsg_dump_rewind_nolock(dumper);
-       raw_spin_unlock_irqrestore(&logbuf_lock, flags);
+       raw_spin_unlock_irqrestore(&log_buf.lock, flags);
 }
 EXPORT_SYMBOL_GPL(kmsg_dump_rewind);