xref: /linux/fs/smb/server/proc.c (revision 83684c4e4d62cb02b2e4d0d18963d1035439278e)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *   Copyright (C) 2025, LG Electronics.
4  *   Author(s): Hyunchul Lee <hyc.lee@gmail.com>
5  *   Copyright (C) 2025, Samsung Electronics.
6  *   Author(s): Vedansh Bhardwaj <v.bhardwaj@samsung.com>
7  */
8 
9 #include <linux/module.h>
10 #include <linux/proc_fs.h>
11 #include <linux/seq_file.h>
12 
13 #include "misc.h"
14 #include "connection.h"
15 #include "server.h"
16 #include "stats.h"
17 #include "smb_common.h"
18 #include "smb2pdu.h"
19 #include "vfs_cache.h"
20 
21 static struct proc_dir_entry *ksmbd_proc_fs;
22 struct ksmbd_counters ksmbd_counters;
23 
24 struct proc_dir_entry *ksmbd_proc_create(const char *name,
25 					 int (*show)(struct seq_file *m, void *v),
26 						 void *v)
27 {
28 	return proc_create_single_data(name, 0400, ksmbd_proc_fs,
29 			   show, v);
30 }
31 
32 void ksmbd_proc_show_flag_names(struct seq_file *m,
33 				const struct ksmbd_const_name *table,
34 				int count, unsigned int flags)
35 {
36 	unsigned int remaining = flags;
37 	bool separator = false;
38 	int i;
39 
40 	for (i = 0; i < count; i++) {
41 		unsigned int flag = table[i].const_value;
42 
43 		if (!flag || (remaining & flag) != flag)
44 			continue;
45 		seq_printf(m, "%s%s", separator ? "," : "", table[i].name);
46 		separator = true;
47 		remaining &= ~flag;
48 	}
49 
50 	if (remaining)
51 		seq_printf(m, "%s0x%08x", separator ? "," : "", remaining);
52 	else if (!separator)
53 		seq_puts(m, "none");
54 }
55 
56 const char *ksmbd_proc_const_name(const struct ksmbd_const_name *table,
57 				  int count, unsigned int const_value)
58 {
59 	int i;
60 
61 	for (i = 0; i < count; i++) {
62 		if (table[i].const_value == const_value)
63 			return table[i].name;
64 	}
65 	return NULL;
66 }
67 
68 struct ksmbd_const_smb2_process_req {
69 	unsigned int const_value;
70 	const char *name;
71 };
72 
73 static const struct ksmbd_const_smb2_process_req smb2_process_req[KSMBD_COUNTER_MAX_REQS] = {
74 	{le16_to_cpu(SMB2_NEGOTIATE), "SMB2_NEGOTIATE"},
75 	{le16_to_cpu(SMB2_SESSION_SETUP), "SMB2_SESSION_SETUP"},
76 	{le16_to_cpu(SMB2_LOGOFF), "SMB2_LOGOFF"},
77 	{le16_to_cpu(SMB2_TREE_CONNECT), "SMB2_TREE_CONNECT"},
78 	{le16_to_cpu(SMB2_TREE_DISCONNECT), "SMB2_TREE_DISCONNECT"},
79 	{le16_to_cpu(SMB2_CREATE), "SMB2_CREATE"},
80 	{le16_to_cpu(SMB2_CLOSE), "SMB2_CLOSE"},
81 	{le16_to_cpu(SMB2_FLUSH), "SMB2_FLUSH"},
82 	{le16_to_cpu(SMB2_READ), "SMB2_READ"},
83 	{le16_to_cpu(SMB2_WRITE), "SMB2_WRITE"},
84 	{le16_to_cpu(SMB2_LOCK), "SMB2_LOCK"},
85 	{le16_to_cpu(SMB2_IOCTL), "SMB2_IOCTL"},
86 	{le16_to_cpu(SMB2_CANCEL), "SMB2_CANCEL"},
87 	{le16_to_cpu(SMB2_ECHO), "SMB2_ECHO"},
88 	{le16_to_cpu(SMB2_QUERY_DIRECTORY), "SMB2_QUERY_DIRECTORY"},
89 	{le16_to_cpu(SMB2_CHANGE_NOTIFY), "SMB2_CHANGE_NOTIFY"},
90 	{le16_to_cpu(SMB2_QUERY_INFO), "SMB2_QUERY_INFO"},
91 	{le16_to_cpu(SMB2_SET_INFO), "SMB2_SET_INFO"},
92 	{le16_to_cpu(SMB2_OPLOCK_BREAK), "SMB2_OPLOCK_BREAK"},
93 };
94 
95 static const char *ksmbd_server_state_string(void)
96 {
97 	switch (READ_ONCE(server_conf.state)) {
98 	case SERVER_STATE_STARTING_UP:
99 		return "starting";
100 	case SERVER_STATE_RUNNING:
101 		return "running";
102 	case SERVER_STATE_RESETTING:
103 		return "resetting";
104 	case SERVER_STATE_SHUTTING_DOWN:
105 		return "shutdown";
106 	default:
107 		return "unknown";
108 	}
109 }
110 
111 static const char *ksmbd_signing_mode_string(void)
112 {
113 	switch (server_conf.signing) {
114 	case KSMBD_CONFIG_OPT_DISABLED:
115 		return "disabled";
116 	case KSMBD_CONFIG_OPT_MANDATORY:
117 		return "mandatory";
118 	case KSMBD_CONFIG_OPT_AUTO:
119 		return "auto";
120 	default:
121 		return "unknown";
122 	}
123 }
124 
125 static void proc_show_runtime_totals(struct seq_file *m)
126 {
127 	struct ksmbd_conn *conn;
128 	unsigned int clients = 0;
129 	unsigned int open_files = 0;
130 	int i;
131 
132 	down_read(&conn_list_lock);
133 	hash_for_each(conn_list, i, conn, hlist) {
134 		clients++;
135 		open_files += atomic_read(&conn->stats.open_files_count);
136 	}
137 	up_read(&conn_list_lock);
138 
139 	seq_printf(m, "clients:\t%u\n", clients);
140 	seq_printf(m, "open_files:\t%u\n", open_files);
141 }
142 
143 static int proc_show_ksmbd_stats(struct seq_file *m, void *v)
144 {
145 	int i;
146 
147 	seq_puts(m, "Server\n");
148 	seq_printf(m, "state:\t%s\n", ksmbd_server_state_string());
149 	seq_printf(m, "name:\t%s\n", ksmbd_server_string());
150 	seq_printf(m, "netbios:\t%s\n", ksmbd_netbios_name());
151 	seq_printf(m, "work_group:\t%s\n", ksmbd_work_group());
152 	seq_printf(m, "min_protocol:\t%s\n", ksmbd_get_protocol_string(server_conf.min_protocol));
153 	seq_printf(m, "max_protocol:\t%s\n", ksmbd_get_protocol_string(server_conf.max_protocol));
154 	seq_printf(m, "flags:\t0x%08x\n", server_conf.flags);
155 	seq_printf(m, "tcp_port:\t%u\n", server_conf.tcp_port);
156 	seq_printf(m, "signing:\t%s\n", ksmbd_signing_mode_string());
157 	seq_printf(m, "signing_enforced:\t%s\n",
158 		   server_conf.enforced_signing ? "yes" : "no");
159 	seq_printf(m, "bind_interfaces_only:\t%s\n",
160 		   server_conf.bind_interfaces_only ? "yes" : "no");
161 	seq_printf(m, "max_connections:\t%u\n", server_conf.max_connections);
162 	seq_printf(m, "max_connections_per_ip:\t%u\n",
163 		   server_conf.max_ip_connections);
164 	seq_printf(m, "max_inflight_requests:\t%u\n",
165 		   server_conf.max_inflight_req);
166 	seq_printf(m, "deadtime_seconds:\t%lu\n", server_conf.deadtime / HZ);
167 	seq_printf(m, "ipc_timeout_seconds:\t%u\n", server_conf.ipc_timeout / HZ);
168 	if (server_conf.ipc_last_active)
169 		seq_printf(m, "ipc_last_active_seconds:\t%lu\n",
170 			   jiffies_to_msecs(jiffies - server_conf.ipc_last_active) /
171 			   MSEC_PER_SEC);
172 	else
173 		seq_puts(m, "ipc_last_active_seconds:\tnever\n");
174 	seq_printf(m, "durable_scavenger:\t%s\n",
175 		   ksmbd_durable_scavenger_active() ? "running" : "stopped");
176 	seq_printf(m, "share_fake_fscaps:\t0x%08x\n",
177 		   server_conf.share_fake_fscaps);
178 	proc_show_runtime_totals(m);
179 	seq_printf(m, "sessions:\t%lld\n",
180 		   ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS));
181 	seq_printf(m, "tree_connects:\t%lld\n",
182 		   ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS));
183 	seq_printf(m, "requests:\t%lld\n",
184 		   ksmbd_counter_sum(KSMBD_COUNTER_REQUESTS));
185 	seq_printf(m, "read_bytes:\t%lld\n",
186 		   ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES));
187 	seq_printf(m, "written_bytes:\t%lld\n",
188 		   ksmbd_counter_sum(KSMBD_COUNTER_WRITE_BYTES));
189 
190 	seq_puts(m, "\nSMB2\n");
191 	for (i = 0; i < KSMBD_COUNTER_MAX_REQS; i++)
192 		seq_printf(m, "%s:\t%lld\n", smb2_process_req[i].name,
193 			   ksmbd_counter_sum(KSMBD_COUNTER_FIRST_REQ + i));
194 
195 	seq_puts(m, "\nSMB2 status\n");
196 	seq_printf(m, "success:\t%lld\n",
197 		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_SUCCESS));
198 	seq_printf(m, "informational:\t%lld\n",
199 		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_INFORMATIONAL));
200 	seq_printf(m, "warning:\t%lld\n",
201 		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_WARNING));
202 	seq_printf(m, "error:\t%lld\n",
203 		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_ERROR));
204 	seq_printf(m, "access_denied:\t%lld\n",
205 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_ACCESS_DENIED));
206 	seq_printf(m, "not_found:\t%lld\n",
207 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_FOUND));
208 	seq_printf(m, "invalid_parameter:\t%lld\n",
209 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_INVALID_PARAMETER));
210 	seq_printf(m, "sharing_violation:\t%lld\n",
211 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_SHARING_VIOLATION));
212 	seq_printf(m, "not_supported:\t%lld\n",
213 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_SUPPORTED));
214 	seq_printf(m, "other:\t%lld\n",
215 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_OTHER));
216 	return 0;
217 }
218 
219 void ksmbd_proc_cleanup(void)
220 {
221 	int i;
222 
223 	if (!ksmbd_proc_fs)
224 		return;
225 
226 	proc_remove(ksmbd_proc_fs);
227 
228 	for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++)
229 		percpu_counter_destroy(&ksmbd_counters.counters[i]);
230 
231 	ksmbd_proc_fs = NULL;
232 }
233 
234 void ksmbd_proc_reset(void)
235 {
236 	int i;
237 
238 	for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++)
239 		percpu_counter_set(&ksmbd_counters.counters[i], 0);
240 }
241 
242 int ksmbd_proc_init(void)
243 {
244 	int i;
245 	int retval = -ENOMEM;
246 
247 	ksmbd_proc_fs = proc_mkdir("fs/ksmbd", NULL);
248 	if (!ksmbd_proc_fs)
249 		return retval;
250 
251 	if (!proc_mkdir_mode("sessions", 0400, ksmbd_proc_fs))
252 		goto err_out;
253 
254 	for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) {
255 		retval = percpu_counter_init(&ksmbd_counters.counters[i], 0, GFP_KERNEL);
256 		if (retval)
257 			goto err_out;
258 	}
259 
260 	if (!ksmbd_proc_create("server", proc_show_ksmbd_stats, NULL)) {
261 		retval = -ENOMEM;
262 		goto err_out;
263 	}
264 
265 	ksmbd_proc_reset();
266 	return 0;
267 err_out:
268 	ksmbd_proc_cleanup();
269 	return retval;
270 }
271