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