// SPDX-License-Identifier: GPL-2.0-or-later/* * Copyright (C) 2025, LG Electronics. * Author(s): Hyunchul Lee <hyc.lee@gmail.com> * Copyright (C) 2025, Samsung Electronics. * Author(s): Vedansh Bhardwaj <v.bhardwaj@samsung.com> */ #include <linux/module.h>#include <linux/proc_fs.h>#include <linux/seq_file.h> #include "misc.h"#include "connection.h"#include "server.h"#include "stats.h"#include "smb_common.h"#include "smb2pdu.h"#include "vfs_cache.h" static struct proc_dir_entry *ksmbd_proc_fs;struct ksmbd_counters ksmbd_counters; struct proc_dir_entry *ksmbd_proc_create(const char *name, int (*show)(struct seq_file *m, void *v), void *v){ return proc_create_single_data(name, 0400, ksmbd_proc_fs, show, v);} void ksmbd_proc_show_flag_names(struct seq_file *m, const struct ksmbd_const_name *table, int count, unsigned int flags){ unsigned int remaining = flags; bool separator = false; int i; for (i = 0; i < count; i++) { unsigned int flag = table[i].const_value; if (!flag || (remaining & flag) != flag) continue; seq_printf(m, "%s%s", separator ? "," : "", table[i].name); separator = true; remaining &= ~flag; } if (remaining) seq_printf(m, "%s0x%08x", separator ? "," : "", remaining); else if (!separator) seq_puts(m, "none");} const char *ksmbd_proc_const_name(const struct ksmbd_const_name *table, int count, unsigned int const_value){ int i; for (i = 0; i < count; i++) { if (table[i].const_value == const_value) return table[i].name; } return NULL;} struct ksmbd_const_smb2_process_req { unsigned int const_value; const char *name;}; static const struct ksmbd_const_smb2_process_req smb2_process_req[KSMBD_COUNTER_MAX_REQS] = { {le16_to_cpu(SMB2_NEGOTIATE), "SMB2_NEGOTIATE"}, {le16_to_cpu(SMB2_SESSION_SETUP), "SMB2_SESSION_SETUP"}, {le16_to_cpu(SMB2_LOGOFF), "SMB2_LOGOFF"}, {le16_to_cpu(SMB2_TREE_CONNECT), "SMB2_TREE_CONNECT"}, {le16_to_cpu(SMB2_TREE_DISCONNECT), "SMB2_TREE_DISCONNECT"}, {le16_to_cpu(SMB2_CREATE), "SMB2_CREATE"}, {le16_to_cpu(SMB2_CLOSE), "SMB2_CLOSE"}, {le16_to_cpu(SMB2_FLUSH), "SMB2_FLUSH"}, {le16_to_cpu(SMB2_READ), "SMB2_READ"}, {le16_to_cpu(SMB2_WRITE), "SMB2_WRITE"}, {le16_to_cpu(SMB2_LOCK), "SMB2_LOCK"}, {le16_to_cpu(SMB2_IOCTL), "SMB2_IOCTL"}, {le16_to_cpu(SMB2_CANCEL), "SMB2_CANCEL"}, {le16_to_cpu(SMB2_ECHO), "SMB2_ECHO"}, {le16_to_cpu(SMB2_QUERY_DIRECTORY), "SMB2_QUERY_DIRECTORY"}, {le16_to_cpu(SMB2_CHANGE_NOTIFY), "SMB2_CHANGE_NOTIFY"}, {le16_to_cpu(SMB2_QUERY_INFO), "SMB2_QUERY_INFO"}, {le16_to_cpu(SMB2_SET_INFO), "SMB2_SET_INFO"}, {le16_to_cpu(SMB2_OPLOCK_BREAK), "SMB2_OPLOCK_BREAK"},}; static const char *ksmbd_server_state_string(void){ switch (READ_ONCE(server_conf.state)) { case SERVER_STATE_STARTING_UP: return "starting"; case SERVER_STATE_RUNNING: return "running"; case SERVER_STATE_RESETTING: return "resetting"; case SERVER_STATE_SHUTTING_DOWN: return "shutdown"; default: return "unknown"; }} static const char *ksmbd_signing_mode_string(void){ switch (server_conf.signing) { case KSMBD_CONFIG_OPT_DISABLED: return "disabled"; case KSMBD_CONFIG_OPT_MANDATORY: return "mandatory"; case KSMBD_CONFIG_OPT_AUTO: return "auto"; default: return "unknown"; }} static void proc_show_runtime_totals(struct seq_file *m){ struct ksmbd_conn *conn; unsigned int clients = 0; unsigned int open_files = 0; int i; down_read(&conn_list_lock); hash_for_each(conn_list, i, conn, hlist) { clients++; open_files += atomic_read(&conn->stats.open_files_count); } up_read(&conn_list_lock); seq_printf(m, "clients:\t%u\n", clients); seq_printf(m, "open_files:\t%u\n", open_files);} static int proc_show_ksmbd_stats(struct seq_file *m, void *v){ int i; seq_puts(m, "Server\n"); seq_printf(m, "state:\t%s\n", ksmbd_server_state_string()); seq_printf(m, "name:\t%s\n", ksmbd_server_string()); seq_printf(m, "netbios:\t%s\n", ksmbd_netbios_name()); seq_printf(m, "work_group:\t%s\n", ksmbd_work_group()); seq_printf(m, "min_protocol:\t%s\n", ksmbd_get_protocol_string(server_conf.min_protocol)); seq_printf(m, "max_protocol:\t%s\n", ksmbd_get_protocol_string(server_conf.max_protocol)); seq_printf(m, "flags:\t0x%08x\n", server_conf.flags); seq_printf(m, "tcp_port:\t%u\n", server_conf.tcp_port); seq_printf(m, "signing:\t%s\n", ksmbd_signing_mode_string()); seq_printf(m, "signing_enforced:\t%s\n", server_conf.enforced_signing ? "yes" : "no"); seq_printf(m, "bind_interfaces_only:\t%s\n", server_conf.bind_interfaces_only ? "yes" : "no"); seq_printf(m, "max_connections:\t%u\n", server_conf.max_connections); seq_printf(m, "max_connections_per_ip:\t%u\n", server_conf.max_ip_connections); seq_printf(m, "max_inflight_requests:\t%u\n", server_conf.max_inflight_req); seq_printf(m, "deadtime_seconds:\t%lu\n", server_conf.deadtime / HZ); seq_printf(m, "ipc_timeout_seconds:\t%u\n", server_conf.ipc_timeout / HZ); if (server_conf.ipc_last_active) seq_printf(m, "ipc_last_active_seconds:\t%lu\n", jiffies_to_msecs(jiffies - server_conf.ipc_last_active) / MSEC_PER_SEC); else seq_puts(m, "ipc_last_active_seconds:\tnever\n"); seq_printf(m, "durable_scavenger:\t%s\n", ksmbd_durable_scavenger_active() ? "running" : "stopped"); seq_printf(m, "share_fake_fscaps:\t0x%08x\n", server_conf.share_fake_fscaps); proc_show_runtime_totals(m); seq_printf(m, "sessions:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS)); seq_printf(m, "tree_connects:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS)); seq_printf(m, "requests:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_REQUESTS)); seq_printf(m, "read_bytes:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES)); seq_printf(m, "written_bytes:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_WRITE_BYTES)); seq_puts(m, "\nSMB2\n"); for (i = 0; i < KSMBD_COUNTER_MAX_REQS; i++) seq_printf(m, "%s:\t%lld\n", smb2_process_req[i].name, ksmbd_counter_sum(KSMBD_COUNTER_FIRST_REQ + i)); seq_puts(m, "\nSMB2 status\n"); seq_printf(m, "success:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_STATUS_SUCCESS)); seq_printf(m, "informational:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_STATUS_INFORMATIONAL)); seq_printf(m, "warning:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_STATUS_WARNING)); seq_printf(m, "error:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_STATUS_ERROR)); seq_printf(m, "access_denied:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_ERROR_ACCESS_DENIED)); seq_printf(m, "not_found:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_FOUND)); seq_printf(m, "invalid_parameter:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_ERROR_INVALID_PARAMETER)); seq_printf(m, "sharing_violation:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_ERROR_SHARING_VIOLATION)); seq_printf(m, "not_supported:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_SUPPORTED)); seq_printf(m, "other:\t%lld\n", ksmbd_counter_sum(KSMBD_COUNTER_ERROR_OTHER)); return 0;} void ksmbd_proc_cleanup(void){ int i; if (!ksmbd_proc_fs) return; proc_remove(ksmbd_proc_fs); for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) percpu_counter_destroy(&ksmbd_counters.counters[i]); ksmbd_proc_fs = NULL;} void ksmbd_proc_reset(void){ int i; for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) percpu_counter_set(&ksmbd_counters.counters[i], 0);} int ksmbd_proc_init(void){ int i; int retval = -ENOMEM; ksmbd_proc_fs = proc_mkdir("fs/ksmbd", NULL); if (!ksmbd_proc_fs) return retval; if (!proc_mkdir_mode("sessions", 0400, ksmbd_proc_fs)) goto err_out; for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) { retval = percpu_counter_init(&ksmbd_counters.counters[i], 0, GFP_KERNEL); if (retval) goto err_out; } if (!ksmbd_proc_create("server", proc_show_ksmbd_stats, NULL)) { retval = -ENOMEM; goto err_out; } ksmbd_proc_reset(); return 0;err_out: ksmbd_proc_cleanup(); return retval;}
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2025, LG Electronics.
* Author(s): Hyunchul Lee <hyc.lee@gmail.com>
* Copyright (C) 2025, Samsung Electronics.
* Author(s): Vedansh Bhardwaj <v.bhardwaj@samsung.com>
*/
#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include "misc.h"
#include "connection.h"
#include "server.h"
#include "stats.h"
#include "smb_common.h"
#include "smb2pdu.h"
#include "vfs_cache.h"
static struct proc_dir_entry *ksmbd_proc_fs;
struct ksmbd_counters ksmbd_counters;
struct proc_dir_entry *ksmbd_proc_create(const char *name,
int (*show)(struct seq_file *m, void *v),
void *v)
{
return proc_create_single_data(name, 0400, ksmbd_proc_fs,
show, v);
}
void ksmbd_proc_show_flag_names(struct seq_file *m,
const struct ksmbd_const_name *table,
int count, unsigned int flags)
{
unsigned int remaining = flags;
bool separator = false;
int i;
for (i = 0; i < count; i++) {
unsigned int flag = table[i].const_value;
if (!flag || (remaining & flag) != flag)
continue;
seq_printf(m, "%s%s", separator ? "," : "", table[i].name);
separator = true;
remaining &= ~flag;
}
if (remaining)
seq_printf(m, "%s0x%08x", separator ? "," : "", remaining);
else if (!separator)
seq_puts(m, "none");
}
const char *ksmbd_proc_const_name(const struct ksmbd_const_name *table,
int count, unsigned int const_value)
{
int i;
for (i = 0; i < count; i++) {
if (table[i].const_value == const_value)
return table[i].name;
}
return NULL;
}
struct ksmbd_const_smb2_process_req {
unsigned int const_value;
const char *name;
};
static const struct ksmbd_const_smb2_process_req smb2_process_req[KSMBD_COUNTER_MAX_REQS] = {
{le16_to_cpu(SMB2_NEGOTIATE), "SMB2_NEGOTIATE"},
{le16_to_cpu(SMB2_SESSION_SETUP), "SMB2_SESSION_SETUP"},
{le16_to_cpu(SMB2_LOGOFF), "SMB2_LOGOFF"},
{le16_to_cpu(SMB2_TREE_CONNECT), "SMB2_TREE_CONNECT"},
{le16_to_cpu(SMB2_TREE_DISCONNECT), "SMB2_TREE_DISCONNECT"},
{le16_to_cpu(SMB2_CREATE), "SMB2_CREATE"},
{le16_to_cpu(SMB2_CLOSE), "SMB2_CLOSE"},
{le16_to_cpu(SMB2_FLUSH), "SMB2_FLUSH"},
{le16_to_cpu(SMB2_READ), "SMB2_READ"},
{le16_to_cpu(SMB2_WRITE), "SMB2_WRITE"},
{le16_to_cpu(SMB2_LOCK), "SMB2_LOCK"},
{le16_to_cpu(SMB2_IOCTL), "SMB2_IOCTL"},
{le16_to_cpu(SMB2_CANCEL), "SMB2_CANCEL"},
{le16_to_cpu(SMB2_ECHO), "SMB2_ECHO"},
{le16_to_cpu(SMB2_QUERY_DIRECTORY), "SMB2_QUERY_DIRECTORY"},
{le16_to_cpu(SMB2_CHANGE_NOTIFY), "SMB2_CHANGE_NOTIFY"},
{le16_to_cpu(SMB2_QUERY_INFO), "SMB2_QUERY_INFO"},
{le16_to_cpu(SMB2_SET_INFO), "SMB2_SET_INFO"},
{le16_to_cpu(SMB2_OPLOCK_BREAK), "SMB2_OPLOCK_BREAK"},
};
static const char *ksmbd_server_state_string(void)
{
switch (READ_ONCE(server_conf.state)) {
case SERVER_STATE_STARTING_UP:
return "starting";
case SERVER_STATE_RUNNING:
return "running";
case SERVER_STATE_RESETTING:
return "resetting";
case SERVER_STATE_SHUTTING_DOWN:
return "shutdown";
default:
return "unknown";
}
}
static const char *ksmbd_signing_mode_string(void)
{
switch (server_conf.signing) {
case KSMBD_CONFIG_OPT_DISABLED:
return "disabled";
case KSMBD_CONFIG_OPT_MANDATORY:
return "mandatory";
case KSMBD_CONFIG_OPT_AUTO:
return "auto";
default:
return "unknown";
}
}
static void proc_show_runtime_totals(struct seq_file *m)
{
struct ksmbd_conn *conn;
unsigned int clients = 0;
unsigned int open_files = 0;
int i;
down_read(&conn_list_lock);
hash_for_each(conn_list, i, conn, hlist) {
clients++;
open_files += atomic_read(&conn->stats.open_files_count);
}
up_read(&conn_list_lock);
seq_printf(m, "clients:\t%u\n", clients);
seq_printf(m, "open_files:\t%u\n", open_files);
}
static int proc_show_ksmbd_stats(struct seq_file *m, void *v)
{
int i;
seq_puts(m, "Server\n");
seq_printf(m, "state:\t%s\n", ksmbd_server_state_string());
seq_printf(m, "name:\t%s\n", ksmbd_server_string());
seq_printf(m, "netbios:\t%s\n", ksmbd_netbios_name());
seq_printf(m, "work_group:\t%s\n", ksmbd_work_group());
seq_printf(m, "min_protocol:\t%s\n", ksmbd_get_protocol_string(server_conf.min_protocol));
seq_printf(m, "max_protocol:\t%s\n", ksmbd_get_protocol_string(server_conf.max_protocol));
seq_printf(m, "flags:\t0x%08x\n", server_conf.flags);
seq_printf(m, "tcp_port:\t%u\n", server_conf.tcp_port);
seq_printf(m, "signing:\t%s\n", ksmbd_signing_mode_string());
seq_printf(m, "signing_enforced:\t%s\n",
server_conf.enforced_signing ? "yes" : "no");
seq_printf(m, "bind_interfaces_only:\t%s\n",
server_conf.bind_interfaces_only ? "yes" : "no");
seq_printf(m, "max_connections:\t%u\n", server_conf.max_connections);
seq_printf(m, "max_connections_per_ip:\t%u\n",
server_conf.max_ip_connections);
seq_printf(m, "max_inflight_requests:\t%u\n",
server_conf.max_inflight_req);
seq_printf(m, "deadtime_seconds:\t%lu\n", server_conf.deadtime / HZ);
seq_printf(m, "ipc_timeout_seconds:\t%u\n", server_conf.ipc_timeout / HZ);
if (server_conf.ipc_last_active)
seq_printf(m, "ipc_last_active_seconds:\t%lu\n",
jiffies_to_msecs(jiffies - server_conf.ipc_last_active) /
MSEC_PER_SEC);
else
seq_puts(m, "ipc_last_active_seconds:\tnever\n");
seq_printf(m, "durable_scavenger:\t%s\n",
ksmbd_durable_scavenger_active() ? "running" : "stopped");
seq_printf(m, "share_fake_fscaps:\t0x%08x\n",
server_conf.share_fake_fscaps);
proc_show_runtime_totals(m);
seq_printf(m, "sessions:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS));
seq_printf(m, "tree_connects:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS));
seq_printf(m, "requests:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_REQUESTS));
seq_printf(m, "read_bytes:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES));
seq_printf(m, "written_bytes:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_WRITE_BYTES));
seq_puts(m, "\nSMB2\n");
for (i = 0; i < KSMBD_COUNTER_MAX_REQS; i++)
seq_printf(m, "%s:\t%lld\n", smb2_process_req[i].name,
ksmbd_counter_sum(KSMBD_COUNTER_FIRST_REQ + i));
seq_puts(m, "\nSMB2 status\n");
seq_printf(m, "success:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_STATUS_SUCCESS));
seq_printf(m, "informational:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_STATUS_INFORMATIONAL));
seq_printf(m, "warning:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_STATUS_WARNING));
seq_printf(m, "error:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_STATUS_ERROR));
seq_printf(m, "access_denied:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_ERROR_ACCESS_DENIED));
seq_printf(m, "not_found:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_FOUND));
seq_printf(m, "invalid_parameter:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_ERROR_INVALID_PARAMETER));
seq_printf(m, "sharing_violation:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_ERROR_SHARING_VIOLATION));
seq_printf(m, "not_supported:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_ERROR_NOT_SUPPORTED));
seq_printf(m, "other:\t%lld\n",
ksmbd_counter_sum(KSMBD_COUNTER_ERROR_OTHER));
return 0;
}
void ksmbd_proc_cleanup(void)
{
int i;
if (!ksmbd_proc_fs)
return;
proc_remove(ksmbd_proc_fs);
for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++)
percpu_counter_destroy(&ksmbd_counters.counters[i]);
ksmbd_proc_fs = NULL;
}
void ksmbd_proc_reset(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++)
percpu_counter_set(&ksmbd_counters.counters[i], 0);
}
int ksmbd_proc_init(void)
{
int i;
int retval = -ENOMEM;
ksmbd_proc_fs = proc_mkdir("fs/ksmbd", NULL);
if (!ksmbd_proc_fs)
return retval;
if (!proc_mkdir_mode("sessions", 0400, ksmbd_proc_fs))
goto err_out;
for (i = 0; i < ARRAY_SIZE(ksmbd_counters.counters); i++) {
retval = percpu_counter_init(&ksmbd_counters.counters[i], 0, GFP_KERNEL);
if (retval)
goto err_out;
}
if (!ksmbd_proc_create("server", proc_show_ksmbd_stats, NULL)) {
retval = -ENOMEM;
goto err_out;
}
ksmbd_proc_reset();
return 0;
err_out:
ksmbd_proc_cleanup();
return retval;
}