Merge branch 'core-printk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6.git] / kernel / printk.c
index e651ab0..b5ac4d9 100644 (file)
 #include <linux/security.h>
 #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, ...)
@@ -60,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.
@@ -73,7 +83,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);
 
@@ -136,6 +145,24 @@ 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 */
 
+#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 size = memparse(str, &str);
@@ -180,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;
@@ -194,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);
@@ -210,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) {
@@ -354,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;
@@ -366,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 */
@@ -382,7 +414,7 @@ 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);
 }
@@ -394,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)))
@@ -526,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;
 
@@ -619,9 +651,23 @@ static int acquire_console_semaphore_for_printk(unsigned int cpu)
 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;
@@ -631,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 */
@@ -669,20 +716,35 @@ asmlinkage int vprintk(const char *fmt, va_list 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++) {
+       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');
@@ -742,11 +804,6 @@ 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)
 {
 }
@@ -896,12 +953,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();
 }
@@ -917,11 +976,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;
 }
@@ -931,6 +988,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;
@@ -984,7 +1045,7 @@ void release_console_sem(void)
        unsigned wake_klogd = 0;
 
        if (console_suspended) {
-               up(&secondary_console_sem);
+               up(&console_sem);
                return;
        }
 
@@ -1028,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;
@@ -1050,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();
@@ -1065,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);
@@ -1102,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
@@ -1128,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;
                        }
                }
@@ -1146,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.
@@ -1213,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);
 
@@ -1255,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;
@@ -1276,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
@@ -1294,8 +1395,11 @@ 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;