FAILED: patch "[PATCH] ksmbd: reset rcount per connection in" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x def036ef87f8641c1c525d5ae17438d7a1006491
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026042410-cinch-frostbite-b760@gregkh' --subject-prefix 'PATCH 6.12.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From def036ef87f8641c1c525d5ae17438d7a1006491 Mon Sep 17 00:00:00 2001
From: DaeMyung Kang <[email protected]>
Date: Sun, 19 Apr 2026 02:28:44 +0900
Subject: [PATCH] ksmbd: reset rcount per connection in
ksmbd_conn_wait_idle_sess_id()
rcount is intended to be connection-specific: 2 for curr_conn, 1 for every other connection sharing the same session. However, it is initialised only once before the hash iteration and is never reset. After the loop visits curr_conn, later sibling connections are also checked against rcount == 2, so a sibling with req_running == 1 is incorrectly treated as idle. This makes the outcome depend on the hash iteration order: whether a given sibling is checked against the loose (< 2) or the strict (< 1) threshold is decided by whether it happens to be visited before or after curr_conn.
The function's contract is "wait until every connection sharing this session is idle" so that destroy_previous_session() can safely tear the session down. The latched rcount violates that contract and reopens the teardown race window the wait logic was meant to close:destroy_previous_session() may proceed before sibling channels have actually quiesced, overlapping session teardown with in-flight work on those connections.
Recompute rcount inside the loop so each connection is compared against its own threshold regardless of iteration order.
This is a code-inspection fix for an iteration-order-dependent logic error; a targeted reproducer would require SMB3 multichannel with in-flight work on a sibling channel landing after curr_conn in hash order, which is not something that can be triggered reliably.
fs/smb/server/connection.c +2 -3
@@ -237,7 +237,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id) {struct ksmbd_conn *conn;int rc, retry_count =0 , max_timeout =120 ; -int rcount =1 , bkt; +int rcount, bkt; retry_idle:if (retry_count >= max_timeout)
@@ -246,8 +246,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)down_read (&conn_list_lock);hash_for_each (conn_list, bkt, conn, hlist) {if (conn->binding ||xa_load (&conn->sessions, sess_id)) { -if (conn == curr_conn) - rcount =2 ; + rcount = (conn == curr_conn) ?2 :1 ;if (atomic_read (&conn->req_running) >= rcount) { rc = wait_event_timeout(conn->req_running_q, atomic_read(&conn->req_running) < rcount,
Sasha Levin
maintainerPATCH 1/3 ksmbd: use msleep instaed of schedule_timeout_interruptible()
From: Namjae Jeon <[email protected]>
[ Upstream commit f75f8bdd4ff4830abe31a1b94892eb12b85b9535 ]
use msleep instaed of schedule_timeout_interruptible() to guarantee the task delays as expected.
Stable-dep-of: def036ef87f8 ("ksmbd: reset rcount per connection in ksmbd_conn_wait_idle_sess_id()")
--- fs/smb/server/connection.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/smb/server/connection.c +1 -1
@@ -495,7 +495,7 @@ static void stop_sessions(void)up_read (&conn_list_lock);if (!list_empty (&conn_list)) { -schedule_timeout_interruptible (HZ /10 );/* 100ms */ +msleep (100 ); goto again; } }
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 ksmbd: replace connection list with hash table
From: Namjae Jeon <[email protected]>
[ Upstream commit 0bcc831be535269556f59cb70396f7e34f03a276 ]
Replace connection list with hash table to improve lookup performance.
Stable-dep-of: def036ef87f8 ("ksmbd: reset rcount per connection in ksmbd_conn_wait_idle_sess_id()")
--- fs/smb/server/connection.c | 23 +++++++++++------------ fs/smb/server/connection.h | 6 ++++-- fs/smb/server/smb2pdu.c | 4 ++-- fs/smb/server/transport_rdma.c | 5 +++++ fs/smb/server/transport_tcp.c | 25 +++++++++++++++++++++---- 5 files changed, 43 insertions(+), 20 deletions(-)
fs/smb/server/connection.c +11 -12
@@ -19,7 +19,7 @@ static DEFINE_MUTEX(init_lock);static struct ksmbd_conn_ops default_conn_ops; -LIST_HEAD (conn_list); +DEFINE_HASHTABLE (conn_list,CONN_HASH_BITS );DECLARE_RWSEM (conn_list_lock);/**
@@ -33,7 +33,7 @@ DECLARE_RWSEM(conn_list_lock);void ksmbd_conn_free (struct ksmbd_conn *conn) {down_write (&conn_list_lock); -list_del (&conn->conns_list); +hash_del (&conn->hlist);up_write (&conn_list_lock);xa_destroy (&conn->sessions);
@@ -78,7 +78,6 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)init_waitqueue_head (&conn->req_running_q);init_waitqueue_head (&conn->r_count_q); -INIT_LIST_HEAD (&conn->conns_list);INIT_LIST_HEAD (&conn->requests);INIT_LIST_HEAD (&conn->async_requests);spin_lock_init (&conn->request_lock);
@@ -91,19 +90,17 @@ struct ksmbd_conn *ksmbd_conn_alloc(void)init_rwsem (&conn->session_lock); -down_write (&conn_list_lock); -list_add (&conn->conns_list, &conn_list); -up_write (&conn_list_lock);return conn; }bool ksmbd_conn_lookup_dialect (struct ksmbd_conn *c) {struct ksmbd_conn *t; +int bkt;bool ret = false;down_read (&conn_list_lock); -list_for_each_entry (t, &conn_list, conns_list) { +hash_for_each (conn_list, bkt, t, hlist) {if (memcmp (t->ClientGUID, c->ClientGUID,SMB2_CLIENT_GUID_SIZE ))continue ;
@@ -164,9 +161,10 @@ void ksmbd_conn_unlock(struct ksmbd_conn *conn)void ksmbd_all_conn_set_status (u64 sess_id,u32 status) {struct ksmbd_conn *conn; +int bkt;down_read (&conn_list_lock); -list_for_each_entry (conn, &conn_list, conns_list) { +hash_for_each (conn_list, bkt, conn, hlist) {if (conn->binding ||xa_load (&conn->sessions, sess_id))WRITE_ONCE (conn->status, status); }
@@ -182,14 +180,14 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id) {struct ksmbd_conn *conn;int rc, retry_count =0 , max_timeout =120 ; -int rcount =1 ; +int rcount =1 , bkt; retry_idle:if (retry_count >= max_timeout)return -EIO ;down_read (&conn_list_lock); -list_for_each_entry (conn, &conn_list, conns_list) { +hash_for_each (conn_list, bkt, conn, hlist) {if (conn->binding ||xa_load (&conn->sessions, sess_id)) {if (conn == curr_conn) rcount =2 ;
@@ -480,10 +478,11 @@ static void stop_sessions(void) {struct ksmbd_conn *conn;struct ksmbd_transport *t; +int bkt; again:down_read (&conn_list_lock); -list_for_each_entry (conn, &conn_list, conns_list) { +hash_for_each (conn_list, bkt, conn, hlist) { t = conn->transport;ksmbd_conn_set_exiting (conn);if (t->ops->shutdown) {
@@ -494,7 +493,7 @@ static void stop_sessions(void) }up_read (&conn_list_lock); -if (!list_empty (&conn_list)) { +if (!hash_empty (conn_list)) {msleep (100 ); goto again; }
fs/smb/server/connection.h +4 -2
@@ -52,11 +52,12 @@ struct ksmbd_conn {u8 inet6_addr[16 ];#endif }; +unsigned int inet_hash;char *request_buf;struct ksmbd_transport *transport;struct nls_table *local_nls;struct unicode_map *um; -struct list_head conns_list; +struct hlist_node hlist;struct rw_semaphore session_lock;/* smb session 1 per user */ struct xarray sessions;
@@ -151,7 +152,8 @@ struct ksmbd_transport {#define KSMBD_TCP_SEND_TIMEOUT (5 * HZ)#define KSMBD_TCP_PEER_SOCKADDR (c) ((struct sockaddr *)&((c)->peer_addr)) -extern struct list_head conn_list; +#define CONN_HASH_BITS 12 +extern DECLARE_HASHTABLE(conn_list, CONN_HASH_BITS);extern struct rw_semaphore conn_list_lock;bool ksmbd_conn_alive (struct ksmbd_conn *conn);
fs/smb/server/smb2pdu.c +2 -2
@@ -7425,7 +7425,7 @@ int smb2_lock(struct ksmbd_work *work)int nolock =0 ;LIST_HEAD (lock_list);LIST_HEAD (rollback_list); -int prior_lock =0 ; +int prior_lock =0 , bkt;WORK_BUFFERS (work, req, rsp);
@@ -7535,7 +7535,7 @@ int smb2_lock(struct ksmbd_work *work) nolock =1 ;/* check locks in connection list */ down_read (&conn_list_lock); -list_for_each_entry (conn, &conn_list, conns_list) { +hash_for_each (conn_list, bkt, conn, hlist) {spin_lock (&conn->llist_lock);list_for_each_entry_safe (cmp_lock, tmp2, &conn->lock_list, clist) { if (file_inode(cmp_lock->fl->c.flc_file) !=
fs/smb/server/transport_rdma.c +5 -0
@@ -381,6 +381,11 @@ static struct smb_direct_transport *alloc_transport(struct rdma_cm_id *cm_id) conn =ksmbd_conn_alloc ();if (!conn) goto err; + +down_write (&conn_list_lock); +hash_add (conn_list, &conn->hlist,0 ); +up_write (&conn_list_lock); + conn->transport =KSMBD_TRANS (t);KSMBD_TRANS (t)->conn = conn;KSMBD_TRANS (t)->ops = &ksmbd_smb_direct_transport_ops;
fs/smb/server/transport_tcp.c +21 -4
@@ -89,13 +89,21 @@ static struct tcp_transport *alloc_transport(struct socket *client_sk) }#if IS_ENABLED (CONFIG_IPV6 ) -if (client_sk->sk->sk_family ==AF_INET6 ) +if (client_sk->sk->sk_family ==AF_INET6 ) {memcpy (&conn->inet6_addr, &client_sk->sk->sk_v6_daddr,16 ); -else + conn->inet_hash =ipv6_addr_hash (&client_sk->sk->sk_v6_daddr); + }else { conn->inet_addr =inet_sk (client_sk->sk)->inet_daddr; + conn->inet_hash =ipv4_addr_hash (inet_sk (client_sk->sk)->inet_daddr); + }#else conn->inet_addr =inet_sk (client_sk->sk)->inet_daddr; + conn->inet_hash =ipv4_addr_hash (inet_sk (client_sk->sk)->inet_daddr);#endif +down_write (&conn_list_lock); +hash_add (conn_list, &conn->hlist, conn->inet_hash); +up_write (&conn_list_lock); + conn->transport =KSMBD_TRANS (t);KSMBD_TRANS (t)->conn = conn;KSMBD_TRANS (t)->ops = &ksmbd_tcp_transport_ops;
@@ -240,7 +248,7 @@ static int ksmbd_kthread_fn(void *p)struct socket *client_sk =NULL ;struct interface *iface = (struct interface *)p;struct ksmbd_conn *conn; -int ret; +int ret, inet_hash;unsigned int max_ip_conns;while (!kthread_should_stop ()) {
@@ -265,9 +273,18 @@ static int ksmbd_kthread_fn(void *p)/* * Limits repeated connections from clients with the same IP. */ +#if IS_ENABLED (CONFIG_IPV6 ) +if (client_sk->sk->sk_family ==AF_INET6 ) + inet_hash =ipv6_addr_hash (&client_sk->sk->sk_v6_daddr); +else + inet_hash =ipv4_addr_hash (inet_sk (client_sk->sk)->inet_daddr); +#else + inet_hash =ipv4_addr_hash (inet_sk (client_sk->sk)->inet_daddr); +#endif + max_ip_conns =0 ;down_read (&conn_list_lock); - list_for_each_entry(conn, &conn_list, conns_list) { + hash_for_each_possible(conn_list, conn, hlist, inet_hash) { #if IS_ENABLED(CONFIG_IPV6) if (client_sk->sk->sk_family == AF_INET6) { if (memcmp(&client_sk->sk->sk_v6_daddr,
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 ksmbd: reset rcount per connection in ksmbd_conn_wait_idle_sess_id()
In reply to Sasha LevinFrom: DaeMyung Kang <[email protected]>
[ Upstream commit def036ef87f8641c1c525d5ae17438d7a1006491 ]
rcount is intended to be connection-specific: 2 for curr_conn, 1 for every other connection sharing the same session. However, it is initialised only once before the hash iteration and is never reset. After the loop visits curr_conn, later sibling connections are also checked against rcount == 2, so a sibling with req_running == 1 is incorrectly treated as idle. This makes the outcome depend on the hash iteration order: whether a given sibling is checked against the loose (< 2) or the strict (< 1) threshold is decided by whether it happens to be visited before or after curr_conn.
The function's contract is "wait until every connection sharing this session is idle" so that destroy_previous_session() can safely tear the session down. The latched rcount violates that contract and reopens the teardown race window the wait logic was meant to close:destroy_previous_session() may proceed before sibling channels have actually quiesced, overlapping session teardown with in-flight work on those connections.
Recompute rcount inside the loop so each connection is compared against its own threshold regardless of iteration order.
This is a code-inspection fix for an iteration-order-dependent logic error; a targeted reproducer would require SMB3 multichannel with in-flight work on a sibling channel landing after curr_conn in hash order, which is not something that can be triggered reliably.
--- fs/smb/server/connection.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
fs/smb/server/connection.c +2 -3
@@ -180,7 +180,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id) {struct ksmbd_conn *conn;int rc, retry_count =0 , max_timeout =120 ; -int rcount =1 , bkt; +int rcount, bkt; retry_idle:if (retry_count >= max_timeout)
@@ -189,8 +189,7 @@ int ksmbd_conn_wait_idle_sess_id(struct ksmbd_conn *curr_conn, u64 sess_id)down_read (&conn_list_lock);hash_for_each (conn_list, bkt, conn, hlist) {if (conn->binding ||xa_load (&conn->sessions, sess_id)) { -if (conn == curr_conn) - rcount =2 ; + rcount = (conn == curr_conn) ?2 :1 ;if (atomic_read (&conn->req_running) >= rcount) { rc = wait_event_timeout(conn->req_running_q, atomic_read(&conn->req_running) < rcount,
Signature · 1 line
2.53.0