ksmbd: expose connection runtime state in procfs

The clients proc file currently shows only a small subset of the state
needed to diagnose stalled or mis-negotiated connections.

Report the transport, connection state, outstanding and total credits,
session count, lifetime request count, and negotiated signing, encryption,
compression, and POSIX features. Report each connection as a key/value
record rather than a wide fixed-width table.

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
This commit is contained in:
Namjae Jeon 2026-06-28 15:37:31 +09:00
parent 1f7dd03a88
commit fe4dc5987d

View File

@ -27,33 +27,105 @@ DECLARE_RWSEM(conn_list_lock);
#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_clients;
static const char *ksmbd_conn_state_string(struct ksmbd_conn *conn)
{
switch (READ_ONCE(conn->status)) {
case KSMBD_SESS_NEW:
return "new";
case KSMBD_SESS_GOOD:
return "good";
case KSMBD_SESS_EXITING:
return "exiting";
case KSMBD_SESS_NEED_RECONNECT:
return "reconnect";
case KSMBD_SESS_NEED_NEGOTIATE:
return "negotiate";
case KSMBD_SESS_NEED_SETUP:
return "setup";
case KSMBD_SESS_RELEASING:
return "releasing";
default:
return "unknown";
}
}
static const char *ksmbd_conn_transport_string(struct ksmbd_conn *conn)
{
if (conn->transport->ops->rdma_read || conn->transport->ops->rdma_write)
return "smbdirect";
return "tcp";
}
static void proc_show_conn_feature(struct seq_file *m, bool *separator,
bool enabled, const char *name)
{
if (!enabled)
return;
seq_printf(m, "%s%s", *separator ? "," : "", name);
*separator = true;
}
static void proc_show_conn_features(struct seq_file *m,
struct ksmbd_conn *conn)
{
bool separator = false;
proc_show_conn_feature(m, &separator,
conn->sign || conn->signing_negotiated, "sign");
proc_show_conn_feature(m, &separator, conn->cipher_type, "encrypt");
proc_show_conn_feature(m, &separator,
conn->compress_algorithm != SMB3_COMPRESS_NONE,
"compress");
proc_show_conn_feature(m, &separator, conn->posix_ext_supported, "posix");
if (!separator)
seq_puts(m, "none");
}
static int proc_show_clients(struct seq_file *m, void *v)
{
struct ksmbd_conn *conn;
struct timespec64 now, t;
int i;
seq_printf(m, "#%-40s %-10s %-10s %-12s %-10s %s\n",
"<client>", "<dialect>", "<credits>", "<open files>",
"<requests>", "<last active>");
down_read(&conn_list_lock);
hash_for_each(conn_list, i, conn, hlist) {
unsigned int outstanding_credits, total_credits;
unsigned long id;
void *entry;
unsigned int sessions = 0;
jiffies_to_timespec64(jiffies - conn->last_active, &t);
ktime_get_real_ts64(&now);
t = timespec64_sub(now, t);
spin_lock(&conn->credits_lock);
outstanding_credits = conn->outstanding_credits;
total_credits = conn->total_credits;
spin_unlock(&conn->credits_lock);
rcu_read_lock();
xa_for_each(&conn->sessions, id, entry)
sessions++;
rcu_read_unlock();
#if IS_ENABLED(CONFIG_IPV6)
if (!conn->inet_addr)
seq_printf(m, " %-40pI6c", &conn->inet6_addr);
seq_printf(m, "client:\t%pI6c\n", &conn->inet6_addr);
else
#endif
seq_printf(m, " %-40pI4", &conn->inet_addr);
seq_printf(m, " 0x%-8x %-10u %-12d %-10d %ptT\n",
conn->dialect,
conn->total_credits,
atomic_read(&conn->stats.open_files_count),
atomic_read(&conn->req_running),
&t);
seq_printf(m, "client:\t%pI4\n", &conn->inet_addr);
seq_printf(m, "transport:\t%s\n", ksmbd_conn_transport_string(conn));
seq_printf(m, "state:\t%s\n", ksmbd_conn_state_string(conn));
seq_printf(m, "dialect:\t0x%04x\n", conn->dialect);
seq_printf(m, "credits:\t%u/%u\n", outstanding_credits,
total_credits);
seq_printf(m, "sessions:\t%u\n", sessions);
seq_printf(m, "open_files:\t%d\n",
atomic_read(&conn->stats.open_files_count));
seq_printf(m, "requests:\t%lld\n",
atomic64_read(&conn->stats.request_served));
seq_puts(m, "features:\t");
proc_show_conn_features(m, conn);
seq_printf(m, "\nlast_active:\t%ptT\n\n", &t);
}
up_read(&conn_list_lock);
return 0;