[PATCH 25/30] panic, printk: Add console flush parameter and convert panic_print to a notifier

Guilherme G. Piccoli
2022-04-27T23:02:23Z

Currently the parameter "panic_print" relies in a function called
directly on panic path; one of the flags the users can set for
panic_print triggers a console replay mechanism, to show the
entire kernel log buffer (from the beginning) in a panic event.

Two problems with that: the dual nature of the panic_print
isn't really appropriate, the function was originally meant
to allow users dumping system information on panic events,
and was "overridden" to also force a console flush of the full
kernel log buffer. It also turns the code a bit more complex
and duplicate than it needs to be.

This patch proposes 2 changes: first, we decouple panic_print
from the console flushing mechanism, in the form of a new kernel
core parameter (panic_console_replay); we kept the functionality
on panic_print to avoid userspace breakage, although we comment
in both code and documentation that this panic_print usage is
deprecated.

We converted panic_print function to a panic notifier too, adding
it on the panic informational notifier list, executed as the final
callback. This allows a more clear code and makes sense, as
panic_print_sys_info() is really a panic-time only function.
We also moved its code to kernel/printk.c, it seems to make more
sense given it's related to printing stuff.

Suggested-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Guilherme G. Piccoli <gpiccoli@igalia.com>
---
.../admin-guide/kernel-parameters.txt | 12 +++-
Documentation/admin-guide/sysctl/kernel.rst | 5 +-
include/linux/console.h | 2 +
include/linux/panic.h | 1 -
include/linux/printk.h | 1 +
kernel/panic.c | 51 +++------------
kernel/printk/printk.c | 62 +++++++++++++++++++
7 files changed, 87 insertions(+), 47 deletions(-)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 8d3524060ce3..c99da8b2b216 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -3791,6 +3791,14 @@
timeout < 0: reboot immediately
Format: <timeout>

+ panic_console_replay
+ [KNL] Force a kernel log replay in the console on
+ panic event. Notice that there is already a flush
+ mechanism for pending messages; this option is meant
+ for users that wish to replay the *full* buffer.
+ It deprecates the bit 5 setting on "panic_print",
+ both having the same functionality.
+
panic_notifiers_level=
[KNL] Set the panic notifiers execution order.
Format: <unsigned int>
@@ -3825,12 +3833,14 @@
bit 2: print timer info
bit 3: print locks info if CONFIG_LOCKDEP is on
bit 4: print ftrace buffer
- bit 5: print all printk messages in buffer
+ bit 5: print all printk messages in buffer (DEPRECATED)
bit 6: print all CPUs backtrace (if available in the arch)
*Be aware* that this option may print a _lot_ of lines,
so there are risks of losing older messages in the log.
Use this option carefully, maybe worth to setup a
bigger log buffer with "log_buf_len" along with this.
+ Also, notice that bit 5 was deprecated in favor of the
+ parameter "panic_console_replay".

panic_on_taint= Bitmask for conditionally calling panic() in add_taint()
Format: <hex>[,nousertaint]
diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst
index 1144ea3229a3..17b293a0e566 100644
--- a/Documentation/admin-guide/sysctl/kernel.rst
+++ b/Documentation/admin-guide/sysctl/kernel.rst
@@ -763,10 +763,13 @@ bit 1 print system memory info
bit 2 print timer info
bit 3 print locks info if ``CONFIG_LOCKDEP`` is on
bit 4 print ftrace buffer
-bit 5 print all printk messages in buffer
+bit 5 print all printk messages in buffer (DEPRECATED)
bit 6 print all CPUs backtrace (if available in the arch)
===== ============================================

+Notice that bit 5 was deprecated in favor of kernel core parameter
+"panic_console_replay" (see kernel-parameters.txt documentation).
+
So for example to print tasks and memory info on panic, user can::

echo 3 > /proc/sys/kernel/panic_print
diff --git a/include/linux/console.h b/include/linux/console.h
index 7cd758a4f44e..351c14f623ad 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -169,6 +169,8 @@ enum con_flush_mode {
CONSOLE_REPLAY_ALL,
};

+extern bool panic_console_replay;
+
extern int add_preferred_console(char *name, int idx, char *options);
extern void register_console(struct console *);
extern int unregister_console(struct console *);
diff --git a/include/linux/panic.h b/include/linux/panic.h
index f5844908a089..34175d0188d0 100644
--- a/include/linux/panic.h
+++ b/include/linux/panic.h
@@ -22,7 +22,6 @@ extern unsigned int sysctl_oops_all_cpu_backtrace;
#endif /* CONFIG_SMP */

extern int panic_timeout;
-extern unsigned long panic_print;
extern int panic_on_oops;
extern int panic_on_unrecovered_nmi;
extern int panic_on_io_nmi;
diff --git a/include/linux/printk.h b/include/linux/printk.h
index 1522df223c0f..aee2e8ebd541 100644
--- a/include/linux/printk.h
+++ b/include/linux/printk.h
@@ -13,6 +13,7 @@
extern const char linux_banner[];
extern const char linux_proc_banner[];

+extern unsigned long panic_print;
extern int oops_in_progress; /* If set, an oops, panic(), BUG() or die() is in progress */

#define PRINTK_MAX_SINGLE_HEADER_LEN 2
diff --git a/kernel/panic.c b/kernel/panic.c
index b7c055d4f87f..ff257bd8f81b 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -68,15 +68,6 @@ unsigned int panic_notifiers_level = 2;
/* DEPRECATED in favor of panic_notifiers_level */
bool crash_kexec_post_notifiers;

-#define PANIC_PRINT_TASK_INFO 0x00000001
-#define PANIC_PRINT_MEM_INFO 0x00000002
-#define PANIC_PRINT_TIMER_INFO 0x00000004
-#define PANIC_PRINT_LOCK_INFO 0x00000008
-#define PANIC_PRINT_FTRACE_INFO 0x00000010
-#define PANIC_PRINT_ALL_PRINTK_MSG 0x00000020
-#define PANIC_PRINT_ALL_CPU_BT 0x00000040
-unsigned long panic_print;
-
ATOMIC_NOTIFIER_HEAD(panic_hypervisor_list);
EXPORT_SYMBOL(panic_hypervisor_list);

@@ -168,33 +159,6 @@ void nmi_panic(struct pt_regs *regs, const char *msg)
}
EXPORT_SYMBOL(nmi_panic);

-static void panic_print_sys_info(bool console_flush)
-{
- if (console_flush) {
- if (panic_print & PANIC_PRINT_ALL_PRINTK_MSG)
- console_flush_on_panic(CONSOLE_REPLAY_ALL);
- return;
- }
-
- if (panic_print & PANIC_PRINT_ALL_CPU_BT)
- trigger_all_cpu_backtrace();
-
- if (panic_print & PANIC_PRINT_TASK_INFO)
- show_state();
-
- if (panic_print & PANIC_PRINT_MEM_INFO)
- show_mem(0, NULL);
-
- if (panic_print & PANIC_PRINT_TIMER_INFO)
- sysrq_timer_list_show();
-
- if (panic_print & PANIC_PRINT_LOCK_INFO)
- debug_show_all_locks();
-
- if (panic_print & PANIC_PRINT_FTRACE_INFO)
- ftrace_dump(DUMP_ALL);
-}
-
/*
* Helper that accumulates all console flushing routines executed on panic.
*/
@@ -218,7 +182,11 @@ static void console_flushing(void)
debug_locks_off();
console_flush_on_panic(CONSOLE_FLUSH_PENDING);

- panic_print_sys_info(true);
+ /* In case users wish to replay the full log buffer... */
+ if (panic_console_replay) {
+ pr_warn("Replaying the log buffer from the beginning\n");
+ console_flush_on_panic(CONSOLE_REPLAY_ALL);
+ }
}

#define PN_HYPERVISOR_BIT 0
@@ -431,10 +399,8 @@ void panic(const char *fmt, ...)
crash_smp_send_stop();
panic_notifier_hypervisor_once(buf);

- if (panic_notifier_info_once(buf)) {
- panic_print_sys_info(false);
+ if (panic_notifier_info_once(buf))
kmsg_dump(KMSG_DUMP_PANIC);
- }

panic_notifier_pre_reboot_once(buf);

@@ -442,10 +408,8 @@ void panic(const char *fmt, ...)

panic_notifier_hypervisor_once(buf);

- if (panic_notifier_info_once(buf)) {
- panic_print_sys_info(false);
+ if (panic_notifier_info_once(buf))
kmsg_dump(KMSG_DUMP_PANIC);
- }

panic_notifier_pre_reboot_once(buf);

@@ -814,7 +778,6 @@ EXPORT_SYMBOL(__stack_chk_fail);
#endif

core_param(panic, panic_timeout, int, 0644);
-core_param(panic_print, panic_print, ulong, 0644);
core_param(pause_on_oops, pause_on_oops, int, 0644);
core_param(panic_on_warn, panic_on_warn, int, 0644);
core_param(panic_notifiers_level, panic_notifiers_level, uint, 0644);
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index e3a1c429fbbc..ad91d4c04246 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -35,6 +35,7 @@
#include <linux/memblock.h>
#include <linux/syscalls.h>
#include <linux/crash_core.h>
+#include <linux/panic_notifier.h>
#include <linux/ratelimit.h>
#include <linux/kmsg_dump.h>
#include <linux/syslog.h>
@@ -3234,6 +3235,61 @@ void __init console_init(void)
}
}

+#define PANIC_PRINT_TASK_INFO 0x00000001
+#define PANIC_PRINT_MEM_INFO 0x00000002
+#define PANIC_PRINT_TIMER_INFO 0x00000004
+#define PANIC_PRINT_LOCK_INFO 0x00000008
+#define PANIC_PRINT_FTRACE_INFO 0x00000010
+
+/* DEPRECATED - please use "panic_console_replay" */
+#define PANIC_PRINT_ALL_PRINTK_MSG 0x00000020
+
+#define PANIC_PRINT_ALL_CPU_BT 0x00000040
+
+unsigned long panic_print;
+bool panic_console_replay;
+
+static int panic_print_sys_info(struct notifier_block *self,
+ unsigned long ev, void *unused)
+{
+ if (panic_print & PANIC_PRINT_ALL_CPU_BT)
+ trigger_all_cpu_backtrace();
+
+ if (panic_print & PANIC_PRINT_TASK_INFO)
+ show_state();
+
+ if (panic_print & PANIC_PRINT_MEM_INFO)
+ show_mem(0, NULL);
+
+ if (panic_print & PANIC_PRINT_TIMER_INFO)
+ sysrq_timer_list_show();
+
+ if (panic_print & PANIC_PRINT_LOCK_INFO)
+ debug_show_all_locks();
+
+ if (panic_print & PANIC_PRINT_FTRACE_INFO)
+ ftrace_dump(DUMP_ALL);
+
+ /*
+ * This is legacy/deprecated feature from panic_print,
+ * the console force flushing. We have now the parameter
+ * "panic_console_replay", but we need to keep the
+ * retro-compatibility with the old stuff...
+ */
+ if (panic_print & PANIC_PRINT_ALL_PRINTK_MSG)
+ panic_console_replay = true;
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_print_nb = {
+ .notifier_call = panic_print_sys_info,
+ .priority = INT_MIN, /* defer to run as late as possible */
+};
+
+core_param(panic_print, panic_print, ulong, 0644);
+core_param(panic_console_replay, panic_console_replay, bool, 0644);
+
/*
* Some boot consoles access data that is in the init section and which will
* be discarded after the initcalls have been run. To make sure that no code
@@ -3253,6 +3309,12 @@ static int __init printk_late_init(void)
struct console *con;
int ret;

+ /*
+ * Register the panic notifier to print user information
+ * in case the user have that set.
+ */
+ atomic_notifier_chain_register(&panic_info_list, &panic_print_nb);
+
for_each_console(con) {
if (!(con->flags & CON_BOOT))
continue;
--
2.36.0