mirror of
https://github.com/torvalds/linux.git
synced 2026-10-30 14:06:03 +01:00
ksmbd_server_init() calls ksmbd_proc_init() before creating the
remaining proc entries and server subsystems. ksmbd_proc_init() tears
down partial state on a procfs or percpu_counter allocation failure,
but returns void, so ksmbd_server_init() continues as if the counters
were usable.
Once userspace starts the server, server_ctrl_handle_init() calls
ksmbd_proc_reset(), which reaches percpu_counter_set() with a NULL
per-CPU counters pointer on SMP systems. The later ksmbd_proc_create()
calls also receive a NULL parent and may create entries in the /proc
root; ksmbd_proc_cleanup() cannot remove those entries because
ksmbd_proc_fs is NULL.
Fixes: b38f99c121 ("ksmbd: add procfs interface for runtime monitoring and statistics")
Signed-off-by: ZhangGuoDong <zhangguodong@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
271 lines
7.9 KiB
C
271 lines
7.9 KiB
C
// 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;
|
|
}
|