Merge tag 'printk-for-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/printk...
[linux-2.6-block.git] / kernel / printk / printk.c
index 1c6e7dfc4ba7941d1ea53e6e81538cab597ddadd..b06f63e276c1f6680975ba26790f8044704d9543 100644 (file)
@@ -347,6 +347,29 @@ static bool panic_in_progress(void)
        return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID);
 }
 
+/* Return true if a panic is in progress on the current CPU. */
+bool this_cpu_in_panic(void)
+{
+       /*
+        * 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 unlikely(atomic_read(&panic_cpu) == raw_smp_processor_id());
+}
+
+/*
+ * 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)
+{
+       return (panic_in_progress() && !this_cpu_in_panic());
+}
+
 /*
  * This is used for debugging the mess that is the VT code by
  * keeping track if we have the console semaphore held. It's
@@ -439,12 +462,6 @@ static int console_msg_format = MSG_FORMAT_DEFAULT;
 static DEFINE_MUTEX(syslog_lock);
 
 #ifdef CONFIG_PRINTK
-/*
- * During panic, heavy printk by other CPUs can delay the
- * panic and risk deadlock on console resources.
- */
-static int __read_mostly suppress_panic_printk;
-
 DECLARE_WAIT_QUEUE_HEAD(log_wait);
 /* All 3 protected by @syslog_lock. */
 /* the next printk record to read by syslog(READ) or /proc/kmsg */
@@ -1835,10 +1852,23 @@ static bool console_waiter;
  */
 static void console_lock_spinning_enable(void)
 {
+       /*
+        * Do not use spinning in panic(). The panic CPU wants to keep the lock.
+        * Non-panic CPUs abandon the flush anyway.
+        *
+        * Just keep the lockdep annotation. The panic-CPU should avoid
+        * taking console_owner_lock because it might cause a deadlock.
+        * This looks like the easiest way how to prevent false lockdep
+        * reports without handling races a lockless way.
+        */
+       if (panic_in_progress())
+               goto lockdep;
+
        raw_spin_lock(&console_owner_lock);
        console_owner = current;
        raw_spin_unlock(&console_owner_lock);
 
+lockdep:
        /* The waiter may spin on us after setting console_owner */
        spin_acquire(&console_owner_dep_map, 0, 0, _THIS_IP_);
 }
@@ -1863,6 +1893,22 @@ static int console_lock_spinning_disable_and_check(int cookie)
 {
        int waiter;
 
+       /*
+        * Ignore spinning waiters during panic() because they might get stopped
+        * or blocked at any time,
+        *
+        * It is safe because nobody is allowed to start spinning during panic
+        * in the first place. If there has been a waiter then non panic CPUs
+        * might stay spinning. They would get stopped anyway. The panic context
+        * will never start spinning and an interrupted spin on panic CPU will
+        * never continue.
+        */
+       if (panic_in_progress()) {
+               /* Keep lockdep happy. */
+               spin_release(&console_owner_dep_map, _THIS_IP_);
+               return 0;
+       }
+
        raw_spin_lock(&console_owner_lock);
        waiter = READ_ONCE(console_waiter);
        console_owner = NULL;
@@ -2259,8 +2305,12 @@ asmlinkage int vprintk_emit(int facility, int level,
        if (unlikely(suppress_printk))
                return 0;
 
-       if (unlikely(suppress_panic_printk) &&
-           atomic_read(&panic_cpu) != raw_smp_processor_id())
+       /*
+        * The messages on the panic CPU are the most important. If
+        * non-panic CPUs are generating any messages, they will be
+        * silently dropped.
+        */
+       if (other_cpu_in_panic())
                return 0;
 
        if (level == LOGLEVEL_SCHED) {
@@ -2590,26 +2640,6 @@ 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
  *
@@ -2765,8 +2795,6 @@ void console_prepend_dropped(struct printk_message *pmsg, unsigned long dropped)
 bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
                             bool is_extended, bool may_suppress)
 {
-       static int panic_console_dropped;
-
        struct printk_buffers *pbufs = pmsg->pbufs;
        const size_t scratchbuf_sz = sizeof(pbufs->scratchbuf);
        const size_t outbuf_sz = sizeof(pbufs->outbuf);
@@ -2794,17 +2822,6 @@ bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
        pmsg->seq = r.info->seq;
        pmsg->dropped = r.info->seq - seq;
 
-       /*
-        * Check for dropped messages in panic here so that printk
-        * suppression can occur as early as possible if necessary.
-        */
-       if (pmsg->dropped &&
-           panic_in_progress() &&
-           panic_console_dropped++ > 10) {
-               suppress_panic_printk = 1;
-               pr_warn_once("Too many dropped messages. Suppress messages on non-panic CPUs to prevent livelock.\n");
-       }
-
        /* Skip record that has level above the console loglevel. */
        if (may_suppress && suppress_message_printing(r.info->level))
                goto out;
@@ -3750,7 +3767,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
 
        might_sleep();
 
-       seq = prb_next_seq(prb);
+       seq = prb_next_reserve_seq(prb);
 
        /* Flush the consoles so that records up to @seq are printed. */
        console_lock();