printk: Add function to replay kernel log on consoles
authorSreenath Vijayan <sreenath.vijayan@sony.com>
Wed, 13 Mar 2024 10:20:52 +0000 (15:50 +0530)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 11 Apr 2024 12:22:52 +0000 (14:22 +0200)
Add a generic function console_replay_all() for replaying
the kernel log on consoles, in any context. It would allow
viewing the logs on an unresponsive terminal via sysrq.

Reuse the existing code from console_flush_on_panic() for
resetting the sequence numbers, by introducing a new helper
function __console_rewind_all(). It is safe to be called
under console_lock().

Try to acquire lock on the console subsystem without waiting.
If successful, reset the sequence number to oldest available
record on all consoles and call console_unlock() which will
automatically flush the messages to the consoles.

Suggested-by: John Ogness <john.ogness@linutronix.de>
Suggested-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Shimoyashiki Taichi <taichi.shimoyashiki@sony.com>
Reviewed-by: John Ogness <john.ogness@linutronix.de>
Signed-off-by: Sreenath Vijayan <sreenath.vijayan@sony.com>
Link: https://lore.kernel.org/r/90ee131c643a5033d117b556c0792de65129d4c3.1710220326.git.sreenath.vijayan@sony.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
include/linux/printk.h
kernel/printk/printk.c

index 5e038e78225650b6247e2a4a0785c0545954835a..1c5936ab991fe70f5d0b407c83d724baf2bdf040 100644 (file)
@@ -195,6 +195,7 @@ void show_regs_print_info(const char *log_lvl);
 extern asmlinkage void dump_stack_lvl(const char *log_lvl) __cold;
 extern asmlinkage void dump_stack(void) __cold;
 void printk_trigger_flush(void);
+void console_replay_all(void);
 #else
 static inline __printf(1, 0)
 int vprintk(const char *s, va_list args)
@@ -274,6 +275,9 @@ static inline void dump_stack(void)
 static inline void printk_trigger_flush(void)
 {
 }
+static inline void console_replay_all(void)
+{
+}
 #endif
 
 bool this_cpu_in_panic(void);
index 1e3b216825472e6f62d37a94f79ea65758c889ee..6206804d275bacfc20da441e6021cc7c92f85cff 100644 (file)
@@ -3161,6 +3161,40 @@ void console_unblank(void)
                pr_flush(1000, true);
 }
 
+/*
+ * Rewind all consoles to the oldest available record.
+ *
+ * IMPORTANT: The function is safe only when called under
+ *            console_lock(). It is not enforced because
+ *            it is used as a best effort in panic().
+ */
+static void __console_rewind_all(void)
+{
+       struct console *c;
+       short flags;
+       int cookie;
+       u64 seq;
+
+       seq = prb_first_valid_seq(prb);
+
+       cookie = console_srcu_read_lock();
+       for_each_console_srcu(c) {
+               flags = console_srcu_read_flags(c);
+
+               if (flags & CON_NBCON) {
+                       nbcon_seq_force(c, seq);
+               } else {
+                       /*
+                        * This assignment is safe only when called under
+                        * console_lock(). On panic, legacy consoles are
+                        * only best effort.
+                        */
+                       c->seq = seq;
+               }
+       }
+       console_srcu_read_unlock(cookie);
+}
+
 /**
  * console_flush_on_panic - flush console content on panic
  * @mode: flush all messages in buffer or just the pending ones
@@ -3189,30 +3223,8 @@ void console_flush_on_panic(enum con_flush_mode mode)
         */
        console_may_schedule = 0;
 
-       if (mode == CONSOLE_REPLAY_ALL) {
-               struct console *c;
-               short flags;
-               int cookie;
-               u64 seq;
-
-               seq = prb_first_valid_seq(prb);
-
-               cookie = console_srcu_read_lock();
-               for_each_console_srcu(c) {
-                       flags = console_srcu_read_flags(c);
-
-                       if (flags & CON_NBCON) {
-                               nbcon_seq_force(c, seq);
-                       } else {
-                               /*
-                                * This is an unsynchronized assignment. On
-                                * panic legacy consoles are only best effort.
-                                */
-                               c->seq = seq;
-                       }
-               }
-               console_srcu_read_unlock(cookie);
-       }
+       if (mode == CONSOLE_REPLAY_ALL)
+               __console_rewind_all();
 
        console_flush_all(false, &next_seq, &handover);
 }
@@ -4301,6 +4313,23 @@ void kmsg_dump_rewind(struct kmsg_dump_iter *iter)
 }
 EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
 
+/**
+ * console_replay_all - replay kernel log on consoles
+ *
+ * Try to obtain lock on console subsystem and replay all
+ * available records in printk buffer on the consoles.
+ * Does nothing if lock is not obtained.
+ *
+ * Context: Any context.
+ */
+void console_replay_all(void)
+{
+       if (console_trylock()) {
+               __console_rewind_all();
+               /* Consoles are flushed as part of console_unlock(). */
+               console_unlock();
+       }
+}
 #endif
 
 #ifdef CONFIG_SMP