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, "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 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 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 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