xref: /linux/fs/smb/server/proc.c (revision c3d85c669d09007aeb9eb9f3d28d8c863401f83f)
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 
ksmbd_proc_create(const char * name,int (* show)(struct seq_file * m,void * v),void * v)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 
ksmbd_proc_show_flag_names(struct seq_file * m,const struct ksmbd_const_name * table,int count,unsigned int flags)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 
ksmbd_proc_const_name(const struct ksmbd_const_name * table,int count,unsigned int const_value)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 
ksmbd_server_state_string(void)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 
ksmbd_signing_mode_string(void)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 
proc_show_runtime_totals(struct seq_file * m)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 
proc_show_ksmbd_stats(struct seq_file * m,void * v)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, "session_timeouts:\t%lld\n",
182 		   ksmbd_counter_sum(KSMBD_COUNTER_SESSION_TIMEOUTS));
183 	seq_printf(m, "tree_connects:\t%lld\n",
184 		   ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS));
185 	seq_printf(m, "requests:\t%lld\n",
186 		   ksmbd_counter_sum(KSMBD_COUNTER_REQUESTS));
187 	seq_printf(m, "read_bytes:\t%lld\n",
188 		   ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES));
189 	seq_printf(m, "written_bytes:\t%lld\n",
190 		   ksmbd_counter_sum(KSMBD_COUNTER_WRITE_BYTES));
191 
192 	seq_puts(m, "\nSMB2\n");
193 	for (i = 0; i < KSMBD_COUNTER_MAX_REQS; i++)
194 		seq_printf(m, "%s:\t%lld\n", smb2_process_req[i].name,
195 			   ksmbd_counter_sum(KSMBD_COUNTER_FIRST_REQ + i));
196 
197 	seq_puts(m, "\nSMB2 status\n");
198 	seq_printf(m, "success:\t%lld\n",
199 		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_SUCCESS));
200 	seq_printf(m, "informational:\t%lld\n",
201 		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_INFORMATIONAL));
202 	seq_printf(m, "warning:\t%lld\n",
203 		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_WARNING));
204 	seq_printf(m, "error:\t%lld\n",
205 		   ksmbd_counter_sum(KSMBD_COUNTER_STATUS_ERROR));
206 	seq_printf(m, "access_denied:\t%lld\n",
207 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_ACCESS_DENIED));
208 	seq_printf(m, "not_found:\t%lld\n",
209 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_FOUND));
210 	seq_printf(m, "invalid_parameter:\t%lld\n",
211 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_INVALID_PARAMETER));
212 	seq_printf(m, "sharing_violation:\t%lld\n",
213 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_SHARING_VIOLATION));
214 	seq_printf(m, "not_supported:\t%lld\n",
215 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_SUPPORTED));
216 	seq_printf(m, "other:\t%lld\n",
217 		   ksmbd_counter_sum(KSMBD_COUNTER_ERROR_OTHER));
218 	return 0;
219 }
220 
ksmbd_proc_cleanup(void)221 void ksmbd_proc_cleanup(void)
222 {
223 	int i;
224 
225 	if (!ksmbd_proc_fs)
226 		return;
227 
228 	proc_remove(ksmbd_proc_fs);
229 
230 	for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++)
231 		percpu_counter_destroy(&ksmbd_counters.counters[i]);
232 
233 	ksmbd_proc_fs = NULL;
234 }
235 
ksmbd_proc_reset(void)236 void ksmbd_proc_reset(void)
237 {
238 	int i;
239 
240 	for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++)
241 		percpu_counter_set(&ksmbd_counters.counters[i], 0);
242 }
243 
ksmbd_proc_init(void)244 int ksmbd_proc_init(void)
245 {
246 	int i;
247 	int retval = -ENOMEM;
248 
249 	ksmbd_proc_fs = proc_mkdir("fs/ksmbd", NULL);
250 	if (!ksmbd_proc_fs)
251 		return retval;
252 
253 	if (!proc_mkdir_mode("sessions", 0400, ksmbd_proc_fs))
254 		goto err_out;
255 
256 	for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) {
257 		retval = percpu_counter_init(&ksmbd_counters.counters[i], 0, GFP_KERNEL);
258 		if (retval)
259 			goto err_out;
260 	}
261 
262 	if (!ksmbd_proc_create("server", proc_show_ksmbd_stats, NULL)) {
263 		retval = -ENOMEM;
264 		goto err_out;
265 	}
266 
267 	ksmbd_proc_reset();
268 	return 0;
269 err_out:
270 	ksmbd_proc_cleanup();
271 	return retval;
272 }
273