]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ksmbd: add procfs interface for runtime monitoring and statistics
authorBahubali B Gumaji <bahubali.bg@samsung.com>
Thu, 5 Feb 2026 00:08:35 +0000 (09:08 +0900)
committerSteve French <stfrench@microsoft.com>
Mon, 9 Feb 2026 02:25:16 +0000 (20:25 -0600)
This patch introduces a /proc filesystem interface to ksmbd, providing
visibility into the internal state of the SMB server. This allows
administrators and developers to monitor active connections, user
sessions, and opened files in real-time without relying on external
tools or heavy debugging.

Key changes include:
 - Connection Monitoring (/proc/fs/ksmbd/clients): Displays a list of
   active network connections, including client IP addresses, SMB dialects,
   credits, and last active timestamps.

 - Session Management (/proc/fs/ksmbd/sessions/): Adds a global sessions
   file to list all authenticated users and their session IDs.

 - Creates individual session entries (e.g., /proc/fs/ksmbd/sessions/<id>)
   detailing capabilities (DFS, Multi-channel, etc.), signing/encryption
   algorithms, and connected tree shares.

 - File Tracking (/proc/fs/ksmbd/files): Shows all currently opened files
   across the server, including tree IDs, process IDs (PID), access modes
   (daccess/saccess), and oplock/lease states.

 - Statistics & Counters: Implements internal counters for global server
   metrics, such as the number of tree connections, total sessions, and
   processed read/write bytes.

Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Bahubali B Gumaji <bahubali.bg@samsung.com>
Signed-off-by: Sang-Soo Lee <constant.lee@samsung.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
18 files changed:
fs/smb/server/Makefile
fs/smb/server/connection.c
fs/smb/server/connection.h
fs/smb/server/mgmt/tree_connect.c
fs/smb/server/mgmt/user_config.c
fs/smb/server/mgmt/user_config.h
fs/smb/server/mgmt/user_session.c
fs/smb/server/mgmt/user_session.h
fs/smb/server/misc.h
fs/smb/server/proc.c [new file with mode: 0644]
fs/smb/server/server.c
fs/smb/server/smb2ops.c
fs/smb/server/smb2pdu.c
fs/smb/server/smb_common.c
fs/smb/server/smb_common.h
fs/smb/server/stats.h [new file with mode: 0644]
fs/smb/server/vfs.c
fs/smb/server/vfs_cache.c

index 7d6337a7dee40cef531ebe7d01e941de356db687..6407ba6b9340128987a6e2db8163d1ca65c4c2e7 100644 (file)
@@ -18,3 +18,4 @@ $(obj)/ksmbd_spnego_negtokeninit.asn1.o: $(obj)/ksmbd_spnego_negtokeninit.asn1.c
 $(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
index 6cac48c8fbe8e3af7440d9aebb4dc427a699d9f3..f0bd244e7d556cdc2f9df048a55b84394124b7f9 100644 (file)
@@ -14,6 +14,7 @@
 #include "connection.h"
 #include "transport_tcp.h"
 #include "transport_rdma.h"
+#include "misc.h"
 
 static DEFINE_MUTEX(init_lock);
 
@@ -22,6 +23,60 @@ static struct ksmbd_conn_ops default_conn_ops;
 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
  *
@@ -472,6 +527,7 @@ int ksmbd_conn_transport_init(void)
        }
 out:
        mutex_unlock(&init_lock);
+       create_proc_clients();
        return ret;
 }
 
@@ -502,6 +558,7 @@ again:
 
 void ksmbd_conn_transport_destroy(void)
 {
+       delete_proc_clients();
        mutex_lock(&init_lock);
        ksmbd_tcp_destroy();
        ksmbd_rdma_stop_listening();
index 7f9bcd9817b540336e75faf97e45dbaf80700011..1e2587036bca633b5454afc537e947cfddc06003 100644 (file)
@@ -7,6 +7,7 @@
 #define __KSMBD_CONNECTION_H__
 
 #include <linux/list.h>
+#include <linux/inet.h>
 #include <linux/ip.h>
 #include <net/sock.h>
 #include <net/tcp.h>
@@ -33,7 +34,7 @@ enum {
        KSMBD_SESS_RELEASING
 };
 
-struct ksmbd_stats {
+struct ksmbd_conn_stats {
        atomic_t                        open_files_count;
        atomic64_t                      request_served;
 };
@@ -78,7 +79,7 @@ struct ksmbd_conn {
        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;
 
index d3483d9c757c79b4d57450ed6f74fe5c73226100..62b97936b5451b2bb11223605df959a0cd762659 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "../transport_ipc.h"
 #include "../connection.h"
+#include "../stats.h"
 
 #include "tree_connect.h"
 #include "user_config.h"
@@ -85,6 +86,7 @@ ksmbd_tree_conn_connect(struct ksmbd_work *work, const char *share_name)
                status.ret = -ENOMEM;
                goto out_error;
        }
+       ksmbd_counter_inc(KSMBD_COUNTER_TREE_CONNS);
        kvfree(resp);
        return status;
 
@@ -115,6 +117,7 @@ int ksmbd_tree_conn_disconnect(struct ksmbd_session *sess,
        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;
index 56c9a38ca878903535cec6e3761a173df6ffe988..3267b86b8c16f672cd5b003c3a55f1398d9b3e41 100644 (file)
@@ -90,11 +90,9 @@ void ksmbd_free_user(struct ksmbd_user *user)
        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)
index 8c227b8d495431150a55ea91086951dc486f0c9b..cc460b4ff7d393b1ca52c5674233b8a5e7d17cf7 100644 (file)
@@ -65,6 +65,6 @@ struct ksmbd_user *ksmbd_login_user(const char *account);
 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__ */
index 7d880ff34402e0fe0cbddc0b0a070efac0658695..68b3e0cb54d38f0d7f29b94f9b7301f5514d4d08 100644 (file)
 #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);
 
@@ -27,6 +30,215 @@ struct ksmbd_session_rpc {
        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;
@@ -159,6 +371,8 @@ void ksmbd_session_destroy(struct ksmbd_session *sess)
        if (!sess)
                return;
 
+       delete_proc_session(sess);
+
        if (sess->user)
                ksmbd_free_user(sess->user);
 
@@ -465,6 +679,8 @@ static struct ksmbd_session *__session_create(int protocol)
        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:
index c5749d6ec7151caf826282f93f33701138eb2d77..176d800c2490657469738e0a7fc1ef461b9d20c0 100644 (file)
@@ -41,7 +41,6 @@ struct ksmbd_session {
 
        bool                            sign;
        bool                            enc;
-       bool                            is_anonymous;
 
        int                             state;
        __u8                            *Preauth_HashValue;
@@ -62,6 +61,9 @@ struct ksmbd_session {
        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;
 };
@@ -111,4 +113,5 @@ void ksmbd_session_rpc_close(struct ksmbd_session *sess, int id);
 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__ */
index 1facfcd21200fd1af3f4c062da4bfb9d0b3e69c4..13423696ae8c62f81f19e59607d378143347b673 100644 (file)
@@ -6,6 +6,9 @@
 #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;
@@ -34,4 +37,31 @@ char *ksmbd_convert_dir_info_name(struct ksmbd_dir_info *d_info,
 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__ */
diff --git a/fs/smb/server/proc.c b/fs/smb/server/proc.c
new file mode 100644 (file)
index 0000000..101a2cc
--- /dev/null
@@ -0,0 +1,134 @@
+// 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();
+}
index d2410a3f163aec875b316e0602143c35d6a262a0..c2c074346da125970322f9027f6b059fa649b751 100644 (file)
@@ -21,6 +21,8 @@
 #include "mgmt/user_session.h"
 #include "crypto_ctx.h"
 #include "auth.h"
+#include "misc.h"
+#include "stats.h"
 
 int ksmbd_debug_types;
 
@@ -145,6 +147,8 @@ andx_again:
        }
 
        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);
@@ -359,6 +363,7 @@ static void server_ctrl_handle_init(struct server_ctrl_struct *ctrl)
 {
        int ret;
 
+       ksmbd_proc_reset();
        ret = ksmbd_conn_transport_init();
        if (ret) {
                server_queue_ctrl_reset_work();
@@ -531,6 +536,7 @@ static int ksmbd_server_shutdown(void)
 {
        WRITE_ONCE(server_conf.state, SERVER_STATE_SHUTTING_DOWN);
 
+       ksmbd_proc_cleanup();
        class_unregister(&ksmbd_control_class);
        ksmbd_workqueue_destroy();
        ksmbd_ipc_release();
@@ -554,6 +560,9 @@ static int __init ksmbd_server_init(void)
                return ret;
        }
 
+       ksmbd_proc_init();
+       create_proc_sessions();
+
        ksmbd_server_tcp_callbacks_init();
 
        ret = server_conf_init();
index edd7eca0714adce0fdf09e480b882608cbe17c22..c9a32ee096b582b979effe109462f0f787710c48 100644 (file)
@@ -11,6 +11,7 @@
 #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,
@@ -121,6 +122,7 @@ static struct smb_version_values smb311_server_values = {
 
 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,
@@ -134,6 +136,7 @@ static struct smb_version_ops smb2_0_server_ops = {
 
 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,
@@ -152,6 +155,7 @@ static struct smb_version_ops smb3_0_server_ops = {
 
 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,
index 2fcd0d4d1fb0ded3b2541e1bbf48fa50997d9ad6..4d3154cc493eaef4c3be815ea5e8c24c3aa64e67 100644 (file)
@@ -39,6 +39,7 @@
 #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)
index 1cd7e738434d7f92752e0b95a8f0a2131468c28c..741aabdfcef5e2a71a97ca81487f3dd27179788b 100644 (file)
@@ -98,6 +98,30 @@ inline int ksmbd_max_protocol(void)
        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;
index ddd6867c50b2e66f4eb3301e0b84aa1a8d8f6586..ca7e3610d07477723485985784be66d32c62e194 100644 (file)
@@ -143,6 +143,7 @@ struct file_id_both_directory_info {
 
 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);
@@ -165,6 +166,7 @@ struct smb_version_cmds {
 
 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);
 
diff --git a/fs/smb/server/stats.h b/fs/smb/server/stats.h
new file mode 100644 (file)
index 0000000..b60c30c
--- /dev/null
@@ -0,0 +1,73 @@
+/* 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
index b8e648b8300f663fa876d285dae4307089c5428f..50cb772d38f200f46938b55ac987189594f6e181 100644 (file)
@@ -31,6 +31,7 @@
 #include "ndr.h"
 #include "auth.h"
 #include "misc.h"
+#include "stats.h"
 
 #include "smb_common.h"
 #include "mgmt/share_config.h"
@@ -384,6 +385,7 @@ int ksmbd_vfs_read(struct ksmbd_work *work, struct ksmbd_file *fp, size_t count,
        }
 
        filp->f_pos = *pos;
+       ksmbd_counter_add(KSMBD_COUNTER_READ_BYTES, (s64)nbytes);
        return nbytes;
 }
 
@@ -521,6 +523,7 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
                        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;
index 6ef116585af645df2a05cefc8de783b64007aada..e302e403e55a634eeb7a4b0c61c5112b251cef7a 100644 (file)
 #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
@@ -34,6 +36,97 @@ static struct ksmbd_file_table global_ft;
 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;
@@ -949,6 +1042,7 @@ void ksmbd_close_session_fds(struct ksmbd_work *work)
 
 int ksmbd_init_global_file_table(void)
 {
+       create_proc_files();
        return ksmbd_init_file_table(&global_ft);
 }