xref: /linux/lib/sys_info.c (revision 5944f875ac27cae8b831206aef011a444efa637d)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <linux/array_size.h>
3 #include <linux/bitops.h>
4 #include <linux/cleanup.h>
5 #include <linux/console.h>
6 #include <linux/log2.h>
7 #include <linux/kernel.h>
8 #include <linux/ftrace.h>
9 #include <linux/nmi.h>
10 #include <linux/sched/debug.h>
11 #include <linux/string.h>
12 #include <linux/sysctl.h>
13 
14 #include <linux/sys_info.h>
15 
16 static const char * const si_names[] = {
17 	[ilog2(SYS_INFO_TASKS)]			= "tasks",
18 	[ilog2(SYS_INFO_MEM)]			= "mem",
19 	[ilog2(SYS_INFO_TIMERS)]		= "timers",
20 	[ilog2(SYS_INFO_LOCKS)]			= "locks",
21 	[ilog2(SYS_INFO_FTRACE)]		= "ftrace",
22 	[ilog2(SYS_INFO_PANIC_CONSOLE_REPLAY)]	= "",
23 	[ilog2(SYS_INFO_ALL_BT)]		= "all_bt",
24 	[ilog2(SYS_INFO_BLOCKED_TASKS)]		= "blocked_tasks",
25 };
26 
27 /* Expecting string like "xxx_sys_info=tasks,mem,timers,locks,ftrace,..." */
28 unsigned long sys_info_parse_param(char *str)
29 {
30 	unsigned long si_bits = 0;
31 	char *s, *name;
32 	int i;
33 
34 	s = str;
35 	while ((name = strsep(&s, ",")) && *name) {
36 		i = match_string(si_names, ARRAY_SIZE(si_names), name);
37 		if (i >= 0)
38 			__set_bit(i, &si_bits);
39 	}
40 
41 	return si_bits;
42 }
43 
44 #ifdef CONFIG_SYSCTL
45 
46 static int sys_info_write_handler(const struct ctl_table *table,
47 				  void *buffer, size_t *lenp, loff_t *ppos,
48 				  unsigned long *si_bits_global)
49 {
50 	unsigned long si_bits;
51 	int ret;
52 
53 	ret = proc_dostring(table, 1, buffer, lenp, ppos);
54 	if (ret)
55 		return ret;
56 
57 	si_bits = sys_info_parse_param(table->data);
58 
59 	/* The access to the global value is not synchronized. */
60 	WRITE_ONCE(*si_bits_global, si_bits);
61 
62 	return 0;
63 }
64 
65 static int sys_info_read_handler(const struct ctl_table *table,
66 				 void *buffer, size_t *lenp, loff_t *ppos,
67 				 unsigned long *si_bits_global)
68 {
69 	unsigned long si_bits;
70 	unsigned int len = 0;
71 	char *delim = "";
72 	unsigned int i;
73 
74 	/* The access to the global value is not synchronized. */
75 	si_bits = READ_ONCE(*si_bits_global);
76 
77 	for_each_set_bit(i, &si_bits, ARRAY_SIZE(si_names)) {
78 		if (*si_names[i]) {
79 			len += scnprintf(table->data + len, table->maxlen - len,
80 					 "%s%s", delim, si_names[i]);
81 			delim = ",";
82 		}
83 	}
84 
85 	return proc_dostring(table, 0, buffer, lenp, ppos);
86 }
87 
88 int sysctl_sys_info_handler(const struct ctl_table *ro_table, int write,
89 					  void *buffer, size_t *lenp,
90 					  loff_t *ppos)
91 {
92 	struct ctl_table table;
93 	unsigned int i;
94 	size_t maxlen;
95 
96 	maxlen = 0;
97 	for (i = 0; i < ARRAY_SIZE(si_names); i++)
98 		maxlen += strlen(si_names[i]) + 1;
99 
100 	char *names __free(kfree) = kzalloc(maxlen, GFP_KERNEL);
101 	if (!names)
102 		return -ENOMEM;
103 
104 	table = *ro_table;
105 	table.data = names;
106 	table.maxlen = maxlen;
107 
108 	if (write)
109 		return sys_info_write_handler(&table, buffer, lenp, ppos, ro_table->data);
110 	else
111 		return sys_info_read_handler(&table, buffer, lenp, ppos, ro_table->data);
112 }
113 #endif
114 
115 void sys_info(unsigned long si_mask)
116 {
117 	if (si_mask & SYS_INFO_TASKS)
118 		show_state();
119 
120 	if (si_mask & SYS_INFO_MEM)
121 		show_mem();
122 
123 	if (si_mask & SYS_INFO_TIMERS)
124 		sysrq_timer_list_show();
125 
126 	if (si_mask & SYS_INFO_LOCKS)
127 		debug_show_all_locks();
128 
129 	if (si_mask & SYS_INFO_FTRACE)
130 		ftrace_dump(DUMP_ALL);
131 
132 	if (si_mask & SYS_INFO_ALL_BT)
133 		trigger_all_cpu_backtrace();
134 
135 	if (si_mask & SYS_INFO_BLOCKED_TASKS)
136 		show_state_filter(TASK_UNINTERRUPTIBLE);
137 }
138