xref: /linux/lib/sys_info.c (revision 509d3f45847627f4c5cdce004c3ec79262b5239c)
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 /*
28  * Default kernel sys_info mask.
29  * If a kernel module calls sys_info() with "parameter == 0", then
30  * this mask will be used.
31  */
32 static unsigned long kernel_si_mask;
33 
34 /* Expecting string like "xxx_sys_info=tasks,mem,timers,locks,ftrace,..." */
sys_info_parse_param(char * str)35 unsigned long sys_info_parse_param(char *str)
36 {
37 	unsigned long si_bits = 0;
38 	char *s, *name;
39 	int i;
40 
41 	s = str;
42 	while ((name = strsep(&s, ",")) && *name) {
43 		i = match_string(si_names, ARRAY_SIZE(si_names), name);
44 		if (i >= 0)
45 			__set_bit(i, &si_bits);
46 	}
47 
48 	return si_bits;
49 }
50 
51 #ifdef CONFIG_SYSCTL
52 
sys_info_write_handler(const struct ctl_table * table,void * buffer,size_t * lenp,loff_t * ppos,unsigned long * si_bits_global)53 static int sys_info_write_handler(const struct ctl_table *table,
54 				  void *buffer, size_t *lenp, loff_t *ppos,
55 				  unsigned long *si_bits_global)
56 {
57 	unsigned long si_bits;
58 	int ret;
59 
60 	ret = proc_dostring(table, 1, buffer, lenp, ppos);
61 	if (ret)
62 		return ret;
63 
64 	si_bits = sys_info_parse_param(table->data);
65 
66 	/* The access to the global value is not synchronized. */
67 	WRITE_ONCE(*si_bits_global, si_bits);
68 
69 	return 0;
70 }
71 
sys_info_read_handler(const struct ctl_table * table,void * buffer,size_t * lenp,loff_t * ppos,unsigned long * si_bits_global)72 static int sys_info_read_handler(const struct ctl_table *table,
73 				 void *buffer, size_t *lenp, loff_t *ppos,
74 				 unsigned long *si_bits_global)
75 {
76 	unsigned long si_bits;
77 	unsigned int len = 0;
78 	char *delim = "";
79 	unsigned int i;
80 
81 	/* The access to the global value is not synchronized. */
82 	si_bits = READ_ONCE(*si_bits_global);
83 
84 	for_each_set_bit(i, &si_bits, ARRAY_SIZE(si_names)) {
85 		if (*si_names[i]) {
86 			len += scnprintf(table->data + len, table->maxlen - len,
87 					 "%s%s", delim, si_names[i]);
88 			delim = ",";
89 		}
90 	}
91 
92 	return proc_dostring(table, 0, buffer, lenp, ppos);
93 }
94 
sysctl_sys_info_handler(const struct ctl_table * ro_table,int write,void * buffer,size_t * lenp,loff_t * ppos)95 int sysctl_sys_info_handler(const struct ctl_table *ro_table, int write,
96 					  void *buffer, size_t *lenp,
97 					  loff_t *ppos)
98 {
99 	struct ctl_table table;
100 	unsigned int i;
101 	size_t maxlen;
102 
103 	maxlen = 0;
104 	for (i = 0; i < ARRAY_SIZE(si_names); i++)
105 		maxlen += strlen(si_names[i]) + 1;
106 
107 	char *names __free(kfree) = kzalloc(maxlen, GFP_KERNEL);
108 	if (!names)
109 		return -ENOMEM;
110 
111 	table = *ro_table;
112 	table.data = names;
113 	table.maxlen = maxlen;
114 
115 	if (write)
116 		return sys_info_write_handler(&table, buffer, lenp, ppos, ro_table->data);
117 	else
118 		return sys_info_read_handler(&table, buffer, lenp, ppos, ro_table->data);
119 }
120 
121 static const struct ctl_table sys_info_sysctls[] = {
122 	{
123 		.procname	= "kernel_sys_info",
124 		.data		= &kernel_si_mask,
125 		.maxlen         = sizeof(kernel_si_mask),
126 		.mode		= 0644,
127 		.proc_handler	= sysctl_sys_info_handler,
128 	},
129 };
130 
sys_info_sysctl_init(void)131 static int __init sys_info_sysctl_init(void)
132 {
133 	register_sysctl_init("kernel", sys_info_sysctls);
134 	return 0;
135 }
136 subsys_initcall(sys_info_sysctl_init);
137 #endif
138 
__sys_info(unsigned long si_mask)139 static void __sys_info(unsigned long si_mask)
140 {
141 	if (si_mask & SYS_INFO_TASKS)
142 		show_state();
143 
144 	if (si_mask & SYS_INFO_MEM)
145 		show_mem();
146 
147 	if (si_mask & SYS_INFO_TIMERS)
148 		sysrq_timer_list_show();
149 
150 	if (si_mask & SYS_INFO_LOCKS)
151 		debug_show_all_locks();
152 
153 	if (si_mask & SYS_INFO_FTRACE)
154 		ftrace_dump(DUMP_ALL);
155 
156 	if (si_mask & SYS_INFO_ALL_BT)
157 		trigger_all_cpu_backtrace();
158 
159 	if (si_mask & SYS_INFO_BLOCKED_TASKS)
160 		show_state_filter(TASK_UNINTERRUPTIBLE);
161 }
162 
sys_info(unsigned long si_mask)163 void sys_info(unsigned long si_mask)
164 {
165 	__sys_info(si_mask ? : kernel_si_mask);
166 }
167