]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
panic: add 'panic_sys_info' sysctl to take human readable string parameter
authorFeng Tang <feng.tang@linux.alibaba.com>
Thu, 3 Jul 2025 02:10:02 +0000 (10:10 +0800)
committerAndrew Morton <akpm@linux-foundation.org>
Sun, 20 Jul 2025 02:08:24 +0000 (19:08 -0700)
Bitmap definition for 'panic_print' is hard to remember and decode.  Add
'panic_sys_info='sysctl to take human readable string like
"tasks,mem,timers,locks,ftrace,..." and translate it into bitmap.

The detailed mapping is:
SYS_INFO_TASKS "tasks"
SYS_INFO_MEM "mem"
SYS_INFO_TIMERS "timers"
SYS_INFO_LOCKS "locks"
SYS_INFO_FTRACE "ftrace"
SYS_INFO_ALL_CPU_BT "all_bt"
SYS_INFO_BLOCKED_TASKS "blocked_tasks"

[nathan@kernel.org: add __maybe_unused to sys_info_avail]
Link: https://lkml.kernel.org/r/20250708-fix-clang-sys_info_avail-warning-v1-1-60d239eacd64@kernel.org
Link: https://lkml.kernel.org/r/20250703021004.42328-4-feng.tang@linux.alibaba.com
Signed-off-by: Feng Tang <feng.tang@linux.alibaba.com>
Suggested-by: Petr Mladek <pmladek@suse.com>
Cc: John Ogness <john.ogness@linutronix.de>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Lance Yang <lance.yang@linux.dev>
Cc: "Paul E . McKenney" <paulmck@kernel.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Documentation/admin-guide/sysctl/kernel.rst
include/linux/sys_info.h
kernel/panic.c
lib/sys_info.c

index 0d08b7a2db2da9298b9353e61e4278065ca27bd9..cccb06d1a6bf4c305c85a3bbf29affdb1e53bb8c 100644 (file)
@@ -899,6 +899,24 @@ So for example to print tasks and memory info on panic, user can::
   echo 3 > /proc/sys/kernel/panic_print
 
 
+panic_sys_info
+==============
+
+A comma separated list of extra information to be dumped on panic,
+for example, "tasks,mem,timers,...".  It is a human readable alternative
+to 'panic_print'. Possible values are:
+
+=============   ===================================================
+tasks           print all tasks info
+mem             print system memory info
+timer           print timers info
+lock            print locks info if CONFIG_LOCKDEP is on
+ftrace          print ftrace buffer
+all_bt          print all CPUs backtrace (if available in the arch)
+blocked_tasks   print only tasks in uninterruptible (blocked) state
+=============   ===================================================
+
+
 panic_on_rcu_stall
 ==================
 
index 53b7e27dbf2afadd5a7e8af876f440568a8dafd3..89d77dc4f2ed2d3d5a38b1f374eebb7bbd6d0e25 100644 (file)
@@ -2,6 +2,8 @@
 #ifndef _LINUX_SYS_INFO_H
 #define _LINUX_SYS_INFO_H
 
+#include <linux/sysctl.h>
+
 /*
  * SYS_INFO_PANIC_CONSOLE_REPLAY is for panic case only, as it needs special
  * handling which only fits panic case.
 #define SYS_INFO_BLOCKED_TASKS         0x00000080
 
 void sys_info(unsigned long si_mask);
+unsigned long sys_info_parse_param(char *str);
 
+#ifdef CONFIG_SYSCTL
+int sysctl_sys_info_handler(const struct ctl_table *ro_table, int write,
+                                         void *buffer, size_t *lenp,
+                                         loff_t *ppos);
+#endif
 #endif /* _LINUX_SYS_INFO_H */
index cbb0681177b345b727b0c1c46ce675e77dfac634..d7aa427dc23c60c0a2b718fc68d566c2e355bfdd 100644 (file)
@@ -126,6 +126,13 @@ static const struct ctl_table kern_panic_table[] = {
                .mode           = 0644,
                .proc_handler   = proc_douintvec,
        },
+       {
+               .procname       = "panic_sys_info",
+               .data           = &panic_print,
+               .maxlen         = sizeof(panic_print),
+               .mode           = 0644,
+               .proc_handler   = sysctl_sys_info_handler,
+       },
 };
 
 static __init int kernel_panic_sysctls_init(void)
index 53031e5cb98e7aa4a99d9fcd4a173da03230466d..5bf503fd7ec14b31ffba0738a0e907140a5c0eed 100644 (file)
 #include <linux/console.h>
 #include <linux/kernel.h>
 #include <linux/ftrace.h>
+#include <linux/sysctl.h>
 #include <linux/nmi.h>
 
 #include <linux/sys_info.h>
 
+struct sys_info_name {
+       unsigned long bit;
+       const char *name;
+};
+
+/*
+ * When 'si_names' gets updated,  please make sure the 'sys_info_avail'
+ * below is updated accordingly.
+ */
+static const struct sys_info_name  si_names[] = {
+       { SYS_INFO_TASKS,               "tasks" },
+       { SYS_INFO_MEM,                 "mem" },
+       { SYS_INFO_TIMERS,              "timers" },
+       { SYS_INFO_LOCKS,               "locks" },
+       { SYS_INFO_FTRACE,              "ftrace" },
+       { SYS_INFO_ALL_CPU_BT,          "all_bt" },
+       { SYS_INFO_BLOCKED_TASKS,       "blocked_tasks" },
+};
+
+/* Expecting string like "xxx_sys_info=tasks,mem,timers,locks,ftrace,..." */
+unsigned long sys_info_parse_param(char *str)
+{
+       unsigned long si_bits = 0;
+       char *s, *name;
+       int i;
+
+       s = str;
+       while ((name = strsep(&s, ",")) && *name) {
+               for (i = 0; i < ARRAY_SIZE(si_names); i++) {
+                       if (!strcmp(name, si_names[i].name)) {
+                               si_bits |= si_names[i].bit;
+                               break;
+                       }
+               }
+       }
+
+       return si_bits;
+}
+
+#ifdef CONFIG_SYSCTL
+
+static const char sys_info_avail[] __maybe_unused = "tasks,mem,timers,locks,ftrace,all_bt,blocked_tasks";
+
+int sysctl_sys_info_handler(const struct ctl_table *ro_table, int write,
+                                         void *buffer, size_t *lenp,
+                                         loff_t *ppos)
+{
+       char names[sizeof(sys_info_avail) + 1];
+       struct ctl_table table;
+       unsigned long *si_bits_global;
+
+       si_bits_global = ro_table->data;
+
+       if (write) {
+               unsigned long si_bits;
+               int ret;
+
+               table = *ro_table;
+               table.data = names;
+               table.maxlen = sizeof(names);
+               ret = proc_dostring(&table, write, buffer, lenp, ppos);
+               if (ret)
+                       return ret;
+
+               si_bits = sys_info_parse_param(names);
+               /* The access to the global value is not synchronized. */
+               WRITE_ONCE(*si_bits_global, si_bits);
+               return 0;
+       } else {
+               /* for 'read' operation */
+               char *delim = "";
+               int i, len = 0;
+
+               for (i = 0; i < ARRAY_SIZE(si_names); i++) {
+                       if (*si_bits_global & si_names[i].bit) {
+                               len += scnprintf(names + len, sizeof(names) - len,
+                                       "%s%s", delim, si_names[i].name);
+                               delim = ",";
+                       }
+               }
+
+               table = *ro_table;
+               table.data = names;
+               table.maxlen = sizeof(names);
+               return proc_dostring(&table, write, buffer, lenp, ppos);
+       }
+}
+#endif
+
 void sys_info(unsigned long si_mask)
 {
        if (si_mask & SYS_INFO_TASKS)