Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
16static 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 */
32static unsigned long kernel_si_mask;
33
34/* Expecting string like "xxx_sys_info=tasks,mem,timers,locks,ftrace,..." */
35unsigned 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
53static 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
72static 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
95int 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
121static 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
131static int __init sys_info_sysctl_init(void)
132{
133 register_sysctl_init("kernel", sys_info_sysctls);
134 return 0;
135}
136subsys_initcall(sys_info_sysctl_init);
137#endif
138
139static 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
163void sys_info(unsigned long si_mask)
164{
165 __sys_info(si_mask ? : kernel_si_mask);
166}