Merge branch 'rework/misc-cleanups' into for-linus
[linux-2.6-microblaze.git] / kernel / printk / printk.c
index 357a4d1..0b3af15 100644 (file)
@@ -88,7 +88,7 @@ EXPORT_SYMBOL(oops_in_progress);
 static DEFINE_MUTEX(console_mutex);
 
 /*
- * console_sem protects updates to console->seq and console_suspended,
+ * console_sem protects updates to console->seq
  * and also provides serialization for console printing.
  */
 static DEFINE_SEMAPHORE(console_sem, 1);
@@ -361,7 +361,7 @@ static bool panic_in_progress(void)
  * paths in the console code where we end up in places I want
  * locked without the console semaphore held).
  */
-static int console_locked, console_suspended;
+static int console_locked;
 
 /*
  *     Array of consoles built from command line options (console=)
@@ -2308,7 +2308,11 @@ asmlinkage int vprintk_emit(int facility, int level,
                preempt_enable();
        }
 
-       wake_up_klogd();
+       if (in_sched)
+               defer_console_output();
+       else
+               wake_up_klogd();
+
        return printed_len;
 }
 EXPORT_SYMBOL(vprintk_emit);
@@ -2547,22 +2551,46 @@ MODULE_PARM_DESC(console_no_auto_verbose, "Disable console loglevel raise to hig
  */
 void suspend_console(void)
 {
+       struct console *con;
+
        if (!console_suspend_enabled)
                return;
        pr_info("Suspending console(s) (use no_console_suspend to debug)\n");
        pr_flush(1000, true);
-       console_lock();
-       console_suspended = 1;
-       up_console_sem();
+
+       console_list_lock();
+       for_each_console(con)
+               console_srcu_write_flags(con, con->flags | CON_SUSPENDED);
+       console_list_unlock();
+
+       /*
+        * Ensure that all SRCU list walks have completed. All printing
+        * contexts must be able to see that they are suspended so that it
+        * is guaranteed that all printing has stopped when this function
+        * completes.
+        */
+       synchronize_srcu(&console_srcu);
 }
 
 void resume_console(void)
 {
+       struct console *con;
+
        if (!console_suspend_enabled)
                return;
-       down_console_sem();
-       console_suspended = 0;
-       console_unlock();
+
+       console_list_lock();
+       for_each_console(con)
+               console_srcu_write_flags(con, con->flags & ~CON_SUSPENDED);
+       console_list_unlock();
+
+       /*
+        * Ensure that all SRCU list walks have completed. All printing
+        * contexts must be able to see they are no longer suspended so
+        * that they are guaranteed to wake up and resume printing.
+        */
+       synchronize_srcu(&console_srcu);
+
        pr_flush(1000, true);
 }
 
@@ -2585,6 +2613,26 @@ static int console_cpu_notify(unsigned int cpu)
        return 0;
 }
 
+/*
+ * Return true if a panic is in progress on a remote CPU.
+ *
+ * On true, the local CPU should immediately release any printing resources
+ * that may be needed by the panic CPU.
+ */
+bool other_cpu_in_panic(void)
+{
+       if (!panic_in_progress())
+               return false;
+
+       /*
+        * We can use raw_smp_processor_id() here because it is impossible for
+        * the task to be migrated to the panic_cpu, or away from it. If
+        * panic_cpu has already been set, and we're not currently executing on
+        * that CPU, then we never will be.
+        */
+       return atomic_read(&panic_cpu) != raw_smp_processor_id();
+}
+
 /**
  * console_lock - block the console subsystem from printing
  *
@@ -2597,9 +2645,11 @@ void console_lock(void)
 {
        might_sleep();
 
+       /* On panic, the console_lock must be left to the panic cpu. */
+       while (other_cpu_in_panic())
+               msleep(1000);
+
        down_console_sem();
-       if (console_suspended)
-               return;
        console_locked = 1;
        console_may_schedule = 1;
 }
@@ -2615,12 +2665,11 @@ EXPORT_SYMBOL(console_lock);
  */
 int console_trylock(void)
 {
-       if (down_trylock_console_sem())
+       /* On panic, the console_lock must be left to the panic cpu. */
+       if (other_cpu_in_panic())
                return 0;
-       if (console_suspended) {
-               up_console_sem();
+       if (down_trylock_console_sem())
                return 0;
-       }
        console_locked = 1;
        console_may_schedule = 0;
        return 1;
@@ -2633,25 +2682,6 @@ int is_console_locked(void)
 }
 EXPORT_SYMBOL(is_console_locked);
 
-/*
- * Return true when this CPU should unlock console_sem without pushing all
- * messages to the console. This reduces the chance that the console is
- * locked when the panic CPU tries to use it.
- */
-static bool abandon_console_lock_in_panic(void)
-{
-       if (!panic_in_progress())
-               return false;
-
-       /*
-        * We can use raw_smp_processor_id() here because it is impossible for
-        * the task to be migrated to the panic_cpu, or away from it. If
-        * panic_cpu has already been set, and we're not currently executing on
-        * that CPU, then we never will be.
-        */
-       return atomic_read(&panic_cpu) != raw_smp_processor_id();
-}
-
 /*
  * Check if the given console is currently capable and allowed to print
  * records.
@@ -2665,6 +2695,9 @@ static inline bool console_is_usable(struct console *con)
        if (!(flags & CON_ENABLED))
                return false;
 
+       if ((flags & CON_SUSPENDED))
+               return false;
+
        if (!con->write)
                return false;
 
@@ -2948,7 +2981,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove
                        any_progress = true;
 
                        /* Allow panic_cpu to take over the consoles safely. */
-                       if (abandon_console_lock_in_panic())
+                       if (other_cpu_in_panic())
                                goto abandon;
 
                        if (do_cond_resched)
@@ -2983,11 +3016,6 @@ void console_unlock(void)
        bool flushed;
        u64 next_seq;
 
-       if (console_suspended) {
-               up_console_sem();
-               return;
-       }
-
        /*
         * Console drivers are called with interrupts disabled, so
         * @console_may_schedule should be cleared before; however, we may
@@ -3045,9 +3073,27 @@ EXPORT_SYMBOL(console_conditional_schedule);
 
 void console_unblank(void)
 {
+       bool found_unblank = false;
        struct console *c;
        int cookie;
 
+       /*
+        * First check if there are any consoles implementing the unblank()
+        * callback. If not, there is no reason to continue and take the
+        * console lock, which in particular can be dangerous if
+        * @oops_in_progress is set.
+        */
+       cookie = console_srcu_read_lock();
+       for_each_console_srcu(c) {
+               if ((console_srcu_read_flags(c) & CON_ENABLED) && c->unblank) {
+                       found_unblank = true;
+                       break;
+               }
+       }
+       console_srcu_read_unlock(cookie);
+       if (!found_unblank)
+               return;
+
        /*
         * Stop console printing because the unblank() callback may
         * assume the console is not within its write() callback.
@@ -3056,6 +3102,16 @@ void console_unblank(void)
         * In that case, attempt a trylock as best-effort.
         */
        if (oops_in_progress) {
+               /* Semaphores are not NMI-safe. */
+               if (in_nmi())
+                       return;
+
+               /*
+                * Attempting to trylock the console lock can deadlock
+                * if another CPU was stopped while modifying the
+                * semaphore. "Hope and pray" that this is not the
+                * current situation.
+                */
                if (down_trylock_console_sem() != 0)
                        return;
        } else
@@ -3085,14 +3141,24 @@ void console_unblank(void)
  */
 void console_flush_on_panic(enum con_flush_mode mode)
 {
+       bool handover;
+       u64 next_seq;
+
+       /*
+        * Ignore the console lock and flush out the messages. Attempting a
+        * trylock would not be useful because:
+        *
+        *   - if it is contended, it must be ignored anyway
+        *   - console_lock() and console_trylock() block and fail
+        *     respectively in panic for non-panic CPUs
+        *   - semaphores are not NMI-safe
+        */
+
        /*
-        * If someone else is holding the console lock, trylock will fail
-        * and may_schedule may be set.  Ignore and proceed to unlock so
-        * that messages are flushed out.  As this can be called from any
-        * context and we don't want to get preempted while flushing,
-        * ensure may_schedule is cleared.
+        * If another context is holding the console lock,
+        * @console_may_schedule might be set. Clear it so that
+        * this context does not call cond_resched() while flushing.
         */
-       console_trylock();
        console_may_schedule = 0;
 
        if (mode == CONSOLE_REPLAY_ALL) {
@@ -3105,15 +3171,15 @@ void console_flush_on_panic(enum con_flush_mode mode)
                cookie = console_srcu_read_lock();
                for_each_console_srcu(c) {
                        /*
-                        * If the above console_trylock() failed, this is an
-                        * unsynchronized assignment. But in that case, the
+                        * This is an unsynchronized assignment, but the
                         * kernel is in "hope and pray" mode anyway.
                         */
                        c->seq = seq;
                }
                console_srcu_read_unlock(cookie);
        }
-       console_unlock();
+
+       console_flush_all(false, &next_seq, &handover);
 }
 
 /*
@@ -3674,13 +3740,18 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
 
        seq = prb_next_seq(prb);
 
+       /* Flush the consoles so that records up to @seq are printed. */
+       console_lock();
+       console_unlock();
+
        for (;;) {
                diff = 0;
 
                /*
                 * Hold the console_lock to guarantee safe access to
-                * console->seq and to prevent changes to @console_suspended
-                * until all consoles have been processed.
+                * console->seq. Releasing console_lock flushes more
+                * records in case @seq is still not printed on all
+                * usable consoles.
                 */
                console_lock();
 
@@ -3688,6 +3759,11 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
                for_each_console_srcu(c) {
                        if (con && con != c)
                                continue;
+                       /*
+                        * If consoles are not usable, it cannot be expected
+                        * that they make forward progress, so only increment
+                        * @diff for usable consoles.
+                        */
                        if (!console_is_usable(c))
                                continue;
                        printk_seq = c->seq;
@@ -3696,18 +3772,12 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
                }
                console_srcu_read_unlock(cookie);
 
-               /*
-                * If consoles are suspended, it cannot be expected that they
-                * make forward progress, so timeout immediately. @diff is
-                * still used to return a valid flush status.
-                */
-               if (console_suspended)
-                       remaining = 0;
-               else if (diff != last_diff && reset_on_progress)
+               if (diff != last_diff && reset_on_progress)
                        remaining = timeout_ms;
 
                console_unlock();
 
+               /* Note: @diff is 0 if there are no usable consoles. */
                if (diff == 0 || remaining == 0)
                        break;
 
@@ -3741,7 +3811,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
  * printer has been seen to make some forward progress.
  *
  * Context: Process context. May sleep while acquiring console lock.
- * Return: true if all enabled printers are caught up.
+ * Return: true if all usable printers are caught up.
  */
 static bool pr_flush(int timeout_ms, bool reset_on_progress)
 {
@@ -3798,11 +3868,33 @@ static void __wake_up_klogd(int val)
        preempt_enable();
 }
 
+/**
+ * wake_up_klogd - Wake kernel logging daemon
+ *
+ * Use this function when new records have been added to the ringbuffer
+ * and the console printing of those records has already occurred or is
+ * known to be handled by some other context. This function will only
+ * wake the logging daemon.
+ *
+ * Context: Any context.
+ */
 void wake_up_klogd(void)
 {
        __wake_up_klogd(PRINTK_PENDING_WAKEUP);
 }
 
+/**
+ * defer_console_output - Wake kernel logging daemon and trigger
+ *     console printing in a deferred context
+ *
+ * Use this function when new records have been added to the ringbuffer,
+ * this context is responsible for console printing those records, but
+ * the current context is not allowed to perform the console printing.
+ * Trigger an irq_work context to perform the console printing. This
+ * function also wakes the logging daemon.
+ *
+ * Context: Any context.
+ */
 void defer_console_output(void)
 {
        /*
@@ -3819,12 +3911,7 @@ void printk_trigger_flush(void)
 
 int vprintk_deferred(const char *fmt, va_list args)
 {
-       int r;
-
-       r = vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args);
-       defer_console_output();
-
-       return r;
+       return vprintk_emit(0, LOGLEVEL_SCHED, NULL, fmt, args);
 }
 
 int _printk_deferred(const char *fmt, ...)