[PATCH v2] tty/sysrq: Dump printk ring buffer messages via sysrq

From: Sreenath Vijayan
Date: Wed Jan 10 2024 - 03:30:48 EST


When terminal is unresponsive, one cannot use dmesg to view printk
ring buffer messages. Also, syslog services may be disabled,
to check them after a reboot, especially on embedded systems.
In this scenario, dump the printk ring buffer messages via sysrq
by pressing sysrq+D.

Signed-off-by: Sreenath Vijayan <sreenath.vijayan@xxxxxxxx>
Signed-off-by: Shimoyashiki Taichi <taichi.shimoyashiki@xxxxxxxx>
---
V2: - modified kernel ring buffer to printk ring buffer
- allocated buf dynamically to prevent stack frame size warnings
- used buf of size 2048 to match PRINTK_MESSAGE_MAX and added comment

Documentation/admin-guide/sysrq.rst | 2 ++
drivers/tty/sysrq.c | 49 ++++++++++++++++++++++++++++-
2 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/Documentation/admin-guide/sysrq.rst b/Documentation/admin-guide/sysrq.rst
index 2f2e5bd440f9..c634e8b4cea2 100644
--- a/Documentation/admin-guide/sysrq.rst
+++ b/Documentation/admin-guide/sysrq.rst
@@ -161,6 +161,8 @@ Command Function
will be printed to your console. (``0``, for example would make
it so that only emergency messages like PANICs or OOPSes would
make it to your console.)
+
+``D`` Dump the printk ring buffer
=========== ===================================================================

Okay, so what can I use them for?
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index 02217e3c916b..62b3911f03b5 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -51,6 +51,8 @@
#include <linux/syscalls.h>
#include <linux/of.h>
#include <linux/rcupdate.h>
+#include <linux/kmsg_dump.h>
+#include <linux/console.h>

#include <asm/ptrace.h>
#include <asm/irq_regs.h>
@@ -450,6 +452,51 @@ static const struct sysrq_key_op sysrq_unrt_op = {
.enable_mask = SYSRQ_ENABLE_RTNICE,
};

+static void dmesg_dump_callback(struct work_struct *work)
+{
+ struct kmsg_dump_iter iter;
+ size_t len;
+ char *buf;
+ struct console *con;
+ int cookie;
+
+ /* Size to be updated if PRINTK_MESSAGE_MAX changes */
+ buf = kzalloc(2048, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ kmsg_dump_rewind(&iter);
+ while (kmsg_dump_get_line(&iter, 1, buf, 2048, &len)) {
+ /*
+ * Since using printk() or pr_*() will append the message to the
+ * printk ring buffer, they cannot be used to display the retrieved
+ * message. Hence console_write() of serial drivers is used.
+ */
+ console_lock();
+ cookie = console_srcu_read_lock();
+ for_each_console_srcu(con) {
+ if ((console_srcu_read_flags(con) & CON_ENABLED) && con->write)
+ con->write(con, buf, len);
+ }
+ console_srcu_read_unlock(cookie);
+ console_unlock();
+ }
+ kfree(buf);
+}
+
+static DECLARE_WORK(sysrq_dmesg_work, dmesg_dump_callback);
+
+static void sysrq_handle_dmesg_dump(u8 key)
+{
+ queue_work(system_unbound_wq, &sysrq_dmesg_work);
+}
+static struct sysrq_key_op sysrq_dmesg_dump_op = {
+ .handler = sysrq_handle_dmesg_dump,
+ .help_msg = "dump-dmesg(D)",
+ .action_msg = "Dump dmesg",
+ .enable_mask = SYSRQ_ENABLE_DUMP,
+};
+
/* Key Operations table and lock */
static DEFINE_SPINLOCK(sysrq_key_table_lock);

@@ -505,7 +552,7 @@ static const struct sysrq_key_op *sysrq_key_table[62] = {
NULL, /* A */
NULL, /* B */
NULL, /* C */
- NULL, /* D */
+ &sysrq_dmesg_dump_op, /* D */
NULL, /* E */
NULL, /* F */
NULL, /* G */
--
2.43.0