genirq: fix regression in irqfixup, irqpoll
[linux-2.6.git] / kernel / printk.c
index 2ddbdc7..28a40d8 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/smp.h>
 #include <linux/security.h>
 #include <linux/bootmem.h>
+#include <linux/memblock.h>
 #include <linux/syscalls.h>
 #include <linux/kexec.h>
 #include <linux/kdb.h>
@@ -53,7 +54,7 @@ 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 */
+#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
 
 /* We show everything that is MORE important than this.. */
 #define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
@@ -113,6 +114,11 @@ static unsigned con_start; /* Index into log_buf: next char to be sent to consol
 static unsigned log_end;       /* Index into log_buf: most-recently-written-char + 1 */
 
 /*
+ * If exclusive_console is non-NULL then only this console is to be printed to.
+ */
+static struct console *exclusive_console;
+
+/*
  *     Array of consoles built from command line options (console=)
  */
 struct console_cmdline
@@ -162,46 +168,74 @@ void log_buf_kexec_setup(void)
 }
 #endif
 
+/* requested log_buf_len from kernel cmdline */
+static unsigned long __initdata new_log_buf_len;
+
+/* save requested log_buf_len since it's too early to process it */
 static int __init log_buf_len_setup(char *str)
 {
        unsigned size = memparse(str, &str);
-       unsigned long flags;
 
        if (size)
                size = roundup_pow_of_two(size);
-       if (size > log_buf_len) {
-               unsigned start, dest_idx, offset;
-               char *new_log_buf;
+       if (size > log_buf_len)
+               new_log_buf_len = size;
 
-               new_log_buf = alloc_bootmem(size);
-               if (!new_log_buf) {
-                       printk(KERN_WARNING "log_buf_len: allocation failed\n");
-                       goto out;
-               }
+       return 0;
+}
+early_param("log_buf_len", log_buf_len_setup);
 
-               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);
+void __init setup_log_buf(int early)
+{
+       unsigned long flags;
+       unsigned start, dest_idx, offset;
+       char *new_log_buf;
+       int free;
+
+       if (!new_log_buf_len)
+               return;
+
+       if (early) {
+               unsigned long mem;
 
-               printk(KERN_NOTICE "log_buf_len: %d\n", log_buf_len);
+               mem = memblock_alloc(new_log_buf_len, PAGE_SIZE);
+               if (mem == MEMBLOCK_ERROR)
+                       return;
+               new_log_buf = __va(mem);
+       } else {
+               new_log_buf = alloc_bootmem_nopanic(new_log_buf_len);
        }
-out:
-       return 1;
-}
 
-__setup("log_buf_len=", log_buf_len_setup);
+       if (unlikely(!new_log_buf)) {
+               pr_err("log_buf_len: %ld bytes not available\n",
+                       new_log_buf_len);
+               return;
+       }
+
+       spin_lock_irqsave(&logbuf_lock, flags);
+       log_buf_len = new_log_buf_len;
+       log_buf = new_log_buf;
+       new_log_buf_len = 0;
+       free = __LOG_BUF_LEN - log_end;
+
+       offset = start = min(con_start, log_start);
+       dest_idx = 0;
+       while (start != log_end) {
+               unsigned log_idx_mask = start & (__LOG_BUF_LEN - 1);
+
+               log_buf[dest_idx] = __log_buf[log_idx_mask];
+               start++;
+               dest_idx++;
+       }
+       log_start -= offset;
+       con_start -= offset;
+       log_end -= offset;
+       spin_unlock_irqrestore(&logbuf_lock, flags);
+
+       pr_info("log_buf_len: %d\n", log_buf_len);
+       pr_info("early log buf free: %d(%d%%)\n",
+               free, (free * 100) / __LOG_BUF_LEN);
+}
 
 #ifdef CONFIG_BOOT_PRINTK_DELAY
 
@@ -262,25 +296,49 @@ int dmesg_restrict = 1;
 int dmesg_restrict;
 #endif
 
+static int syslog_action_restricted(int type)
+{
+       if (dmesg_restrict)
+               return 1;
+       /* Unless restricted, we allow "read all" and "get buffer size" for everybody */
+       return type != SYSLOG_ACTION_READ_ALL && type != SYSLOG_ACTION_SIZE_BUFFER;
+}
+
+static int check_syslog_permissions(int type, bool from_file)
+{
+       /*
+        * If this is from /proc/kmsg and we've already opened it, then we've
+        * already done the capabilities checks at open time.
+        */
+       if (from_file && type != SYSLOG_ACTION_OPEN)
+               return 0;
+
+       if (syslog_action_restricted(type)) {
+               if (capable(CAP_SYSLOG))
+                       return 0;
+               /* For historical reasons, accept CAP_SYS_ADMIN too, with a warning */
+               if (capable(CAP_SYS_ADMIN)) {
+                       printk_once(KERN_WARNING "%s (%d): "
+                                "Attempt to access syslog with CAP_SYS_ADMIN "
+                                "but no CAP_SYSLOG (deprecated).\n",
+                                current->comm, task_pid_nr(current));
+                       return 0;
+               }
+               return -EPERM;
+       }
+       return 0;
+}
+
 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;
+       int error;
 
-       /*
-        * If this is from /proc/kmsg we only do the capabilities checks
-        * at open time.
-        */
-       if (type == SYSLOG_ACTION_OPEN || !from_file) {
-               if (dmesg_restrict && !capable(CAP_SYSLOG))
-                       goto warn; /* switch to return -EPERM after 2.6.39 */
-               if ((type != SYSLOG_ACTION_READ_ALL &&
-                    type != SYSLOG_ACTION_SIZE_BUFFER) &&
-                   !capable(CAP_SYSLOG))
-                       goto warn; /* switch to return -EPERM after 2.6.39 */
-       }
+       error = check_syslog_permissions(type, from_file);
+       if (error)
+               goto out;
 
        error = security_syslog(type);
        if (error)
@@ -423,12 +481,6 @@ int do_syslog(int type, char __user *buf, int len, bool from_file)
        }
 out:
        return error;
-warn:
-       /* remove after 2.6.39 */
-       if (capable(CAP_SYS_ADMIN))
-               WARN_ONCE(1, "Attempt to access syslog with CAP_SYS_ADMIN "
-                 "but no CAP_SYSLOG (deprecated and denied).\n");
-       return -EPERM;
 }
 
 SYSCALL_DEFINE3(syslog, int, type, char __user *, buf, int, len)
@@ -460,6 +512,8 @@ static void __call_console_drivers(unsigned start, unsigned end)
        struct console *con;
 
        for_each_console(con) {
+               if (exclusive_console && con != exclusive_console)
+                       continue;
                if ((con->flags & CON_ENABLED) && con->write &&
                                (cpu_online(smp_processor_id()) ||
                                (con->flags & CON_ANYTIME)))
@@ -499,6 +553,71 @@ static void _call_console_drivers(unsigned start,
 }
 
 /*
+ * Parse the syslog header <[0-9]*>. The decimal value represents 32bit, the
+ * lower 3 bit are the log level, the rest are the log facility. In case
+ * userspace passes usual userspace syslog messages to /dev/kmsg or
+ * /dev/ttyprintk, the log prefix might contain the facility. Printk needs
+ * to extract the correct log level for in-kernel processing, and not mangle
+ * the original value.
+ *
+ * If a prefix is found, the length of the prefix is returned. If 'level' is
+ * passed, it will be filled in with the log level without a possible facility
+ * value. If 'special' is passed, the special printk prefix chars are accepted
+ * and returned. If no valid header is found, 0 is returned and the passed
+ * variables are not touched.
+ */
+static size_t log_prefix(const char *p, unsigned int *level, char *special)
+{
+       unsigned int lev = 0;
+       char sp = '\0';
+       size_t len;
+
+       if (p[0] != '<' || !p[1])
+               return 0;
+       if (p[2] == '>') {
+               /* usual single digit level number or special char */
+               switch (p[1]) {
+               case '0' ... '7':
+                       lev = p[1] - '0';
+                       break;
+               case 'c': /* KERN_CONT */
+               case 'd': /* KERN_DEFAULT */
+                       sp = p[1];
+                       break;
+               default:
+                       return 0;
+               }
+               len = 3;
+       } else {
+               /* multi digit including the level and facility number */
+               char *endp = NULL;
+
+               if (p[1] < '0' && p[1] > '9')
+                       return 0;
+
+               lev = (simple_strtoul(&p[1], &endp, 10) & 7);
+               if (endp == NULL || endp[0] != '>')
+                       return 0;
+               len = (endp + 1) - p;
+       }
+
+       /* do not accept special char if not asked for */
+       if (sp && !special)
+               return 0;
+
+       if (special) {
+               *special = sp;
+               /* return special char, do not touch level */
+               if (sp)
+                       return len;
+       }
+
+       if (level)
+               *level = lev;
+       return len;
+}
+
+/*
  * Call the console drivers, asking them to write out
  * log_buf[start] to log_buf[end - 1].
  * The console_lock must be held.
@@ -513,13 +632,9 @@ static void call_console_drivers(unsigned start, unsigned end)
        cur_index = start;
        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';
-                       cur_index += 3;
+               if (msg_level < 0 && ((end - cur_index) > 2)) {
+                       /* strip log prefix */
+                       cur_index += log_prefix(&LOG_BUF(cur_index), &msg_level, NULL);
                        start_print = cur_index;
                }
                while (cur_index != end) {
@@ -669,7 +784,7 @@ static inline int can_use_console(unsigned int cpu)
 static int console_trylock_for_printk(unsigned int cpu)
        __releases(&logbuf_lock)
 {
-       int retval = 0;
+       int retval = 0, wake = 0;
 
        if (console_trylock()) {
                retval = 1;
@@ -682,12 +797,14 @@ static int console_trylock_for_printk(unsigned int cpu)
                 */
                if (!can_use_console(cpu)) {
                        console_locked = 0;
-                       up(&console_sem);
+                       wake = 1;
                        retval = 0;
                }
        }
        printk_cpu = UINT_MAX;
        spin_unlock(&logbuf_lock);
+       if (wake)
+               up(&console_sem);
        return retval;
 }
 static const char recursion_bug_msg [] =
@@ -717,6 +834,8 @@ asmlinkage int vprintk(const char *fmt, va_list args)
        unsigned long flags;
        int this_cpu;
        char *p;
+       size_t plen;
+       char special;
 
        boot_delay_msec();
        printk_delay();
@@ -757,45 +876,52 @@ asmlinkage int vprintk(const char *fmt, va_list args)
        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('\n');
-                                       new_text_line = 1;
-                               }
-                       /* Fallthrough - skip the loglevel */
-                       case 'c': /* KERN_CONT */
-                               p += 3;
-                               break;
+       /* Read log level and handle special printk prefix */
+       plen = log_prefix(p, &current_log_level, &special);
+       if (plen) {
+               p += plen;
+
+               switch (special) {
+               case 'c': /* Strip <c> KERN_CONT, continue line */
+                       plen = 0;
+                       break;
+               case 'd': /* Strip <d> KERN_DEFAULT, start new line */
+                       plen = 0;
+               default:
+                       if (!new_text_line) {
+                               emit_log_char('\n');
+                               new_text_line = 1;
                        }
                }
        }
 
        /*
-        * Copy the output into log_buf.  If the caller didn't provide
-        * appropriate log level tags, we insert them here
+        * Copy the output into log_buf. If the caller didn't provide
+        * the appropriate log prefix, we insert them here
         */
-       for ( ; *p; p++) {
+       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 (plen) {
+                               /* Copy original log prefix */
+                               int i;
+
+                               for (i = 0; i < plen; i++)
+                                       emit_log_char(printk_buf[i]);
+                               printed_len += plen;
+                       } else {
+                               /* Add log prefix */
+                               emit_log_char('<');
+                               emit_log_char(current_log_level + '0');
+                               emit_log_char('>');
+                               printed_len += 3;
+                       }
+
                        if (printk_time) {
-                               /* Follow the token with the time */
+                               /* Add the current time stamp */
                                char tbuf[50], *tp;
                                unsigned tlen;
                                unsigned long long t;
@@ -1120,7 +1246,7 @@ void console_unlock(void)
 {
        unsigned long flags;
        unsigned _con_start, _log_end;
-       unsigned wake_klogd = 0;
+       unsigned wake_klogd = 0, retry = 0;
 
        if (console_suspended) {
                up(&console_sem);
@@ -1129,6 +1255,7 @@ void console_unlock(void)
 
        console_may_schedule = 0;
 
+again:
        for ( ; ; ) {
                spin_lock_irqsave(&logbuf_lock, flags);
                wake_klogd |= log_start - log_end;
@@ -1144,8 +1271,28 @@ void console_unlock(void)
                local_irq_restore(flags);
        }
        console_locked = 0;
+
+       /* Release the exclusive_console once it is used */
+       if (unlikely(exclusive_console))
+               exclusive_console = NULL;
+
+       spin_unlock(&logbuf_lock);
+
        up(&console_sem);
+
+       /*
+        * Someone could have filled up the buffer again, so re-check if there's
+        * something to flush. In case we cannot trylock the console_sem again,
+        * there's a new owner and the console_unlock() from them will do the
+        * flush, no worries.
+        */
+       spin_lock(&logbuf_lock);
+       if (con_start != log_end)
+               retry = 1;
        spin_unlock_irqrestore(&logbuf_lock, flags);
+       if (retry && console_trylock())
+               goto again;
+
        if (wake_klogd)
                wake_up_klogd();
 }
@@ -1230,6 +1377,18 @@ void console_start(struct console *console)
 }
 EXPORT_SYMBOL(console_start);
 
+static int __read_mostly keep_bootcon;
+
+static int __init keep_bootcon_setup(char *str)
+{
+       keep_bootcon = 1;
+       printk(KERN_INFO "debug: skip boot console de-registration.\n");
+
+       return 0;
+}
+
+early_param("keep_bootcon", keep_bootcon_setup);
+
 /*
  * The console driver calls this routine during kernel initialization
  * to register the console printing procedure with printk() and to
@@ -1366,6 +1525,12 @@ void register_console(struct console *newcon)
                spin_lock_irqsave(&logbuf_lock, flags);
                con_start = log_start;
                spin_unlock_irqrestore(&logbuf_lock, flags);
+               /*
+                * We're about to replay the log buffer.  Only do this to the
+                * just-registered console to avoid excessive message spam to
+                * the already-registered consoles.
+                */
+               exclusive_console = newcon;
        }
        console_unlock();
        console_sysfs_notify();
@@ -1377,7 +1542,9 @@ void register_console(struct console *newcon)
         * 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)) {
+       if (bcon &&
+           ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
+           !keep_bootcon) {
                /* we need to iterate through twice, to make sure we print
                 * everything out, before we unregister the console(s)
                 */
@@ -1437,7 +1604,7 @@ static int __init printk_late_init(void)
        struct console *con;
 
        for_each_console(con) {
-               if (con->flags & CON_BOOT) {
+               if (!keep_bootcon && con->flags & CON_BOOT) {
                        printk(KERN_INFO "turn off boot console %s%d\n",
                                con->name, con->index);
                        unregister_console(con);