Commit d7477559 authored by Feng Tang's avatar Feng Tang Committed by Andrew Morton
Browse files

panic: add 'panic_sys_info' sysctl to take human readable string parameter

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: default avatarFeng Tang <feng.tang@linux.alibaba.com>
Suggested-by: default avatarPetr 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: default avatarAndrew Morton <akpm@linux-foundation.org>
parent b76e89e5
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -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
==================

+8 −0
Original line number Diff line number Diff line
@@ -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.
@@ -16,5 +18,11 @@
#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 */
+7 −0
Original line number Diff line number Diff line
@@ -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)
+90 −0
Original line number Diff line number Diff line
@@ -3,10 +3,100 @@
#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)