Merge branch 'core-printk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6.git] / kernel / printk.c
index a87770c..b5ac4d9 100644 (file)
 #include <linux/bootmem.h>
 #include <linux/syscalls.h>
 #include <linux/kexec.h>
+#include <linux/ratelimit.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, ...)
@@ -61,6 +68,8 @@ int console_printk[4] = {
        DEFAULT_CONSOLE_LOGLEVEL,       /* default_console_loglevel */
 };
 
+static int saved_console_loglevel = -1;
+
 /*
  * Low level drivers may need that to know if they can schedule in
  * their unblank() callback or not. So let's export it.
@@ -198,12 +207,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;
@@ -212,10 +220,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);
@@ -228,7 +235,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) {
@@ -372,10 +379,15 @@ int do_syslog(int type, char __user *buf, int len)
                logged_chars = 0;
                break;
        case 6:         /* Disable logging to console */
+               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;
+               if (saved_console_loglevel != -1) {
+                       console_loglevel = saved_console_loglevel;
+                       saved_console_loglevel = -1;
+               }
                break;
        case 8:         /* Set level of messages printed to console */
                error = -EINVAL;
@@ -384,6 +396,8 @@ int do_syslog(int type, char __user *buf, int len)
                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 */
@@ -412,7 +426,7 @@ 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)))
@@ -544,7 +558,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;
 
@@ -640,6 +654,20 @@ 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;
@@ -649,6 +677,7 @@ asmlinkage int vprintk(const char *fmt, va_list args)
        char *p;
 
        boot_delay_msec();
+       printk_delay();
 
        preempt_disable();
        /* This stops the holder of console_sem just where we want him */
@@ -696,6 +725,8 @@ asmlinkage int vprintk(const char *fmt, va_list args)
                        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;
@@ -1058,12 +1089,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;
@@ -1080,7 +1105,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();
@@ -1095,7 +1120,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);
@@ -1132,25 +1157,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
@@ -1158,13 +1207,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;
                        }
                }
@@ -1176,64 +1225,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 (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) {
+       if (newcon->flags & CON_PRINTBUFFER) {
                /*
                 * release_console_sem() will print out the buffered messages
                 * for us.
@@ -1243,6 +1290,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);
 
@@ -1285,11 +1354,13 @@ 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;
@@ -1306,11 +1377,11 @@ late_initcall(disable_boot_consoles);
  */
 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