cgroups: revamp subsys array
[linux-2.6.git] / kernel / printk.c
index 58bbec6..75077ad 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/jiffies.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 __attribute__((weak)) early_printk(const char *fmt, ...)
+void asmlinkage __attribute__((weak)) early_printk(const char *fmt, ...)
 {
 }
 
@@ -74,8 +83,9 @@ 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);
+
 /*
  * This is used for debugging the mess that is the VT code by
  * keeping track if we have the console semaphore held. It's
@@ -93,16 +103,16 @@ static int console_locked, console_suspended;
  */
 static DEFINE_SPINLOCK(logbuf_lock);
 
-#define LOG_BUF_MASK   (log_buf_len-1)
+#define LOG_BUF_MASK (log_buf_len-1)
 #define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])
 
 /*
  * The indices into log_buf are not constrained to log_buf_len - they
  * must be masked before subscripting
  */
-static unsigned long log_start;        /* Index into log_buf: next char to be read by syslog() */
-static unsigned long con_start;        /* Index into log_buf: next char to be sent to consoles */
-static unsigned long log_end;  /* Index into log_buf: most-recently-written-char + 1 */
+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 */
 
 /*
  *     Array of consoles built from command line options (console=)
@@ -112,6 +122,9 @@ struct console_cmdline
        char    name[8];                        /* Name of the driver       */
        int     index;                          /* Minor dev. to use        */
        char    *options;                       /* Options for the driver   */
+#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
+       char    *brl_options;                   /* Options for braille driver */
+#endif
 };
 
 #define MAX_CMDLINECONSOLES 8
@@ -119,6 +132,8 @@ struct console_cmdline
 static struct console_cmdline console_cmdline[MAX_CMDLINECONSOLES];
 static int selected_console = -1;
 static int preferred_console = -1;
+int console_set_on_cmdline;
+EXPORT_SYMBOL(console_set_on_cmdline);
 
 /* Flag: console code may call schedule() */
 static int console_may_schedule;
@@ -128,17 +143,36 @@ static int console_may_schedule;
 static char __log_buf[__LOG_BUF_LEN];
 static char *log_buf = __log_buf;
 static int log_buf_len = __LOG_BUF_LEN;
-static unsigned long logged_chars; /* Number of chars produced since last read+clear operation */
+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)
+{
+       VMCOREINFO_SYMBOL(log_buf);
+       VMCOREINFO_SYMBOL(log_end);
+       VMCOREINFO_SYMBOL(log_buf_len);
+       VMCOREINFO_SYMBOL(logged_chars);
+}
+#endif
 
 static int __init log_buf_len_setup(char *str)
 {
-       unsigned long size = memparse(str, &str);
+       unsigned size = memparse(str, &str);
        unsigned long flags;
 
        if (size)
                size = roundup_pow_of_two(size);
        if (size > log_buf_len) {
-               unsigned long start, dest_idx, offset;
+               unsigned start, dest_idx, offset;
                char *new_log_buf;
 
                new_log_buf = alloc_bootmem(size);
@@ -174,12 +208,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;
@@ -188,10 +221,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);
@@ -204,7 +236,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) {
@@ -226,90 +258,23 @@ static inline void boot_delay_msec(void)
 }
 #endif
 
-/*
- * Return the number of unread characters in the log buffer.
- */
-int log_buf_get_len(void)
+int do_syslog(int type, char __user *buf, int len, bool from_file)
 {
-       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;
-}
-
-/*
- * Extract a single character from the log buffer.
- */
-int log_buf_read(int idx)
-{
-       char ret;
-
-       if (log_buf_copy(&ret, idx, 1) == 1)
-               return ret;
-       else
-               return -1;
-}
-
-/*
- * 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)
-{
-       unsigned long i, j, limit, count;
+       unsigned i, j, limit, count;
        int do_clear = 0;
        char c;
        int error = 0;
 
-       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;
@@ -340,10 +305,12 @@ int do_syslog(int type, char __user *buf, int len)
                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;
@@ -396,28 +363,41 @@ int do_syslog(int type, char __user *buf, int len)
                        }
                }
                break;
-       case 5:         /* Clear ring buffer */
+       /* Clear ring buffer */
+       case SYSLOG_ACTION_CLEAR:
                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 */
+       /* Number of chars in the log buffer */
+       case SYSLOG_ACTION_SIZE_UNREAD:
                error = log_end - log_start;
                break;
-       case 10:        /* Size of the log buffer */
+       /* Size of the log buffer */
+       case SYSLOG_ACTION_SIZE_BUFFER:
                error = log_buf_len;
                break;
        default:
@@ -428,19 +408,19 @@ 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 long start, unsigned long end)
+static void __call_console_drivers(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)))
@@ -455,16 +435,16 @@ static int __init ignore_loglevel_setup(char *str)
        ignore_loglevel = 1;
        printk(KERN_INFO "debug: ignoring loglevel setting.\n");
 
-       return 1;
+       return 0;
 }
 
-__setup("ignore_loglevel", ignore_loglevel_setup);
+early_param("ignore_loglevel", ignore_loglevel_setup);
 
 /*
  * Write out chars from start to end - 1 inclusive
  */
-static void _call_console_drivers(unsigned long start,
-                               unsigned long end, int msg_log_level)
+static void _call_console_drivers(unsigned start,
+                               unsigned end, int msg_log_level)
 {
        if ((msg_log_level < console_loglevel || ignore_loglevel) &&
                        console_drivers && start != end) {
@@ -484,12 +464,12 @@ static void _call_console_drivers(unsigned long start,
  * log_buf[start] to log_buf[end - 1].
  * The console_sem must be held.
  */
-static void call_console_drivers(unsigned long start, unsigned long end)
+static void call_console_drivers(unsigned start, unsigned end)
 {
-       unsigned long cur_index, start_print;
+       unsigned cur_index, start_print;
        static int msg_level = -1;
 
-       BUG_ON(((long)(start - end)) > 0);
+       BUG_ON(((int)(start - end)) > 0);
 
        cur_index = start;
        start_print = start;
@@ -567,25 +547,12 @@ static int printk_time = 0;
 #endif
 module_param_named(time, printk_time, bool, S_IRUGO | S_IWUSR);
 
-static int __init printk_time_setup(char *str)
-{
-       if (*str)
-               return 0;
-       printk_time = 1;
-       printk(KERN_NOTICE "The 'time' option is deprecated and "
-               "is scheduled for removal in early 2008\n");
-       printk(KERN_NOTICE "Use 'printk.time=<value>' instead\n");
-       return 1;
-}
-
-__setup("time", printk_time_setup);
-
 /* Check if we have any console registered that can be called early in boot. */
 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;
 
@@ -597,9 +564,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
@@ -613,6 +577,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, ...)
@@ -630,21 +596,82 @@ asmlinkage int printk(const char *fmt, ...)
 /* cpu currently holding logbuf_lock */
 static volatile unsigned int printk_cpu = UINT_MAX;
 
-const char printk_recursion_bug_msg [] =
-                       KERN_CRIT "BUG: recent printk recursion!\n";
-static int printk_recursion_bug;
+/*
+ * Can we actually use the console at this time on this cpu?
+ *
+ * Console drivers may assume that per-cpu resources have
+ * been allocated. So unless they're explicitly marked as
+ * being able to cope (CON_ANYTIME) don't call them until
+ * this CPU is officially up.
+ */
+static inline int can_use_console(unsigned int cpu)
+{
+       return cpu_online(cpu) || have_callable_console();
+}
 
-asmlinkage int vprintk(const char *fmt, va_list args)
+/*
+ * Try to get console ownership to actually show the kernel
+ * messages from a 'printk'. Return true (and with the
+ * console_semaphore held, and 'console_locked' set) if it
+ * is successful, false otherwise.
+ *
+ * This gets called with the 'logbuf_lock' spinlock held and
+ * 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 log_level_unknown = 1;
-       static char printk_buf[1024];
+       int retval = 0;
 
-       unsigned long flags;
+       if (!try_acquire_console_sem()) {
+               retval = 1;
+
+               /*
+                * If we can't use the console, we need to release
+                * the console semaphore by hand to avoid flushing
+                * the buffer. We need to hold the console semaphore
+                * in order to do this test safely.
+                */
+               if (!can_use_console(cpu)) {
+                       console_locked = 0;
+                       up(&console_sem);
+                       retval = 0;
+               }
+       }
+       printk_cpu = UINT_MAX;
+       spin_unlock(&logbuf_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 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;
+       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 */
@@ -663,7 +690,7 @@ asmlinkage int vprintk(const char *fmt, va_list args)
                 * it can be printed at the next appropriate moment:
                 */
                if (!oops_in_progress) {
-                       printk_recursion_bug = 1;
+                       recursion_bug = 1;
                        goto out_restore_irqs;
                }
                zap_locks();
@@ -673,109 +700,95 @@ asmlinkage int vprintk(const char *fmt, va_list args)
        spin_lock(&logbuf_lock);
        printk_cpu = this_cpu;
 
-       if (printk_recursion_bug) {
-               printk_recursion_bug = 0;
-               strcpy(printk_buf, printk_recursion_bug_msg);
-               printed_len = sizeof(printk_recursion_bug_msg);
+       if (recursion_bug) {
+               recursion_bug = 0;
+               strcpy(printk_buf, 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), fmt, args);
+                                 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('\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++) {
-               if (log_level_unknown) {
-                        /* log_level_unknown signals the start of a new line */
+       for ( ; *p; p++) {
+               if (new_text_line) {
+                       /* Always output the token */
+                       emit_log_char('<');
+                       emit_log_char(current_log_level + '0');
+                       emit_log_char('>');
+                       printed_len += 3;
+                       new_text_line = 0;
+
                        if (printk_time) {
-                               int loglev_char;
+                               /* Follow the token with the time */
                                char tbuf[50], *tp;
                                unsigned tlen;
                                unsigned long long t;
                                unsigned long nanosec_rem;
 
-                               /*
-                                * force the log level token to be
-                                * before the time output.
-                                */
-                               if (p[0] == '<' && p[1] >='0' &&
-                                  p[1] <= '7' && p[2] == '>') {
-                                       loglev_char = p[1];
-                                       p += 3;
-                                       printed_len -= 3;
-                               } else {
-                                       loglev_char = default_message_loglevel
-                                               + '0';
-                               }
                                t = cpu_clock(printk_cpu);
                                nanosec_rem = do_div(t, 1000000000);
-                               tlen = sprintf(tbuf,
-                                               "<%c>[%5lu.%06lu] ",
-                                               loglev_char,
-                                               (unsigned long)t,
-                                               nanosec_rem/1000);
+                               tlen = sprintf(tbuf, "[%5lu.%06lu] ",
+                                               (unsigned long) t,
+                                               nanosec_rem / 1000);
 
                                for (tp = tbuf; tp < tbuf + tlen; tp++)
                                        emit_log_char(*tp);
                                printed_len += tlen;
-                       } else {
-                               if (p[0] != '<' || p[1] < '0' ||
-                                  p[1] > '7' || p[2] != '>') {
-                                       emit_log_char('<');
-                                       emit_log_char(default_message_loglevel
-                                               + '0');
-                                       emit_log_char('>');
-                                       printed_len += 3;
-                               }
                        }
-                       log_level_unknown = 0;
+
                        if (!*p)
                                break;
                }
+
                emit_log_char(*p);
                if (*p == '\n')
-                       log_level_unknown = 1;
+                       new_text_line = 1;
        }
 
-       if (!down_trylock(&console_sem)) {
-               /*
-                * We own the drivers.  We can drop the spinlock and
-                * let release_console_sem() print the text, maybe ...
-                */
-               console_locked = 1;
-               printk_cpu = UINT_MAX;
-               spin_unlock(&logbuf_lock);
+       /*
+        * Try to acquire and then immediately release the
+        * console semaphore. The release will do all the
+        * actual magic (print out buffers, wake up klogd,
+        * etc). 
+        *
+        * The acquire_console_semaphore_for_printk() function
+        * will release 'logbuf_lock' regardless of whether it
+        * actually gets the semaphore or not.
+        */
+       if (acquire_console_semaphore_for_printk(this_cpu))
+               release_console_sem();
 
-               /*
-                * Console drivers may assume that per-cpu resources have
-                * been allocated. So unless they're explicitly marked as
-                * being able to cope (CON_ANYTIME) don't call them until
-                * this CPU is officially up.
-                */
-               if (cpu_online(smp_processor_id()) || have_callable_console()) {
-                       console_may_schedule = 0;
-                       release_console_sem();
-               } else {
-                       /* Release by hand to avoid flushing the buffer. */
-                       console_locked = 0;
-                       up(&console_sem);
-               }
-               lockdep_on();
-               raw_local_irq_restore(flags);
-       } else {
-               /*
-                * Someone else owns the drivers.  We drop the spinlock, which
-                * allows the semaphore holder to proceed and to call the
-                * console drivers with the output which we just produced.
-                */
-               printk_cpu = UINT_MAX;
-               spin_unlock(&logbuf_lock);
-               lockdep_on();
+       lockdep_on();
 out_restore_irqs:
-               raw_local_irq_restore(flags);
-       }
+       raw_local_irq_restore(flags);
 
        preempt_enable();
        return printed_len;
@@ -785,26 +798,66 @@ EXPORT_SYMBOL(vprintk);
 
 #else
 
-asmlinkage long sys_syslog(int type, char __user *buf, int len)
+static void call_console_drivers(unsigned start, unsigned end)
 {
-       return -ENOSYS;
 }
 
-static void call_console_drivers(unsigned long start, unsigned long end)
+#endif
+
+static int __add_preferred_console(char *name, int idx, char *options,
+                                  char *brl_options)
 {
-}
+       struct console_cmdline *c;
+       int i;
 
+       /*
+        *      See if this tty is not yet registered, and
+        *      if we have a slot free.
+        */
+       for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
+               if (strcmp(console_cmdline[i].name, name) == 0 &&
+                         console_cmdline[i].index == idx) {
+                               if (!brl_options)
+                                       selected_console = i;
+                               return 0;
+               }
+       if (i == MAX_CMDLINECONSOLES)
+               return -E2BIG;
+       if (!brl_options)
+               selected_console = i;
+       c = &console_cmdline[i];
+       strlcpy(c->name, name, sizeof(c->name));
+       c->options = options;
+#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
+       c->brl_options = brl_options;
 #endif
-
+       c->index = idx;
+       return 0;
+}
 /*
  * Set up a list of consoles.  Called from init/main.c
  */
 static int __init console_setup(char *str)
 {
        char buf[sizeof(console_cmdline[0].name) + 4]; /* 4 for index */
-       char *s, *options;
+       char *s, *options, *brl_options = NULL;
        int idx;
 
+#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
+       if (!memcmp(str, "brl,", 4)) {
+               brl_options = "";
+               str += 4;
+       } else if (!memcmp(str, "brl=", 4)) {
+               brl_options = str + 4;
+               str = strchr(brl_options, ',');
+               if (!str) {
+                       printk(KERN_ERR "need port name after brl=\n");
+                       return 1;
+               }
+               *(str++) = 0;
+       }
+#endif
+
        /*
         * Decode str into name, index, options.
         */
@@ -829,7 +882,8 @@ static int __init console_setup(char *str)
        idx = simple_strtoul(s, NULL, 10);
        *s = 0;
 
-       add_preferred_console(buf, idx, options);
+       __add_preferred_console(buf, idx, options, brl_options);
+       console_set_on_cmdline = 1;
        return 1;
 }
 __setup("console=", console_setup);
@@ -849,28 +903,7 @@ __setup("console=", console_setup);
  */
 int add_preferred_console(char *name, int idx, char *options)
 {
-       struct console_cmdline *c;
-       int i;
-
-       /*
-        *      See if this tty is not yet registered, and
-        *      if we have a slot free.
-        */
-       for (i = 0; i < MAX_CMDLINECONSOLES && console_cmdline[i].name[0]; i++)
-               if (strcmp(console_cmdline[i].name, name) == 0 &&
-                         console_cmdline[i].index == idx) {
-                               selected_console = i;
-                               return 0;
-               }
-       if (i == MAX_CMDLINECONSOLES)
-               return -E2BIG;
-       selected_console = i;
-       c = &console_cmdline[i];
-       memcpy(c->name, name, sizeof(c->name));
-       c->name[sizeof(c->name) - 1] = 0;
-       c->options = options;
-       c->index = idx;
-       return 0;
+       return __add_preferred_console(name, idx, options, NULL);
 }
 
 int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, char *options)
@@ -882,7 +915,7 @@ int update_console_cmdline(char *name, int idx, char *name_new, int idx_new, cha
                if (strcmp(console_cmdline[i].name, name) == 0 &&
                          console_cmdline[i].index == idx) {
                                c = &console_cmdline[i];
-                               memcpy(c->name, name_new, sizeof(c->name));
+                               strlcpy(c->name, name_new, sizeof(c->name));
                                c->name[sizeof(c->name) - 1] = 0;
                                c->options = options;
                                c->index = idx_new;
@@ -911,15 +944,17 @@ void suspend_console(void)
 {
        if (!console_suspend_enabled)
                return;
-       printk("Suspending console(s)\n");
+       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();
 }
@@ -935,11 +970,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;
 }
@@ -949,6 +982,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;
@@ -960,10 +997,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;
 }
 
 /**
@@ -983,11 +1035,11 @@ void wake_up_klogd(void)
 void release_console_sem(void)
 {
        unsigned long flags;
-       unsigned long _con_start, _log_end;
-       unsigned long wake_klogd = 0;
+       unsigned _con_start, _log_end;
+       unsigned wake_klogd = 0;
 
        if (console_suspended) {
-               up(&secondary_console_sem);
+               up(&console_sem);
                return;
        }
 
@@ -1002,7 +1054,9 @@ void release_console_sem(void)
                _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);
        }
        console_locked = 0;
@@ -1029,12 +1083,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;
@@ -1051,7 +1099,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();
@@ -1066,7 +1114,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);
@@ -1103,25 +1151,49 @@ 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
@@ -1129,12 +1201,15 @@ 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 | CON_CONSDEV;
-                       preferred_console = 0;
+               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;
+                       }
                }
        }
 
@@ -1144,54 +1219,62 @@ 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 (console->setup &&
-                   console->setup(console, console_cmdline[i].options) != 0)
+               if (newcon->index < 0)
+                       newcon->index = console_cmdline[i].index;
+#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
+               if (console_cmdline[i].brl_options) {
+                       newcon->flags |= CON_BRL;
+                       braille_register_console(newcon,
+                                       console_cmdline[i].index,
+                                       console_cmdline[i].options,
+                                       console_cmdline[i].brl_options);
+                       return;
+               }
+#endif
+               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) {
+       if (newcon->flags & CON_PRINTBUFFER) {
                /*
                 * release_console_sem() will print out the buffered messages
                 * for us.
@@ -1201,6 +1284,28 @@ void register_console(struct console *console)
                spin_unlock_irqrestore(&logbuf_lock, flags);
        }
        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);
 
@@ -1209,6 +1314,11 @@ int unregister_console(struct console *console)
         struct console *a, *b;
        int res = 1;
 
+#ifdef CONFIG_A11Y_BRAILLE_CONSOLE
+       if (console->flags & CON_BRL)
+               return braille_unregister_console(console);
+#endif
+
        acquire_console_sem();
        if (console_drivers == console) {
                console_drivers=console->next;
@@ -1238,82 +1348,34 @@ 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->driver->write)
-               tty->driver->write(tty, msg, strlen(msg));
-       return;
-}
+#if defined CONFIG_PRINTK
 
 /*
  * printk rate limiting, lifted from the networking subsystem.
  *
- * This enforces a rate limit: not more than one kernel message
- * every printk_ratelimit_jiffies to make a denial-of-service
- * attack impossible.
+ * This enforces a rate limit: not more than 10 kernel messages
+ * every 5s to make a denial-of-service attack impossible.
  */
-int __printk_ratelimit(int ratelimit_jiffies, int ratelimit_burst)
-{
-       static DEFINE_SPINLOCK(ratelimit_lock);
-       static unsigned long toks = 10 * 5 * HZ;
-       static unsigned long last_msg;
-       static int missed;
-       unsigned long flags;
-       unsigned long now = jiffies;
-
-       spin_lock_irqsave(&ratelimit_lock, flags);
-       toks += now - last_msg;
-       last_msg = now;
-       if (toks > (ratelimit_burst * ratelimit_jiffies))
-               toks = ratelimit_burst * ratelimit_jiffies;
-       if (toks >= ratelimit_jiffies) {
-               int lost = missed;
-
-               missed = 0;
-               toks -= ratelimit_jiffies;
-               spin_unlock_irqrestore(&ratelimit_lock, flags);
-               if (lost)
-                       printk(KERN_WARNING "printk: %d messages suppressed.\n", lost);
-               return 1;
-       }
-       missed++;
-       spin_unlock_irqrestore(&ratelimit_lock, flags);
-       return 0;
-}
-EXPORT_SYMBOL(__printk_ratelimit);
-
-/* minimum time in jiffies between messages */
-int printk_ratelimit_jiffies = 5 * HZ;
+DEFINE_RATELIMIT_STATE(printk_ratelimit_state, 5 * HZ, 10);
 
-/* number of messages we send before ratelimiting */
-int printk_ratelimit_burst = 10;
-
-int printk_ratelimit(void)
+int __printk_ratelimit(const char *func)
 {
-       return __printk_ratelimit(printk_ratelimit_jiffies,
-                               printk_ratelimit_burst);
+       return ___ratelimit(&printk_ratelimit_state, func);
 }
-EXPORT_SYMBOL(printk_ratelimit);
+EXPORT_SYMBOL(__printk_ratelimit);
 
 /**
  * printk_timed_ratelimit - caller-controlled printk ratelimiting
@@ -1327,10 +1389,133 @@ 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;
+
+       /* 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(&logbuf_lock, flags);
+       end = log_end & LOG_BUF_MASK;
+       chars = logged_chars;
+       spin_unlock_irqrestore(&logbuf_lock, flags);
+
+       if (logged_chars > end) {
+               s1 = log_buf + log_buf_len - logged_chars + end;
+               l1 = logged_chars - end;
+
+               s2 = log_buf;
+               l2 = end;
+       } else {
+               s1 = "";
+               l1 = 0;
+
+               s2 = log_buf + end - logged_chars;
+               l2 = 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