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