Containerized syslog working properly
[safe/jmp/linux-2.6] / kernel / printk.c
index b51b156..7f60d8a 100644 (file)
@@ -13,7 +13,7 @@
  * Fixed SMP synchronization, 08/08/99, Manfred Spraul
  *     manfred@colorfullife.com
  * Rewrote bits to get rid of console_lock
- *     01Mar01 Andrew Morton <andrewm@uow.edu.au>
+ *     01Mar01 Andrew Morton
  */
 
 #include <linux/kernel.h>
 #include <linux/security.h>
 #include <linux/bootmem.h>
 #include <linux/syscalls.h>
+#include <linux/kexec.h>
+#include <linux/ratelimit.h>
+#include <linux/kmsg_dump.h>
+#include <linux/syslog.h>
 
 #include <asm/uaccess.h>
 
 /*
+ * for_each_console() allows you to iterate on each console
+ */
+#define for_each_console(con) \
+       for (con = console_drivers; con != NULL; con = con->next)
+
+/*
  * Architectures can override it:
  */
 void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...)
 {
 }
 
-#define __LOG_BUF_LEN  (1 << CONFIG_LOG_BUF_SHIFT)
-
 /* printk's without a loglevel use this.. */
 #define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */
 
@@ -73,7 +81,6 @@ EXPORT_SYMBOL(oops_in_progress);
  * driver system.
  */
 static DECLARE_MUTEX(console_sem);
-static DECLARE_MUTEX(secondary_console_sem);
 struct console *console_drivers;
 EXPORT_SYMBOL_GPL(console_drivers);
 
@@ -87,23 +94,19 @@ EXPORT_SYMBOL_GPL(console_drivers);
  */
 static int console_locked, console_suspended;
 
-/*
- * logbuf_lock protects log_buf, log_start, log_end, con_start and logged_chars
- * It is also used in interesting ways to provide interlocking in
- * release_console_sem().
- */
-static DEFINE_SPINLOCK(logbuf_lock);
-
-#define LOG_BUF_MASK (log_buf_len-1)
-#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])
+#define LOG_BUF_MASK(ns) ((ns)->buf_len-1)
+#define LOG_BUF(ns, idx) ((ns)->buf[(idx) & LOG_BUF_MASK(ns)])
 
 /*
- * The indices into log_buf are not constrained to log_buf_len - they
- * must be masked before subscripting
+ * To access container syslog ring buffer
  */
-static unsigned log_start;     /* Index into log_buf: next char to be read by syslog() */
-static unsigned con_start;     /* Index into log_buf: next char to be sent to consoles */
-static unsigned log_end;       /* Index into log_buf: most-recently-written-char + 1 */
+#define sys_log_lock (syslog_ns->logbuf_lock)
+#define sys_log_start (syslog_ns->log_start)
+#define sys_log_end (syslog_ns->log_end)
+#define sys_log_con_start (syslog_ns->con_start)
+#define sys_log_buf_len (syslog_ns->buf_len)
+#define sys_log_logged_chars (syslog_ns->logged_chars)
+#define sys_log_buf (syslog_ns->buf)
 
 /*
  *     Array of consoles built from command line options (console=)
@@ -131,47 +134,36 @@ static int console_may_schedule;
 
 #ifdef CONFIG_PRINTK
 
-static char __log_buf[__LOG_BUF_LEN];
-static char *log_buf = __log_buf;
-static int log_buf_len = __LOG_BUF_LEN;
-static unsigned logged_chars; /* Number of chars produced since last read+clear operation */
+static int saved_console_loglevel = -1;
+
+#ifdef CONFIG_KEXEC
+/*
+ * This appends the listed symbols to /proc/vmcoreinfo
+ *
+ * /proc/vmcoreinfo is used by various utiilties, like crash and makedumpfile to
+ * obtain access to symbols that are otherwise very difficult to locate.  These
+ * symbols are specifically used so that utilities can access and extract the
+ * dmesg log from a vmcore file after a crash.
+ */
+void log_buf_kexec_setup(void)
+{
+       struct syslog_ns *syslog_ns = current_syslog_ns();
+
+       VMCOREINFO_SYMBOL(sys_log_buf);
+       VMCOREINFO_SYMBOL(sys_log_end);
+       VMCOREINFO_SYMBOL(sys_log_buf_len);
+       VMCOREINFO_SYMBOL(sys_log_logged_chars);
+}
+#endif
 
 static int __init log_buf_len_setup(char *str)
 {
        unsigned size = memparse(str, &str);
-       unsigned long flags;
 
-       if (size)
+       if (size) {
                size = roundup_pow_of_two(size);
-       if (size > log_buf_len) {
-               unsigned start, dest_idx, offset;
-               char *new_log_buf;
-
-               new_log_buf = alloc_bootmem(size);
-               if (!new_log_buf) {
-                       printk(KERN_WARNING "log_buf_len: allocation failed\n");
-                       goto out;
-               }
-
-               spin_lock_irqsave(&logbuf_lock, flags);
-               log_buf_len = size;
-               log_buf = new_log_buf;
-
-               offset = start = min(con_start, log_start);
-               dest_idx = 0;
-               while (start != log_end) {
-                       log_buf[dest_idx] = __log_buf[start & (__LOG_BUF_LEN - 1)];
-                       start++;
-                       dest_idx++;
-               }
-               log_start -= offset;
-               con_start -= offset;
-               log_end -= offset;
-               spin_unlock_irqrestore(&logbuf_lock, flags);
-
-               printk(KERN_NOTICE "log_buf_len: %d\n", log_buf_len);
+               resize_syslog_ns(&init_syslog_ns, size);
        }
-out:
        return 1;
 }
 
@@ -180,12 +172,11 @@ __setup("log_buf_len=", log_buf_len_setup);
 #ifdef CONFIG_BOOT_PRINTK_DELAY
 
 static unsigned int boot_delay; /* msecs delay after each printk during bootup */
-static unsigned long long printk_delay_msec; /* per msec, based on boot_delay */
+static unsigned long long loops_per_msec;      /* based on boot_delay */
 
 static int __init boot_delay_setup(char *str)
 {
        unsigned long lpj;
-       unsigned long long loops_per_msec;
 
        lpj = preset_lpj ? preset_lpj : 1000000;        /* some guess */
        loops_per_msec = (unsigned long long)lpj / 1000 * HZ;
@@ -194,10 +185,9 @@ static int __init boot_delay_setup(char *str)
        if (boot_delay > 10 * 1000)
                boot_delay = 0;
 
-       printk_delay_msec = loops_per_msec;
-       printk(KERN_DEBUG "boot_delay: %u, preset_lpj: %ld, lpj: %lu, "
-               "HZ: %d, printk_delay_msec: %llu\n",
-               boot_delay, preset_lpj, lpj, HZ, printk_delay_msec);
+       pr_debug("boot_delay: %u, preset_lpj: %ld, lpj: %lu, "
+               "HZ: %d, loops_per_msec: %llu\n",
+               boot_delay, preset_lpj, lpj, HZ, loops_per_msec);
        return 1;
 }
 __setup("boot_delay=", boot_delay_setup);
@@ -210,7 +200,7 @@ static void boot_delay_msec(void)
        if (boot_delay == 0 || system_state != SYSTEM_BOOTING)
                return;
 
-       k = (unsigned long long)printk_delay_msec * boot_delay;
+       k = (unsigned long long)loops_per_msec * boot_delay;
 
        timeout = jiffies + msecs_to_jiffies(boot_delay);
        while (k) {
@@ -232,77 +222,24 @@ static inline void boot_delay_msec(void)
 }
 #endif
 
-/*
- * Return the number of unread characters in the log buffer.
- */
-static int log_buf_get_len(void)
-{
-       return logged_chars;
-}
-
-/*
- * Copy a range of characters from the log buffer.
- */
-int log_buf_copy(char *dest, int idx, int len)
-{
-       int ret, max;
-       bool took_lock = false;
-
-       if (!oops_in_progress) {
-               spin_lock_irq(&logbuf_lock);
-               took_lock = true;
-       }
-
-       max = log_buf_get_len();
-       if (idx < 0 || idx >= max) {
-               ret = -1;
-       } else {
-               if (len > max)
-                       len = max;
-               ret = len;
-               idx += (log_end - max);
-               while (len-- > 0)
-                       dest[len] = LOG_BUF(idx + len);
-       }
-
-       if (took_lock)
-               spin_unlock_irq(&logbuf_lock);
-
-       return ret;
-}
-
-/*
- * Commands to do_syslog:
- *
- *     0 -- Close the log.  Currently a NOP.
- *     1 -- Open the log. Currently a NOP.
- *     2 -- Read from the log.
- *     3 -- Read all messages remaining in the ring buffer.
- *     4 -- Read and clear all messages remaining in the ring buffer
- *     5 -- Clear ring buffer.
- *     6 -- Disable printk's to console
- *     7 -- Enable printk's to console
- *     8 -- Set level of messages printed to console
- *     9 -- Return number of unread characters in the log buffer
- *     10 -- Return size of the log buffer
- */
-int do_syslog(int type, char __user *buf, int len)
+int do_syslog(int type, char __user *buf, int len, bool from_file)
 {
        unsigned i, j, limit, count;
        int do_clear = 0;
        char c;
        int error = 0;
+       struct syslog_ns *syslog_ns = current_syslog_ns();
 
-       error = security_syslog(type);
+       error = security_syslog(type, from_file);
        if (error)
                return error;
 
        switch (type) {
-       case 0:         /* Close log */
+       case SYSLOG_ACTION_CLOSE:       /* Close log */
                break;
-       case 1:         /* Open log */
+       case SYSLOG_ACTION_OPEN:        /* Open log */
                break;
-       case 2:         /* Read from log */
+       case SYSLOG_ACTION_READ:        /* Read from log */
                error = -EINVAL;
                if (!buf || len < 0)
                        goto out;
@@ -314,29 +251,31 @@ int do_syslog(int type, char __user *buf, int len)
                        goto out;
                }
                error = wait_event_interruptible(log_wait,
-                                                       (log_start - log_end));
+                                       (sys_log_start - sys_log_end));
                if (error)
                        goto out;
                i = 0;
-               spin_lock_irq(&logbuf_lock);
-               while (!error && (log_start != log_end) && i < len) {
-                       c = LOG_BUF(log_start);
-                       log_start++;
-                       spin_unlock_irq(&logbuf_lock);
+               spin_lock_irq(&sys_log_lock);
+               while (!error && (sys_log_start != sys_log_end) && i < len) {
+                       c = LOG_BUF(syslog_ns, sys_log_start);
+                       sys_log_start++;
+                       spin_unlock_irq(&sys_log_lock);
                        error = __put_user(c,buf);
                        buf++;
                        i++;
                        cond_resched();
-                       spin_lock_irq(&logbuf_lock);
+                       spin_lock_irq(&sys_log_lock);
                }
-               spin_unlock_irq(&logbuf_lock);
+               spin_unlock_irq(&sys_log_lock);
                if (!error)
                        error = i;
                break;
-       case 4:         /* Read/clear last kernel messages */
+       /* Read/clear last kernel messages */
+       case SYSLOG_ACTION_READ_CLEAR:
                do_clear = 1;
                /* FALL THRU */
-       case 3:         /* Read last kernel messages */
+       /* Read last kernel messages */
+       case SYSLOG_ACTION_READ_ALL:
                error = -EINVAL;
                if (!buf || len < 0)
                        goto out;
@@ -348,14 +287,14 @@ int do_syslog(int type, char __user *buf, int len)
                        goto out;
                }
                count = len;
-               if (count > log_buf_len)
-                       count = log_buf_len;
-               spin_lock_irq(&logbuf_lock);
-               if (count > logged_chars)
-                       count = logged_chars;
+               if (count > sys_log_buf_len)
+                       count = sys_log_buf_len;
+               spin_lock_irq(&sys_log_lock);
+               if (count > sys_log_logged_chars)
+                       count = sys_log_logged_chars;
                if (do_clear)
-                       logged_chars = 0;
-               limit = log_end;
+                       sys_log_logged_chars = 0;
+               limit = sys_log_end;
                /*
                 * __put_user() could sleep, and while we sleep
                 * printk() could overwrite the messages
@@ -364,15 +303,15 @@ int do_syslog(int type, char __user *buf, int len)
                 */
                for (i = 0; i < count && !error; i++) {
                        j = limit-1-i;
-                       if (j + log_buf_len < log_end)
+                       if (j + sys_log_buf_len < sys_log_end)
                                break;
-                       c = LOG_BUF(j);
-                       spin_unlock_irq(&logbuf_lock);
+                       c = LOG_BUF(syslog_ns, j);
+                       spin_unlock_irq(&sys_log_lock);
                        error = __put_user(c,&buf[count-1-i]);
                        cond_resched();
-                       spin_lock_irq(&logbuf_lock);
+                       spin_lock_irq(&sys_log_lock);
                }
-               spin_unlock_irq(&logbuf_lock);
+               spin_unlock_irq(&sys_log_lock);
                if (error)
                        break;
                error = i;
@@ -389,29 +328,42 @@ int do_syslog(int type, char __user *buf, int len)
                        }
                }
                break;
-       case 5:         /* Clear ring buffer */
-               logged_chars = 0;
+       /* Clear ring buffer */
+       case SYSLOG_ACTION_CLEAR:
+               sys_log_logged_chars = 0;
                break;
-       case 6:         /* Disable logging to console */
+       /* Disable logging to console */
+       case SYSLOG_ACTION_CONSOLE_OFF:
+               if (saved_console_loglevel == -1)
+                       saved_console_loglevel = console_loglevel;
                console_loglevel = minimum_console_loglevel;
                break;
-       case 7:         /* Enable logging to console */
-               console_loglevel = default_console_loglevel;
+       /* Enable logging to console */
+       case SYSLOG_ACTION_CONSOLE_ON:
+               if (saved_console_loglevel != -1) {
+                       console_loglevel = saved_console_loglevel;
+                       saved_console_loglevel = -1;
+               }
                break;
-       case 8:         /* Set level of messages printed to console */
+       /* Set level of messages printed to console */
+       case SYSLOG_ACTION_CONSOLE_LEVEL:
                error = -EINVAL;
                if (len < 1 || len > 8)
                        goto out;
                if (len < minimum_console_loglevel)
                        len = minimum_console_loglevel;
                console_loglevel = len;
+               /* Implicitly re-enable logging to console */
+               saved_console_loglevel = -1;
                error = 0;
                break;
-       case 9:         /* Number of chars in the log buffer */
-               error = log_end - log_start;
+       /* Number of chars in the log buffer */
+       case SYSLOG_ACTION_SIZE_UNREAD:
+               error = sys_log_end - sys_log_start;
                break;
-       case 10:        /* Size of the log buffer */
-               error = log_buf_len;
+       /* Size of the log buffer */
+       case SYSLOG_ACTION_SIZE_BUFFER:
+               error = sys_log_buf_len;
                break;
        default:
                error = -EINVAL;
@@ -421,23 +373,25 @@ out:
        return error;
 }
 
-asmlinkage long sys_syslog(int type, char __user *buf, int len)
+SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
 {
-       return do_syslog(type, buf, len);
+       return do_syslog(type, buf, len, SYSLOG_FROM_CALL);
 }
 
 /*
  * Call the console drivers on a range of log_buf
  */
-static void __call_console_drivers(unsigned start, unsigned end)
+static void __call_console_drivers(struct syslog_ns *syslog_ns,
+                               unsigned start, unsigned end)
 {
        struct console *con;
 
-       for (con = console_drivers; con; con = con->next) {
+       for_each_console(con) {
                if ((con->flags & CON_ENABLED) && con->write &&
                                (cpu_online(smp_processor_id()) ||
                                (con->flags & CON_ANYTIME)))
-                       con->write(con, &LOG_BUF(start), end - start);
+                       con->write(con, &LOG_BUF(syslog_ns, start),
+                               end - start);
        }
 }
 
@@ -456,18 +410,21 @@ early_param("ignore_loglevel", ignore_loglevel_setup);
 /*
  * Write out chars from start to end - 1 inclusive
  */
-static void _call_console_drivers(unsigned start,
-                               unsigned end, int msg_log_level)
+static void _call_console_drivers(struct syslog_ns *syslog_ns, unsigned start,
+                                 unsigned end, int msg_log_level)
 {
        if ((msg_log_level < console_loglevel || ignore_loglevel) &&
                        console_drivers && start != end) {
-               if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {
+               if ((start & LOG_BUF_MASK(syslog_ns)) >
+                       (end & LOG_BUF_MASK(syslog_ns))) {
                        /* wrapped write */
-                       __call_console_drivers(start & LOG_BUF_MASK,
-                                               log_buf_len);
-                       __call_console_drivers(0, end & LOG_BUF_MASK);
+                       __call_console_drivers(syslog_ns,
+                                       start & LOG_BUF_MASK(syslog_ns),
+                                       sys_log_buf_len);
+                       __call_console_drivers(syslog_ns, 0,
+                                       end & LOG_BUF_MASK(syslog_ns));
                } else {
-                       __call_console_drivers(start, end);
+                       __call_console_drivers(syslog_ns, start, end);
                }
        }
 }
@@ -477,7 +434,8 @@ static void _call_console_drivers(unsigned start,
  * log_buf[start] to log_buf[end - 1].
  * The console_sem must be held.
  */
-static void call_console_drivers(unsigned start, unsigned end)
+static void call_console_drivers(struct syslog_ns *syslog_ns,
+                               unsigned start, unsigned end)
 {
        unsigned cur_index, start_print;
        static int msg_level = -1;
@@ -488,16 +446,16 @@ static void call_console_drivers(unsigned start, unsigned end)
        start_print = start;
        while (cur_index != end) {
                if (msg_level < 0 && ((end - cur_index) > 2) &&
-                               LOG_BUF(cur_index + 0) == '<' &&
-                               LOG_BUF(cur_index + 1) >= '0' &&
-                               LOG_BUF(cur_index + 1) <= '7' &&
-                               LOG_BUF(cur_index + 2) == '>') {
-                       msg_level = LOG_BUF(cur_index + 1) - '0';
+                               LOG_BUF(syslog_ns, cur_index + 0) == '<' &&
+                               LOG_BUF(syslog_ns, cur_index + 1) >= '0' &&
+                               LOG_BUF(syslog_ns, cur_index + 1) <= '7' &&
+                               LOG_BUF(syslog_ns, cur_index + 2) == '>') {
+                       msg_level = LOG_BUF(syslog_ns, cur_index + 1) - '0';
                        cur_index += 3;
                        start_print = cur_index;
                }
                while (cur_index != end) {
-                       char c = LOG_BUF(cur_index);
+                       char c = LOG_BUF(syslog_ns, cur_index);
 
                        cur_index++;
                        if (c == '\n') {
@@ -510,26 +468,27 @@ static void call_console_drivers(unsigned start, unsigned end)
                                         */
                                        msg_level = default_message_loglevel;
                                }
-                               _call_console_drivers(start_print, cur_index, msg_level);
+                               _call_console_drivers(syslog_ns,
+                                       start_print, cur_index, msg_level);
                                msg_level = -1;
                                start_print = cur_index;
                                break;
                        }
                }
        }
-       _call_console_drivers(start_print, end, msg_level);
+       _call_console_drivers(syslog_ns, start_print, end, msg_level);
 }
 
-static void emit_log_char(char c)
+static void emit_log_char(struct syslog_ns *syslog_ns, char c)
 {
-       LOG_BUF(log_end) = c;
-       log_end++;
-       if (log_end - log_start > log_buf_len)
-               log_start = log_end - log_buf_len;
-       if (log_end - con_start > log_buf_len)
-               con_start = log_end - log_buf_len;
-       if (logged_chars < log_buf_len)
-               logged_chars++;
+       LOG_BUF(syslog_ns, sys_log_end) = c;
+       sys_log_end++;
+       if (sys_log_end - sys_log_start > sys_log_buf_len)
+               sys_log_start = sys_log_end - sys_log_buf_len;
+       if (sys_log_end - sys_log_con_start > sys_log_buf_len)
+               sys_log_con_start = sys_log_end - sys_log_buf_len;
+       if (sys_log_logged_chars < sys_log_buf_len)
+               sys_log_logged_chars++;
 }
 
 /*
@@ -537,7 +496,7 @@ static void emit_log_char(char c)
  * every 10 seconds, to leave time for slow consoles to print a
  * full oops.
  */
-static void zap_locks(void)
+static void zap_locks(struct syslog_ns *syslog_ns)
 {
        static unsigned long oops_timestamp;
 
@@ -548,7 +507,7 @@ static void zap_locks(void)
        oops_timestamp = jiffies;
 
        /* If a crash is occurring, make sure we can't deadlock */
-       spin_lock_init(&logbuf_lock);
+       spin_lock_init(&sys_log_lock);
        /* And make sure that we print immediately */
        init_MUTEX(&console_sem);
 }
@@ -565,7 +524,7 @@ static int have_callable_console(void)
 {
        struct console *con;
 
-       for (con = console_drivers; con; con = con->next)
+       for_each_console(con)
                if (con->flags & CON_ANYTIME)
                        return 1;
 
@@ -577,9 +536,6 @@ static int have_callable_console(void)
  * @fmt: format string
  *
  * This is printk().  It can be called from any context.  We want it to work.
- * Be aware of the fact that if oops_in_progress is not set, we might try to
- * wake klogd up which could deadlock on runqueue lock if printk() is called
- * from scheduler code.
  *
  * We try to grab the console_sem.  If we succeed, it's easy - we log the output and
  * call the console drivers.  If we fail to get the semaphore we place the output
@@ -593,6 +549,8 @@ static int have_callable_console(void)
  *
  * See also:
  * printf(3)
+ *
+ * See the vsnprintf() documentation for format string extensions over C99.
  */
 
 asmlinkage int printk(const char *fmt, ...)
@@ -633,7 +591,8 @@ static inline int can_use_console(unsigned int cpu)
  * interrupts disabled. It should return with 'lockbuf_lock'
  * released but interrupts still disabled.
  */
-static int acquire_console_semaphore_for_printk(unsigned int cpu)
+static int acquire_console_semaphore_for_printk(
+               struct syslog_ns *syslog_ns, unsigned int cpu)
 {
        int retval = 0;
 
@@ -653,24 +612,40 @@ static int acquire_console_semaphore_for_printk(unsigned int cpu)
                }
        }
        printk_cpu = UINT_MAX;
-       spin_unlock(&logbuf_lock);
+       spin_unlock(&sys_log_lock);
        return retval;
 }
 static const char recursion_bug_msg [] =
                KERN_CRIT "BUG: recent printk recursion!\n";
 static int recursion_bug;
-       static int new_text_line = 1;
+static int new_text_line = 1;
 static char printk_buf[1024];
 
+int printk_delay_msec __read_mostly;
+
+static inline void printk_delay(void)
+{
+       if (unlikely(printk_delay_msec)) {
+               int m = printk_delay_msec;
+
+               while (m--) {
+                       mdelay(1);
+                       touch_nmi_watchdog();
+               }
+       }
+}
+
 asmlinkage int vprintk(const char *fmt, va_list args)
 {
        int printed_len = 0;
        int current_log_level = default_message_loglevel;
+       struct syslog_ns *syslog_ns = current_syslog_ns();
        unsigned long flags;
        int this_cpu;
        char *p;
 
        boot_delay_msec();
+       printk_delay();
 
        preempt_disable();
        /* This stops the holder of console_sem just where we want him */
@@ -692,41 +667,56 @@ asmlinkage int vprintk(const char *fmt, va_list args)
                        recursion_bug = 1;
                        goto out_restore_irqs;
                }
-               zap_locks();
+               zap_locks(syslog_ns);
        }
 
        lockdep_off();
-       spin_lock(&logbuf_lock);
+       spin_lock(&sys_log_lock);
        printk_cpu = this_cpu;
 
        if (recursion_bug) {
                recursion_bug = 0;
                strcpy(printk_buf, recursion_bug_msg);
-               printed_len = sizeof(recursion_bug_msg);
+               printed_len = strlen(recursion_bug_msg);
        }
        /* Emit the output into the temporary buffer */
        printed_len += vscnprintf(printk_buf + printed_len,
                                  sizeof(printk_buf) - printed_len, fmt, args);
 
 
+       p = printk_buf;
+
+       /* Do we have a loglevel in the string? */
+       if (p[0] == '<') {
+               unsigned char c = p[1];
+               if (c && p[2] == '>') {
+                       switch (c) {
+                       case '0' ... '7': /* loglevel */
+                               current_log_level = c - '0';
+                       /* Fallthrough - make sure we're on a new line */
+                       case 'd': /* KERN_DEFAULT */
+                               if (!new_text_line) {
+                                       emit_log_char(syslog_ns, '\n');
+                                       new_text_line = 1;
+                               }
+                       /* Fallthrough - skip the loglevel */
+                       case 'c': /* KERN_CONT */
+                               p += 3;
+                               break;
+                       }
+               }
+       }
+
        /*
         * Copy the output into log_buf.  If the caller didn't provide
         * appropriate log level tags, we insert them here
         */
-       for (p = printk_buf; *p; p++) {
+       for ( ; *p; p++) {
                if (new_text_line) {
-                       /* If a token, set current_log_level and skip over */
-                       if (p[0] == '<' && p[1] >= '0' && p[1] <= '7' &&
-                           p[2] == '>') {
-                               current_log_level = p[1] - '0';
-                               p += 3;
-                               printed_len -= 3;
-                       }
-
                        /* Always output the token */
-                       emit_log_char('<');
-                       emit_log_char(current_log_level + '0');
-                       emit_log_char('>');
+                       emit_log_char(syslog_ns, '<');
+                       emit_log_char(syslog_ns, current_log_level + '0');
+                       emit_log_char(syslog_ns, '>');
                        printed_len += 3;
                        new_text_line = 0;
 
@@ -739,12 +729,13 @@ asmlinkage int vprintk(const char *fmt, va_list args)
 
                                t = cpu_clock(printk_cpu);
                                nanosec_rem = do_div(t, 1000000000);
-                               tlen = sprintf(tbuf, "[%5lu.%06lu] ",
+                               tlen = sprintf(tbuf, "ns_id='%d' %5lu.%06lu] ",
+                                               syslog_ns->handle,
                                                (unsigned long) t,
                                                nanosec_rem / 1000);
 
                                for (tp = tbuf; tp < tbuf + tlen; tp++)
-                                       emit_log_char(*tp);
+                                       emit_log_char(syslog_ns, *tp);
                                printed_len += tlen;
                        }
 
@@ -752,7 +743,7 @@ asmlinkage int vprintk(const char *fmt, va_list args)
                                break;
                }
 
-               emit_log_char(*p);
+               emit_log_char(syslog_ns, *p);
                if (*p == '\n')
                        new_text_line = 1;
        }
@@ -767,7 +758,7 @@ asmlinkage int vprintk(const char *fmt, va_list args)
         * will release 'logbuf_lock' regardless of whether it
         * actually gets the semaphore or not.
         */
-       if (acquire_console_semaphore_for_printk(this_cpu))
+       if (acquire_console_semaphore_for_printk(syslog_ns, this_cpu))
                release_console_sem();
 
        lockdep_on();
@@ -780,17 +771,6 @@ out_restore_irqs:
 EXPORT_SYMBOL(printk);
 EXPORT_SYMBOL(vprintk);
 
-#else
-
-asmlinkage long sys_syslog(int type, char __user *buf, int len)
-{
-       return -ENOSYS;
-}
-
-static void call_console_drivers(unsigned start, unsigned end)
-{
-}
-
 #endif
 
 static int __add_preferred_console(char *name, int idx, char *options,
@@ -936,12 +916,14 @@ void suspend_console(void)
        printk("Suspending console(s) (use no_console_suspend to debug)\n");
        acquire_console_sem();
        console_suspended = 1;
+       up(&console_sem);
 }
 
 void resume_console(void)
 {
        if (!console_suspend_enabled)
                return;
+       down(&console_sem);
        console_suspended = 0;
        release_console_sem();
 }
@@ -957,11 +939,9 @@ void resume_console(void)
 void acquire_console_sem(void)
 {
        BUG_ON(in_interrupt());
-       if (console_suspended) {
-               down(&secondary_console_sem);
-               return;
-       }
        down(&console_sem);
+       if (console_suspended)
+               return;
        console_locked = 1;
        console_may_schedule = 1;
 }
@@ -971,6 +951,10 @@ int try_acquire_console_sem(void)
 {
        if (down_trylock(&console_sem))
                return -1;
+       if (console_suspended) {
+               up(&console_sem);
+               return -1;
+       }
        console_locked = 1;
        console_may_schedule = 0;
        return 0;
@@ -982,10 +966,25 @@ int is_console_locked(void)
        return console_locked;
 }
 
-void wake_up_klogd(void)
+static DEFINE_PER_CPU(int, printk_pending);
+
+void printk_tick(void)
 {
-       if (!oops_in_progress && waitqueue_active(&log_wait))
+       if (__get_cpu_var(printk_pending)) {
+               __get_cpu_var(printk_pending) = 0;
                wake_up_interruptible(&log_wait);
+       }
+}
+
+int printk_needs_cpu(int cpu)
+{
+       return per_cpu(printk_pending, cpu);
+}
+
+void wake_up_klogd(void)
+{
+       if (waitqueue_active(&log_wait))
+               __raw_get_cpu_var(printk_pending) = 1;
 }
 
 /**
@@ -1004,36 +1003,40 @@ void wake_up_klogd(void)
  */
 void release_console_sem(void)
 {
-       unsigned long flags;
-       unsigned _con_start, _log_end;
-       unsigned wake_klogd = 0;
-
        if (console_suspended) {
-               up(&secondary_console_sem);
+               up(&console_sem);
                return;
        }
 
        console_may_schedule = 0;
-
-       for ( ; ; ) {
-               spin_lock_irqsave(&logbuf_lock, flags);
-               wake_klogd |= log_start - log_end;
-               if (con_start == log_end)
-                       break;                  /* Nothing to print */
-               _con_start = con_start;
-               _log_end = log_end;
-               con_start = log_end;            /* Flush */
-               spin_unlock(&logbuf_lock);
-               stop_critical_timings();        /* don't trace print latency */
-               call_console_drivers(_con_start, _log_end);
-               start_critical_timings();
-               local_irq_restore(flags);
+#ifdef CONFIG_PRINTK
+       {
+               unsigned long flags;
+               unsigned _con_start, _log_end;
+               unsigned wake_klogd = 0;
+               struct syslog_ns *syslog_ns = current_syslog_ns();
+
+               for ( ; ; ) {
+                       spin_lock_irqsave(&sys_log_lock, flags);
+                       wake_klogd |= sys_log_start - sys_log_end;
+                       if (sys_log_con_start == sys_log_end)
+                               break;                  /* Nothing to print */
+                       _con_start = sys_log_con_start;
+                       _log_end = sys_log_end;
+                       sys_log_con_start = sys_log_end;        /* Flush */
+                       spin_unlock(&sys_log_lock);
+                       stop_critical_timings();/* don't trace print latency */
+                       call_console_drivers(syslog_ns, _con_start, _log_end);
+                       start_critical_timings();
+                       local_irq_restore(flags);
+               }
+               spin_unlock_irqrestore(&sys_log_lock, flags);
+               if (wake_klogd)
+                       wake_up_klogd();
        }
+#endif
        console_locked = 0;
        up(&console_sem);
-       spin_unlock_irqrestore(&logbuf_lock, flags);
-       if (wake_klogd)
-               wake_up_klogd();
 }
 EXPORT_SYMBOL(release_console_sem);
 
@@ -1053,12 +1056,6 @@ void __sched console_conditional_schedule(void)
 }
 EXPORT_SYMBOL(console_conditional_schedule);
 
-void console_print(const char *s)
-{
-       printk(KERN_EMERG "%s", s);
-}
-EXPORT_SYMBOL(console_print);
-
 void console_unblank(void)
 {
        struct console *c;
@@ -1075,7 +1072,7 @@ void console_unblank(void)
 
        console_locked = 1;
        console_may_schedule = 0;
-       for (c = console_drivers; c != NULL; c = c->next)
+       for_each_console(c)
                if ((c->flags & CON_ENABLED) && c->unblank)
                        c->unblank();
        release_console_sem();
@@ -1090,7 +1087,7 @@ struct tty_driver *console_device(int *index)
        struct tty_driver *driver = NULL;
 
        acquire_console_sem();
-       for (c = console_drivers; c != NULL; c = c->next) {
+       for_each_console(c) {
                if (!c->device)
                        continue;
                driver = c->device(c, index);
@@ -1127,25 +1124,48 @@ EXPORT_SYMBOL(console_start);
  * to register the console printing procedure with printk() and to
  * print any messages that were printed by the kernel before the
  * console driver was initialized.
+ *
+ * This can happen pretty early during the boot process (because of
+ * early_printk) - sometimes before setup_arch() completes - be careful
+ * of what kernel features are used - they may not be initialised yet.
+ *
+ * There are two types of consoles - bootconsoles (early_printk) and
+ * "real" consoles (everything which is not a bootconsole) which are
+ * handled differently.
+ *  - Any number of bootconsoles can be registered at any time.
+ *  - As soon as a "real" console is registered, all bootconsoles
+ *    will be unregistered automatically.
+ *  - Once a "real" console is registered, any attempt to register a
+ *    bootconsoles will be rejected
  */
-void register_console(struct console *console)
+void register_console(struct console *newcon)
 {
        int i;
-       unsigned long flags;
-       struct console *bootconsole = NULL;
+       struct console *bcon = NULL;
 
-       if (console_drivers) {
-               if (console->flags & CON_BOOT)
-                       return;
-               if (console_drivers->flags & CON_BOOT)
-                       bootconsole = console_drivers;
+       /*
+        * before we register a new CON_BOOT console, make sure we don't
+        * already have a valid console
+        */
+       if (console_drivers && newcon->flags & CON_BOOT) {
+               /* find the last or real console */
+               for_each_console(bcon) {
+                       if (!(bcon->flags & CON_BOOT)) {
+                               printk(KERN_INFO "Too late to register bootconsole %s%d\n",
+                                       newcon->name, newcon->index);
+                               return;
+                       }
+               }
        }
 
-       if (preferred_console < 0 || bootconsole || !console_drivers)
+       if (console_drivers && console_drivers->flags & CON_BOOT)
+               bcon = console_drivers;
+
+       if (preferred_console < 0 || bcon || !console_drivers)
                preferred_console = selected_console;
 
-       if (console->early_setup)
-               console->early_setup();
+       if (newcon->early_setup)
+               newcon->early_setup();
 
        /*
         *      See if we want to use this console driver. If we
@@ -1153,13 +1173,13 @@ void register_console(struct console *console)
         *      that registers here.
         */
        if (preferred_console < 0) {
-               if (console->index < 0)
-                       console->index = 0;
-               if (console->setup == NULL ||
-                   console->setup(console, NULL) == 0) {
-                       console->flags |= CON_ENABLED;
-                       if (console->device) {
-                               console->flags |= CON_CONSDEV;
+               if (newcon->index < 0)
+                       newcon->index = 0;
+               if (newcon->setup == NULL ||
+                   newcon->setup(newcon, NULL) == 0) {
+                       newcon->flags |= CON_ENABLED;
+                       if (newcon->device) {
+                               newcon->flags |= CON_CONSDEV;
                                preferred_console = 0;
                        }
                }
@@ -1171,73 +1191,99 @@ void register_console(struct console *console)
         */
        for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0];
                        i++) {
-               if (strcmp(console_cmdline[i].name, console->name) != 0)
+               if (strcmp(console_cmdline[i].name, newcon->name) != 0)
                        continue;
-               if (console->index >= 0 &&
-                   console->index != console_cmdline[i].index)
+               if (newcon->index >= 0 &&
+                   newcon->index != console_cmdline[i].index)
                        continue;
-               if (console->index < 0)
-                       console->index = console_cmdline[i].index;
+               if (newcon->index < 0)
+                       newcon->index = console_cmdline[i].index;
 #ifdef CONFIG_A11Y_BRAILLE_CONSOLE
                if (console_cmdline[i].brl_options) {
-                       console->flags |= CON_BRL;
-                       braille_register_console(console,
+                       newcon->flags |= CON_BRL;
+                       braille_register_console(newcon,
                                        console_cmdline[i].index,
                                        console_cmdline[i].options,
                                        console_cmdline[i].brl_options);
                        return;
                }
 #endif
-               if (console->setup &&
-                   console->setup(console, console_cmdline[i].options) != 0)
+               if (newcon->setup &&
+                   newcon->setup(newcon, console_cmdline[i].options) != 0)
                        break;
-               console->flags |= CON_ENABLED;
-               console->index = console_cmdline[i].index;
+               newcon->flags |= CON_ENABLED;
+               newcon->index = console_cmdline[i].index;
                if (i == selected_console) {
-                       console->flags |= CON_CONSDEV;
+                       newcon->flags |= CON_CONSDEV;
                        preferred_console = selected_console;
                }
                break;
        }
 
-       if (!(console->flags & CON_ENABLED))
+       if (!(newcon->flags & CON_ENABLED))
                return;
 
-       if (bootconsole && (console->flags & CON_CONSDEV)) {
-               printk(KERN_INFO "console handover: boot [%s%d] -> real [%s%d]\n",
-                      bootconsole->name, bootconsole->index,
-                      console->name, console->index);
-               unregister_console(bootconsole);
-               console->flags &= ~CON_PRINTBUFFER;
-       } else {
-               printk(KERN_INFO "console [%s%d] enabled\n",
-                      console->name, console->index);
-       }
+       /*
+        * If we have a bootconsole, and are switching to a real console,
+        * don't print everything out again, since when the boot console, and
+        * the real console are the same physical device, it's annoying to
+        * see the beginning boot messages twice
+        */
+       if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV))
+               newcon->flags &= ~CON_PRINTBUFFER;
 
        /*
         *      Put this console in the list - keep the
         *      preferred driver at the head of the list.
         */
        acquire_console_sem();
-       if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
-               console->next = console_drivers;
-               console_drivers = console;
-               if (console->next)
-                       console->next->flags &= ~CON_CONSDEV;
+       if ((newcon->flags & CON_CONSDEV) || console_drivers == NULL) {
+               newcon->next = console_drivers;
+               console_drivers = newcon;
+               if (newcon->next)
+                       newcon->next->flags &= ~CON_CONSDEV;
        } else {
-               console->next = console_drivers->next;
-               console_drivers->next = console;
+               newcon->next = console_drivers->next;
+               console_drivers->next = newcon;
        }
-       if (console->flags & CON_PRINTBUFFER) {
+#ifdef CONFIG_PRINTK
+       if (newcon->flags & CON_PRINTBUFFER) {
+               unsigned long flags;
                /*
                 * release_console_sem() will print out the buffered messages
                 * for us.
                 */
-               spin_lock_irqsave(&logbuf_lock, flags);
-               con_start = log_start;
-               spin_unlock_irqrestore(&logbuf_lock, flags);
+
+               struct syslog_ns *syslog_ns = current_syslog_ns();
+
+               spin_lock_irqsave(&sys_log_lock, flags);
+               sys_log_con_start = sys_log_start;
+               spin_unlock_irqrestore(&sys_log_lock, flags);
        }
+#endif
        release_console_sem();
+
+       /*
+        * By unregistering the bootconsoles after we enable the real console
+        * we get the "console xxx enabled" message on all the consoles -
+        * boot consoles, real consoles, etc - this is to ensure that end
+        * users know there might be something in the kernel's log buffer that
+        * went to the bootconsole (that they do not see on the real console)
+        */
+       if (bcon && ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV)) {
+               /* we need to iterate through twice, to make sure we print
+                * everything out, before we unregister the console(s)
+                */
+               printk(KERN_INFO "console [%s%d] enabled, bootconsole disabled\n",
+                       newcon->name, newcon->index);
+               for_each_console(bcon)
+                       if (bcon->flags & CON_BOOT)
+                               unregister_console(bcon);
+       } else {
+               printk(KERN_INFO "%sconsole [%s%d] enabled\n",
+                       (newcon->flags & CON_BOOT) ? "boot" : "" ,
+                       newcon->name, newcon->index);
+       }
 }
 EXPORT_SYMBOL(register_console);
 
@@ -1280,33 +1326,19 @@ EXPORT_SYMBOL(unregister_console);
 
 static int __init disable_boot_consoles(void)
 {
-       if (console_drivers != NULL) {
-               if (console_drivers->flags & CON_BOOT) {
+       struct console *con;
+
+       for_each_console(con) {
+               if (con->flags & CON_BOOT) {
                        printk(KERN_INFO "turn off boot console %s%d\n",
-                               console_drivers->name, console_drivers->index);
-                       return unregister_console(console_drivers);
+                               con->name, con->index);
+                       unregister_console(con);
                }
        }
        return 0;
 }
 late_initcall(disable_boot_consoles);
 
-/**
- * tty_write_message - write a message to a certain tty, not just the console.
- * @tty: the destination tty_struct
- * @msg: the message to write
- *
- * This is used for messages that need to be redirected to a specific tty.
- * We don't put it into the syslog queue right now maybe in the future if
- * really needed.
- */
-void tty_write_message(struct tty_struct *tty, char *msg)
-{
-       if (tty && tty->ops->write)
-               tty->ops->write(tty, msg, strlen(msg));
-       return;
-}
-
 #if defined CONFIG_PRINTK
 
 /*
@@ -1317,11 +1349,11 @@ void tty_write_message(struct tty_struct *tty, char *msg)
  */
 DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 10);
 
-int printk_ratelimit(void)
+int __printk_ratelimit(const char *func)
 {
-       return __ratelimit(&printk_ratelimit_state);
+       return ___ratelimit(&printk_ratelimit_state, func);
 }
-EXPORT_SYMBOL(printk_ratelimit);
+EXPORT_SYMBOL(__printk_ratelimit);
 
 /**
  * printk_timed_ratelimit - caller-controlled printk ratelimiting
@@ -1335,11 +1367,135 @@ EXPORT_SYMBOL(printk_ratelimit);
 bool printk_timed_ratelimit(unsigned long *caller_jiffies,
                        unsigned int interval_msecs)
 {
-       if (*caller_jiffies == 0 || time_after(jiffies, *caller_jiffies)) {
-               *caller_jiffies = jiffies + msecs_to_jiffies(interval_msecs);
+       if (*caller_jiffies == 0
+                       || !time_in_range(jiffies, *caller_jiffies,
+                                       *caller_jiffies
+                                       + msecs_to_jiffies(interval_msecs))) {
+               *caller_jiffies = jiffies;
                return true;
        }
        return false;
 }
 EXPORT_SYMBOL(printk_timed_ratelimit);
+
+static DEFINE_SPINLOCK(dump_list_lock);
+static LIST_HEAD(dump_list);
+
+/**
+ * kmsg_dump_register - register a kernel log dumper.
+ * @dumper: pointer to the kmsg_dumper structure
+ *
+ * Adds a kernel log dumper to the system. The dump callback in the
+ * structure will be called when the kernel oopses or panics and must be
+ * set. Returns zero on success and %-EINVAL or %-EBUSY otherwise.
+ */
+int kmsg_dump_register(struct kmsg_dumper *dumper)
+{
+       unsigned long flags;
+       int err = -EBUSY;
+
+       /* The dump callback needs to be set */
+       if (!dumper->dump)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dump_list_lock, flags);
+       /* Don't allow registering multiple times */
+       if (!dumper->registered) {
+               dumper->registered = 1;
+               list_add_tail(&dumper->list, &dump_list);
+               err = 0;
+       }
+       spin_unlock_irqrestore(&dump_list_lock, flags);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(kmsg_dump_register);
+
+/**
+ * kmsg_dump_unregister - unregister a kmsg dumper.
+ * @dumper: pointer to the kmsg_dumper structure
+ *
+ * Removes a dump device from the system. Returns zero on success and
+ * %-EINVAL otherwise.
+ */
+int kmsg_dump_unregister(struct kmsg_dumper *dumper)
+{
+       unsigned long flags;
+       int err = -EINVAL;
+
+       spin_lock_irqsave(&dump_list_lock, flags);
+       if (dumper->registered) {
+               dumper->registered = 0;
+               list_del(&dumper->list);
+               err = 0;
+       }
+       spin_unlock_irqrestore(&dump_list_lock, flags);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(kmsg_dump_unregister);
+
+static const char const *kmsg_reasons[] = {
+       [KMSG_DUMP_OOPS]        = "oops",
+       [KMSG_DUMP_PANIC]       = "panic",
+       [KMSG_DUMP_KEXEC]       = "kexec",
+};
+
+static const char *kmsg_to_str(enum kmsg_dump_reason reason)
+{
+       if (reason >= ARRAY_SIZE(kmsg_reasons) || reason < 0)
+               return "unknown";
+
+       return kmsg_reasons[reason];
+}
+
+/**
+ * kmsg_dump - dump kernel log to kernel message dumpers.
+ * @reason: the reason (oops, panic etc) for dumping
+ *
+ * Iterate through each of the dump devices and call the oops/panic
+ * callbacks with the log buffer.
+ */
+void kmsg_dump(enum kmsg_dump_reason reason)
+{
+       unsigned long end;
+       unsigned chars;
+       struct kmsg_dumper *dumper;
+       const char *s1, *s2;
+       unsigned long l1, l2;
+       unsigned long flags;
+       struct syslog_ns *syslog_ns = current_syslog_ns();
+
+       /* Theoretically, the log could move on after we do this, but
+          there's not a lot we can do about that. The new messages
+          will overwrite the start of what we dump. */
+       spin_lock_irqsave(&sys_log_lock, flags);
+       end = sys_log_end & LOG_BUF_MASK(syslog_ns);
+       chars = sys_log_logged_chars;
+       spin_unlock_irqrestore(&sys_log_lock, flags);
+
+       if (sys_log_logged_chars > end) {
+               s1 = sys_log_buf + sys_log_buf_len - sys_log_logged_chars + end;
+               l1 = sys_log_logged_chars - end;
+
+               s2 = sys_log_buf;
+               l2 = end;
+       } else {
+               s1 = "";
+               l1 = 0;
+
+               s2 = sys_log_buf + end - sys_log_logged_chars;
+               l2 = sys_log_logged_chars;
+       }
+
+       if (!spin_trylock_irqsave(&dump_list_lock, flags)) {
+               printk(KERN_ERR "dump_kmsg: dump list lock is held during %s, skipping dump\n",
+                               kmsg_to_str(reason));
+               return;
+       }
+       list_for_each_entry(dumper, &dump_list, list)
+               dumper->dump(dumper, reason, s1, l1, s2, l2);
+       spin_unlock_irqrestore(&dump_list_lock, flags);
+}
 #endif
+