$(obj)/ksmbd_spnego_negtokentarg.asn1.o: $(obj)/ksmbd_spnego_negtokentarg.asn1.c $(obj)/ksmbd_spnego_negtokentarg.asn1.h
ksmbd-$(CONFIG_SMB_SERVER_SMBDIRECT) += transport_rdma.o
+ksmbd-$(CONFIG_PROC_FS) += proc.o
#include "connection.h"
#include "transport_tcp.h"
#include "transport_rdma.h"
+#include "misc.h"
static DEFINE_MUTEX(init_lock);
DEFINE_HASHTABLE(conn_list, CONN_HASH_BITS);
DECLARE_RWSEM(conn_list_lock);
+#ifdef CONFIG_PROC_FS
+static struct proc_dir_entry *proc_clients;
+
+static int proc_show_clients(struct seq_file *m, void *v)
+{
+ struct ksmbd_conn *conn;
+ struct timespec64 now, t;
+ int i;
+
+ seq_printf(m, "#%-20s %-10s %-10s %-10s %-10s %-10s\n",
+ "<name>", "<dialect>", "<credits>", "<open files>",
+ "<requests>", "<last active>");
+
+ down_read(&conn_list_lock);
+ hash_for_each(conn_list, i, conn, hlist) {
+ jiffies_to_timespec64(jiffies - conn->last_active, &t);
+ ktime_get_real_ts64(&now);
+ t = timespec64_sub(now, t);
+ if (conn->inet_addr)
+ seq_printf(m, "%-20pI4", &conn->inet_addr);
+ else
+ seq_printf(m, "%-20pI6c", &conn->inet6_addr);
+ seq_printf(m, " 0x%-10x %-10u %-12d %-10d %ptT\n",
+ conn->dialect,
+ conn->total_credits,
+ atomic_read(&conn->stats.open_files_count),
+ atomic_read(&conn->req_running),
+ &t);
+ }
+ up_read(&conn_list_lock);
+ return 0;
+}
+
+static int create_proc_clients(void)
+{
+ proc_clients = ksmbd_proc_create("clients",
+ proc_show_clients, NULL);
+ if (!proc_clients)
+ return -ENOMEM;
+ return 0;
+}
+
+static void delete_proc_clients(void)
+{
+ if (proc_clients) {
+ proc_remove(proc_clients);
+ proc_clients = NULL;
+ }
+}
+#else
+static int create_proc_clients(void) { return 0; }
+static void delete_proc_clients(void) {}
+#endif
+
/**
* ksmbd_conn_free() - free resources of the connection instance
*
}
out:
mutex_unlock(&init_lock);
+ create_proc_clients();
return ret;
}
void ksmbd_conn_transport_destroy(void)
{
+ delete_proc_clients();
mutex_lock(&init_lock);
ksmbd_tcp_destroy();
ksmbd_rdma_stop_listening();
#define __KSMBD_CONNECTION_H__
#include <linux/list.h>
+#include <linux/inet.h>
#include <linux/ip.h>
#include <net/sock.h>
#include <net/tcp.h>
KSMBD_SESS_RELEASING
};
-struct ksmbd_stats {
+struct ksmbd_conn_stats {
atomic_t open_files_count;
atomic64_t request_served;
};
struct list_head requests;
struct list_head async_requests;
int connection_type;
- struct ksmbd_stats stats;
+ struct ksmbd_conn_stats stats;
char ClientGUID[SMB2_CLIENT_GUID_SIZE];
struct ntlmssp_auth ntlmssp;
#include "../transport_ipc.h"
#include "../connection.h"
+#include "../stats.h"
#include "tree_connect.h"
#include "user_config.h"
status.ret = -ENOMEM;
goto out_error;
}
+ ksmbd_counter_inc(KSMBD_COUNTER_TREE_CONNS);
kvfree(resp);
return status;
ret = ksmbd_ipc_tree_disconnect_request(sess->id, tree_conn->id);
ksmbd_release_tree_conn_id(sess, tree_conn->id);
ksmbd_share_config_put(tree_conn->share_conf);
+ ksmbd_counter_dec(KSMBD_COUNTER_TREE_CONNS);
if (atomic_dec_and_test(&tree_conn->refcount))
kfree(tree_conn);
return ret;
kfree(user);
}
-int ksmbd_anonymous_user(struct ksmbd_user *user)
+bool ksmbd_anonymous_user(struct ksmbd_user *user)
{
- if (user->name[0] == '\0')
- return 1;
- return 0;
+ return user->name[0] == '\0';
}
bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2)
struct ksmbd_user *ksmbd_alloc_user(struct ksmbd_login_response *resp,
struct ksmbd_login_response_ext *resp_ext);
void ksmbd_free_user(struct ksmbd_user *user);
-int ksmbd_anonymous_user(struct ksmbd_user *user);
+bool ksmbd_anonymous_user(struct ksmbd_user *user);
bool ksmbd_compare_user(struct ksmbd_user *u1, struct ksmbd_user *u2);
#endif /* __USER_CONFIG_MANAGEMENT_H__ */
#include "user_session.h"
#include "user_config.h"
#include "tree_connect.h"
+#include "share_config.h"
#include "../transport_ipc.h"
#include "../connection.h"
#include "../vfs_cache.h"
+#include "../misc.h"
+#include "../stats.h"
static DEFINE_IDA(session_ida);
unsigned int method;
};
+#ifdef CONFIG_PROC_FS
+
+static const struct ksmbd_const_name ksmbd_sess_cap_const_names[] = {
+ {SMB2_GLOBAL_CAP_DFS, "dfs"},
+ {SMB2_GLOBAL_CAP_LEASING, "lease"},
+ {SMB2_GLOBAL_CAP_LARGE_MTU, "large-mtu"},
+ {SMB2_GLOBAL_CAP_MULTI_CHANNEL, "multi-channel"},
+ {SMB2_GLOBAL_CAP_PERSISTENT_HANDLES, "persistent-handles"},
+ {SMB2_GLOBAL_CAP_DIRECTORY_LEASING, "dir-lease"},
+ {SMB2_GLOBAL_CAP_ENCRYPTION, "encryption"}
+};
+
+static const struct ksmbd_const_name ksmbd_cipher_const_names[] = {
+ {le16_to_cpu(SMB2_ENCRYPTION_AES128_CCM), "aes128-ccm"},
+ {le16_to_cpu(SMB2_ENCRYPTION_AES128_GCM), "aes128-gcm"},
+ {le16_to_cpu(SMB2_ENCRYPTION_AES256_CCM), "aes256-ccm"},
+ {le16_to_cpu(SMB2_ENCRYPTION_AES256_GCM), "aes256-gcm"},
+};
+
+static const struct ksmbd_const_name ksmbd_signing_const_names[] = {
+ {SIGNING_ALG_HMAC_SHA256, "hmac-sha256"},
+ {SIGNING_ALG_AES_CMAC, "aes-cmac"},
+ {SIGNING_ALG_AES_GMAC, "aes-gmac"},
+};
+
+static const char *session_state_string(struct ksmbd_session *session)
+{
+ switch (session->state) {
+ case SMB2_SESSION_VALID:
+ return "valid";
+ case SMB2_SESSION_IN_PROGRESS:
+ return "progress";
+ case SMB2_SESSION_EXPIRED:
+ return "expired";
+ default:
+ return "";
+ }
+}
+
+static const char *session_user_name(struct ksmbd_session *session)
+{
+ if (user_guest(session->user))
+ return "(Guest)";
+ else if (ksmbd_anonymous_user(session->user))
+ return "(Anonymous)";
+ return session->user->name;
+}
+
+static int show_proc_session(struct seq_file *m, void *v)
+{
+ struct ksmbd_session *sess;
+ struct ksmbd_tree_connect *tree_conn;
+ struct ksmbd_share_config *share_conf;
+ struct channel *chan;
+ unsigned long id;
+ int i = 0;
+
+ sess = (struct ksmbd_session *)m->private;
+ ksmbd_user_session_get(sess);
+
+ i = 0;
+ xa_for_each(&sess->ksmbd_chann_list, id, chan) {
+#if IS_ENABLED(CONFIG_IPV6)
+ if (chan->conn->inet_addr)
+ seq_printf(m, "%-20s\t%pI4\n", "client",
+ &chan->conn->inet_addr);
+ else
+ seq_printf(m, "%-20s\t%pI6c\n", "client",
+ &chan->conn->inet6_addr);
+#else
+ seq_printf(m, "%-20s\t%pI4\n", "client",
+ &chan->conn->inet_addr);
+#endif
+ seq_printf(m, "%-20s\t%s\n", "user", session_user_name(sess));
+ seq_printf(m, "%-20s\t%llu\n", "id", sess->id);
+ seq_printf(m, "%-20s\t%s\n", "state",
+ session_state_string(sess));
+
+ seq_printf(m, "%-20s\t", "capabilities");
+ ksmbd_proc_show_flag_names(m,
+ ksmbd_sess_cap_const_names,
+ ARRAY_SIZE(ksmbd_sess_cap_const_names),
+ chan->conn->vals->req_capabilities);
+
+ if (sess->sign) {
+ seq_printf(m, "%-20s\t", "signing");
+ ksmbd_proc_show_const_name(m, "%s\t",
+ ksmbd_signing_const_names,
+ ARRAY_SIZE(ksmbd_signing_const_names),
+ le16_to_cpu(chan->conn->signing_algorithm));
+ } else if (sess->enc) {
+ seq_printf(m, "%-20s\t", "encryption");
+ ksmbd_proc_show_const_name(m, "%s\t",
+ ksmbd_cipher_const_names,
+ ARRAY_SIZE(ksmbd_cipher_const_names),
+ le16_to_cpu(chan->conn->cipher_type));
+ }
+ i++;
+ }
+
+ seq_printf(m, "%-20s\t%d\n", "channels", i);
+
+ i = 0;
+ xa_for_each(&sess->tree_conns, id, tree_conn) {
+ share_conf = tree_conn->share_conf;
+ seq_printf(m, "%-20s\t%s\t%8d", "share",
+ share_conf->name, tree_conn->id);
+ if (test_share_config_flag(share_conf, KSMBD_SHARE_FLAG_PIPE))
+ seq_printf(m, " %s ", "pipe");
+ else
+ seq_printf(m, " %s ", "disk");
+ seq_putc(m, '\n');
+ }
+
+ ksmbd_user_session_put(sess);
+ return 0;
+}
+
+void ksmbd_proc_show_flag_names(struct seq_file *m,
+ const struct ksmbd_const_name *table,
+ int count,
+ unsigned int flags)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ if (table[i].const_value & flags)
+ seq_printf(m, "0x%08x\t", table[i].const_value);
+ }
+ seq_putc(m, '\n');
+}
+
+void ksmbd_proc_show_const_name(struct seq_file *m,
+ const char *format,
+ 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)
+ seq_printf(m, format, table[i].name);
+ }
+ seq_putc(m, '\n');
+}
+
+static int create_proc_session(struct ksmbd_session *sess)
+{
+ char name[30];
+
+ snprintf(name, sizeof(name), "sessions/%llu", sess->id);
+ sess->proc_entry = ksmbd_proc_create(name,
+ show_proc_session, sess);
+ return 0;
+}
+
+static void delete_proc_session(struct ksmbd_session *sess)
+{
+ if (sess->proc_entry)
+ proc_remove(sess->proc_entry);
+}
+
+static int show_proc_sessions(struct seq_file *m, void *v)
+{
+ struct ksmbd_session *session;
+ struct channel *chan;
+ int i;
+ unsigned long id;
+
+ seq_printf(m, "#%-40s %-15s %-10s %-10s\n",
+ "<client>", "<user>", "<sess_id>", "<state>");
+
+ down_read(&sessions_table_lock);
+ hash_for_each(sessions_table, i, session, hlist) {
+ xa_for_each(&session->ksmbd_chann_list, id, chan) {
+ down_read(&chan->conn->session_lock);
+ ksmbd_user_session_get(session);
+
+ if (chan->conn->inet_addr)
+ seq_printf(m, " %-40pI4", &chan->conn->inet_addr);
+ else
+ seq_printf(m, " %-40pI6c", &chan->conn->inet6_addr);
+ seq_printf(m, " %-15s %-10llu %-10s\n",
+ session_user_name(session),
+ session->id,
+ session_state_string(session));
+
+ ksmbd_user_session_put(session);
+ up_read(&chan->conn->session_lock);
+ }
+ }
+ up_read(&sessions_table_lock);
+ return 0;
+}
+
+int create_proc_sessions(void)
+{
+ if (!ksmbd_proc_create("sessions/sessions",
+ show_proc_sessions, NULL))
+ return -ENOMEM;
+ return 0;
+}
+#else
+int create_proc_sessions(void) { return 0; }
+static int create_proc_session(struct ksmbd_session *sess) { return 0; }
+static void delete_proc_session(struct ksmbd_session *sess) {}
+#endif
+
static void free_channel_list(struct ksmbd_session *sess)
{
struct channel *chann;
if (!sess)
return;
+ delete_proc_session(sess);
+
if (sess->user)
ksmbd_free_user(sess->user);
hash_add(sessions_table, &sess->hlist, sess->id);
up_write(&sessions_table_lock);
+ create_proc_session(sess);
+ ksmbd_counter_inc(KSMBD_COUNTER_SESSIONS);
return sess;
error:
bool sign;
bool enc;
- bool is_anonymous;
int state;
__u8 *Preauth_HashValue;
unsigned long last_active;
rwlock_t tree_conns_lock;
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *proc_entry;
+#endif
atomic_t refcnt;
struct rw_semaphore rpc_lock;
};
int ksmbd_session_rpc_method(struct ksmbd_session *sess, int id);
void ksmbd_user_session_get(struct ksmbd_session *sess);
void ksmbd_user_session_put(struct ksmbd_session *sess);
+int create_proc_sessions(void);
#endif /* __USER_SESSION_MANAGEMENT_H__ */
#ifndef __KSMBD_MISC_H__
#define __KSMBD_MISC_H__
+#ifdef CONFIG_PROC_FS
+#include <linux/proc_fs.h>
+#endif
struct ksmbd_share_config;
struct nls_table;
struct kstat;
struct timespec64 ksmbd_NTtimeToUnix(__le64 ntutc);
u64 ksmbd_UnixTimeToNT(struct timespec64 t);
long long ksmbd_systime(void);
+
+#ifdef CONFIG_PROC_FS
+struct ksmbd_const_name {
+ unsigned int const_value;
+ const char *name;
+};
+
+void ksmbd_proc_init(void);
+void ksmbd_proc_cleanup(void);
+void ksmbd_proc_reset(void);
+struct proc_dir_entry *ksmbd_proc_create(const char *name,
+ int (*show)(struct seq_file *m, void *v),
+ void *v);
+void ksmbd_proc_show_flag_names(struct seq_file *m,
+ const struct ksmbd_const_name *table,
+ int count,
+ unsigned int flags);
+void ksmbd_proc_show_const_name(struct seq_file *m,
+ const char *format,
+ const struct ksmbd_const_name *table,
+ int count,
+ unsigned int const_value);
+#else
+static inline void ksmbd_proc_init(void) {}
+static inline void ksmbd_proc_cleanup(void) {}
+static inline void ksmbd_proc_reset(void) {}
+#endif
#endif /* __KSMBD_MISC_H__ */
--- /dev/null
+// 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 "server.h"
+#include "stats.h"
+#include "smb_common.h"
+#include "smb2pdu.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);
+}
+
+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 int proc_show_ksmbd_stats(struct seq_file *m, void *v)
+{
+ int i;
+
+ seq_puts(m, "Server\n");
+ seq_printf(m, "name: %s\n", ksmbd_server_string());
+ seq_printf(m, "netbios: %s\n", ksmbd_netbios_name());
+ seq_printf(m, "work group: %s\n", ksmbd_work_group());
+ seq_printf(m, "min protocol: %s\n", ksmbd_get_protocol_string(server_conf.min_protocol));
+ seq_printf(m, "max protocol: %s\n", ksmbd_get_protocol_string(server_conf.max_protocol));
+ seq_printf(m, "flags: 0x%08x\n", server_conf.flags);
+ seq_printf(m, "share_fake_fscaps: 0x%08x\n",
+ server_conf.share_fake_fscaps);
+ seq_printf(m, "sessions: %lld\n",
+ ksmbd_counter_sum(KSMBD_COUNTER_SESSIONS));
+ seq_printf(m, "tree connects: %lld\n",
+ ksmbd_counter_sum(KSMBD_COUNTER_TREE_CONNS));
+ seq_printf(m, "read bytes: %lld\n",
+ ksmbd_counter_sum(KSMBD_COUNTER_READ_BYTES));
+ seq_printf(m, "written bytes: %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, "%-20s:\t%lld\n", smb2_process_req[i].name,
+ ksmbd_counter_sum(KSMBD_COUNTER_FIRST_REQ + i));
+ 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);
+}
+
+void ksmbd_proc_init(void)
+{
+ int i;
+ int retval;
+
+ ksmbd_proc_fs = proc_mkdir("fs/ksmbd", NULL);
+ if (!ksmbd_proc_fs)
+ return;
+
+ 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))
+ goto err_out;
+
+ ksmbd_proc_reset();
+ return;
+err_out:
+ ksmbd_proc_cleanup();
+}
#include "mgmt/user_session.h"
#include "crypto_ctx.h"
#include "auth.h"
+#include "misc.h"
+#include "stats.h"
int ksmbd_debug_types;
}
ret = cmds->proc(work);
+ if (conn->ops->inc_reqs)
+ conn->ops->inc_reqs(command);
if (ret < 0)
ksmbd_debug(CONN, "Failed to process %u [%d]\n", command, ret);
{
int ret;
+ ksmbd_proc_reset();
ret = ksmbd_conn_transport_init();
if (ret) {
server_queue_ctrl_reset_work();
{
WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN);
+ ksmbd_proc_cleanup();
class_unregister(&ksmbd_control_class);
ksmbd_workqueue_destroy();
ksmbd_ipc_release();
return ret;
}
+ ksmbd_proc_init();
+ create_proc_sessions();
+
ksmbd_server_tcp_callbacks_init();
ret = server_conf_init();
#include "connection.h"
#include "smb_common.h"
#include "server.h"
+#include "stats.h"
static struct smb_version_values smb21_server_values = {
.version_string = SMB21_VERSION_STRING,
static struct smb_version_ops smb2_0_server_ops = {
.get_cmd_val = get_smb2_cmd_val,
+ .inc_reqs = ksmbd_counter_inc_reqs,
.init_rsp_hdr = init_smb2_rsp_hdr,
.set_rsp_status = set_smb2_rsp_status,
.allocate_rsp_buf = smb2_allocate_rsp_buf,
static struct smb_version_ops smb3_0_server_ops = {
.get_cmd_val = get_smb2_cmd_val,
+ .inc_reqs = ksmbd_counter_inc_reqs,
.init_rsp_hdr = init_smb2_rsp_hdr,
.set_rsp_status = set_smb2_rsp_status,
.allocate_rsp_buf = smb2_allocate_rsp_buf,
static struct smb_version_ops smb3_11_server_ops = {
.get_cmd_val = get_smb2_cmd_val,
+ .inc_reqs = ksmbd_counter_inc_reqs,
.init_rsp_hdr = init_smb2_rsp_hdr,
.set_rsp_status = set_smb2_rsp_status,
.allocate_rsp_buf = smb2_allocate_rsp_buf,
#include "mgmt/user_session.h"
#include "mgmt/ksmbd_ida.h"
#include "ndr.h"
+#include "stats.h"
#include "transport_tcp.h"
static void __wbuf(struct ksmbd_work *work, void **req, void **rsp)
return SMB311_PROT;
}
+static const struct {
+ int version;
+ const char *string;
+} version_strings[] = {
+#ifdef CONFIG_SMB_INSECURE_SERVER
+ {SMB1_PROT, SMB1_VERSION_STRING},
+#endif
+ {SMB2_PROT, SMB20_VERSION_STRING},
+ {SMB21_PROT, SMB21_VERSION_STRING},
+ {SMB30_PROT, SMB30_VERSION_STRING},
+ {SMB302_PROT, SMB302_VERSION_STRING},
+ {SMB311_PROT, SMB311_VERSION_STRING},
+};
+
+const char *ksmbd_get_protocol_string(int version)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(version_strings); i++) {
+ if (version_strings[i].version == version)
+ return version_strings[i].string;
+ }
+ return "";
+}
int ksmbd_lookup_protocol_idx(char *str)
{
int offt = ARRAY_SIZE(smb1_protos) - 1;
struct smb_version_ops {
u16 (*get_cmd_val)(struct ksmbd_work *swork);
+ void (*inc_reqs)(unsigned int cmd);
int (*init_rsp_hdr)(struct ksmbd_work *swork);
void (*set_rsp_status)(struct ksmbd_work *swork, __le32 err);
int (*allocate_rsp_buf)(struct ksmbd_work *work);
int ksmbd_min_protocol(void);
int ksmbd_max_protocol(void);
+const char *ksmbd_get_protocol_string(int version);
int ksmbd_lookup_protocol_idx(char *str);
--- /dev/null
+/* 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>
+ */
+
+#ifndef __KSMBD_STATS_H__
+#define __KSMBD_STATS_H__
+
+#define KSMBD_COUNTER_MAX_REQS 19
+
+enum {
+ KSMBD_COUNTER_SESSIONS = 0,
+ KSMBD_COUNTER_TREE_CONNS,
+ KSMBD_COUNTER_REQUESTS,
+ KSMBD_COUNTER_READ_BYTES,
+ KSMBD_COUNTER_WRITE_BYTES,
+ KSMBD_COUNTER_FIRST_REQ,
+ KSMBD_COUNTER_LAST_REQ = KSMBD_COUNTER_FIRST_REQ +
+ KSMBD_COUNTER_MAX_REQS - 1,
+ KSMBD_COUNTER_MAX,
+};
+
+#ifdef CONFIG_PROC_FS
+extern struct ksmbd_counters ksmbd_counters;
+
+struct ksmbd_counters {
+ struct percpu_counter counters[KSMBD_COUNTER_MAX];
+};
+
+static inline void ksmbd_counter_inc(int type)
+{
+ percpu_counter_inc(&ksmbd_counters.counters[type]);
+}
+
+static inline void ksmbd_counter_dec(int type)
+{
+ percpu_counter_dec(&ksmbd_counters.counters[type]);
+}
+
+static inline void ksmbd_counter_add(int type, s64 value)
+{
+ percpu_counter_add(&ksmbd_counters.counters[type], value);
+}
+
+static inline void ksmbd_counter_sub(int type, s64 value)
+{
+ percpu_counter_sub(&ksmbd_counters.counters[type], value);
+}
+
+static inline void ksmbd_counter_inc_reqs(unsigned int cmd)
+{
+ if (cmd < KSMBD_COUNTER_MAX_REQS)
+ percpu_counter_inc(&ksmbd_counters.counters[KSMBD_COUNTER_FIRST_REQ + cmd]);
+}
+
+static inline s64 ksmbd_counter_sum(int type)
+{
+ return percpu_counter_sum_positive(&ksmbd_counters.counters[type]);
+}
+#else
+
+static inline void ksmbd_counter_inc(int type) {}
+static inline void ksmbd_counter_dec(int type) {}
+static inline void ksmbd_counter_add(int type, s64 value) {}
+static inline void ksmbd_counter_sub(int type, s64 value) {}
+static inline void ksmbd_counter_inc_reqs(unsigned int cmd) {}
+static inline s64 ksmbd_counter_sum(int type) { return 0; }
+#endif
+
+#endif
#include "ndr.h"
#include "auth.h"
#include "misc.h"
+#include "stats.h"
#include "smb_common.h"
#include "mgmt/share_config.h"
}
filp->f_pos = *pos;
+ ksmbd_counter_add(KSMBD_COUNTER_READ_BYTES, (s64)nbytes);
return nbytes;
}
pr_err("fsync failed for filename = %pD, err = %d\n",
fp->filp, err);
}
+ ksmbd_counter_add(KSMBD_COUNTER_WRITE_BYTES, (s64)*written);
out:
return err;
#include "oplock.h"
#include "vfs.h"
#include "connection.h"
+#include "misc.h"
#include "mgmt/tree_connect.h"
#include "mgmt/user_session.h"
#include "smb_common.h"
#include "server.h"
+#include "smb2pdu.h"
#define S_DEL_PENDING 1
#define S_DEL_ON_CLS 2
static atomic_long_t fd_limit;
static struct kmem_cache *filp_cache;
+#define OPLOCK_NONE 0
+#define OPLOCK_EXCLUSIVE 1
+#define OPLOCK_BATCH 2
+#define OPLOCK_READ 3 /* level 2 oplock */
+
+#ifdef CONFIG_PROC_FS
+
+static const struct ksmbd_const_name ksmbd_lease_const_names[] = {
+ {le32_to_cpu(SMB2_LEASE_NONE_LE), "LEASE_NONE"},
+ {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE), "LEASE_R"},
+ {le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_H"},
+ {le32_to_cpu(SMB2_LEASE_WRITE_CACHING_LE), "LEASE_W"},
+ {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE |
+ SMB2_LEASE_HANDLE_CACHING_LE), "LEASE_RH"},
+ {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE |
+ SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RW"},
+ {le32_to_cpu(SMB2_LEASE_HANDLE_CACHING_LE |
+ SMB2_LEASE_WRITE_CACHING_LE), "LEASE_WH"},
+ {le32_to_cpu(SMB2_LEASE_READ_CACHING_LE |
+ SMB2_LEASE_HANDLE_CACHING_LE |
+ SMB2_LEASE_WRITE_CACHING_LE), "LEASE_RWH"},
+};
+
+static const struct ksmbd_const_name ksmbd_oplock_const_names[] = {
+ {SMB2_OPLOCK_LEVEL_NONE, "OPLOCK_NONE"},
+ {SMB2_OPLOCK_LEVEL_II, "OPLOCK_II"},
+ {SMB2_OPLOCK_LEVEL_EXCLUSIVE, "OPLOCK_EXECL"},
+ {SMB2_OPLOCK_LEVEL_BATCH, "OPLOCK_BATCH"},
+};
+
+static int proc_show_files(struct seq_file *m, void *v)
+{
+ struct ksmbd_file *fp = NULL;
+ unsigned int id;
+ struct oplock_info *opinfo;
+
+ seq_printf(m, "#%-10s %-10s %-10s %-10s %-15s %-10s %-10s %s\n",
+ "<tree id>", "<pid>", "<vid>", "<refcnt>",
+ "<oplock>", "<daccess>", "<saccess>",
+ "<name>");
+
+ read_lock(&global_ft.lock);
+ idr_for_each_entry(global_ft.idr, fp, id) {
+ seq_printf(m, "%#-10x %#-10llx %#-10llx %#-10x",
+ fp->tcon->id,
+ fp->persistent_id,
+ fp->volatile_id,
+ atomic_read(&fp->refcount));
+
+ rcu_read_lock();
+ opinfo = rcu_dereference(fp->f_opinfo);
+ rcu_read_unlock();
+
+ if (!opinfo) {
+ seq_printf(m, " %-15s", " ");
+ } else {
+ const struct ksmbd_const_name *const_names;
+ int count;
+ unsigned int level;
+
+ if (opinfo->is_lease) {
+ const_names = ksmbd_lease_const_names;
+ count = ARRAY_SIZE(ksmbd_lease_const_names);
+ level = le32_to_cpu(opinfo->o_lease->state);
+ } else {
+ const_names = ksmbd_oplock_const_names;
+ count = ARRAY_SIZE(ksmbd_oplock_const_names);
+ level = opinfo->level;
+ }
+ ksmbd_proc_show_const_name(m, " %-15s",
+ const_names, count, level);
+ }
+
+ seq_printf(m, " %#010x %#010x %s\n",
+ le32_to_cpu(fp->daccess),
+ le32_to_cpu(fp->saccess),
+ fp->filp->f_path.dentry->d_name.name);
+ }
+ read_unlock(&global_ft.lock);
+ return 0;
+}
+
+static int create_proc_files(void)
+{
+ ksmbd_proc_create("files", proc_show_files, NULL);
+ return 0;
+}
+#else
+static int create_proc_files(void) { return 0; }
+#endif
+
static bool durable_scavenger_running;
static DEFINE_MUTEX(durable_scavenger_lock);
static wait_queue_head_t dh_wq;
int ksmbd_init_global_file_table(void)
{
+ create_proc_files();
return ksmbd_init_file_table(&global_ft);
}