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