Merge branch 'upstream' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik/misc-2.6
[sfrench/cifs-2.6.git] / kernel / printk.c
index 01b58d7d17ff763d50f92cb80b66e8ffe67dad4f..a967605bc2e358cbcf95dd7fe60f7870fe9dd14b 100644 (file)
@@ -514,6 +514,9 @@ asmlinkage int printk(const char *fmt, ...)
        return r;
 }
 
+/* cpu currently holding logbuf_lock */
+static volatile unsigned int printk_cpu = UINT_MAX;
+
 asmlinkage int vprintk(const char *fmt, va_list args)
 {
        unsigned long flags;
@@ -522,11 +525,15 @@ asmlinkage int vprintk(const char *fmt, va_list args)
        static char printk_buf[1024];
        static int log_level_unknown = 1;
 
-       if (unlikely(oops_in_progress))
+       preempt_disable();
+       if (unlikely(oops_in_progress) && printk_cpu == smp_processor_id())
+               /* If a crash is occurring during printk() on this CPU,
+                * make sure we can't deadlock */
                zap_locks();
 
        /* This stops the holder of console_sem just where we want him */
        spin_lock_irqsave(&logbuf_lock, flags);
+       printk_cpu = smp_processor_id();
 
        /* Emit the output into the temporary buffer */
        printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args);
@@ -588,14 +595,14 @@ asmlinkage int vprintk(const char *fmt, va_list args)
                        log_level_unknown = 1;
        }
 
-       if (!cpu_online(smp_processor_id()) &&
-           system_state != SYSTEM_RUNNING) {
+       if (!cpu_online(smp_processor_id())) {
                /*
                 * Some console drivers may assume that per-cpu resources have
                 * been allocated.  So don't allow them to be called by this
                 * CPU until it is officially up.  We shouldn't be calling into
                 * random console drivers on a CPU which doesn't exist yet..
                 */
+               printk_cpu = UINT_MAX;
                spin_unlock_irqrestore(&logbuf_lock, flags);
                goto out;
        }
@@ -605,6 +612,7 @@ asmlinkage int vprintk(const char *fmt, va_list args)
                 * We own the drivers.  We can drop the spinlock and let
                 * release_console_sem() print the text
                 */
+               printk_cpu = UINT_MAX;
                spin_unlock_irqrestore(&logbuf_lock, flags);
                console_may_schedule = 0;
                release_console_sem();
@@ -614,9 +622,11 @@ asmlinkage int vprintk(const char *fmt, va_list args)
                 * 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_irqrestore(&logbuf_lock, flags);
        }
 out:
+       preempt_enable();
        return printed_len;
 }
 EXPORT_SYMBOL(printk);
@@ -876,8 +886,10 @@ void register_console(struct console * console)
                        break;
                console->flags |= CON_ENABLED;
                console->index = console_cmdline[i].index;
-               if (i == preferred_console)
+               if (i == selected_console) {
                        console->flags |= CON_CONSDEV;
+                       preferred_console = selected_console;
+               }
                break;
        }
 
@@ -897,6 +909,8 @@ void register_console(struct console * console)
        if ((console->flags & CON_CONSDEV) || console_drivers == NULL) {
                console->next = console_drivers;
                console_drivers = console;
+               if (console->next)
+                       console->next->flags &= ~CON_CONSDEV;
        } else {
                console->next = console_drivers->next;
                console_drivers->next = console;
@@ -937,10 +951,14 @@ int unregister_console(struct console * console)
        /* If last console is removed, we re-enable picking the first
         * one that gets registered. Without that, pmac early boot console
         * would prevent fbcon from taking over.
+        *
+        * If this isn't the last console and it has CON_CONSDEV set, we
+        * need to set it on the next preferred console.
         */
        if (console_drivers == NULL)
                preferred_console = selected_console;
-               
+       else if (console->flags & CON_CONSDEV)
+               console_drivers->flags |= CON_CONSDEV;
 
        release_console_sem();
        return res;