]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.19-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 13 Feb 2026 10:10:57 +0000 (11:10 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 13 Feb 2026 10:10:57 +0000 (11:10 +0100)
added patches:
driver-core-enforce-device_lock-for-driver_match_device.patch
ksmbd-add-chann_lock-to-protect-ksmbd_chann_list-xarray.patch
ksmbd-fix-infinite-loop-caused-by-next_smb2_rcv_hdr_off-reset-in-error-paths.patch
smb-client-fix-last-send-credit-problem-causing-disconnects.patch
smb-client-introduce-and-use-smbd_-alloc-free-_send_io.patch
smb-client-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch
smb-client-let-send_done-handle-a-completion-without-ib_send_signaled.patch
smb-client-let-smbd_post_send-make-use-of-request-wr.patch
smb-client-let-smbd_post_send_negotiate_req-use-smbd_post_send.patch
smb-client-make-use-of-smbdirect_socket.recv_io.credits.available.patch
smb-client-make-use-of-smbdirect_socket.send_io.bcredits.patch
smb-client-port-and-use-the-wait_for_credits-logic-used-by-server.patch
smb-client-remove-pointless-sc-recv_io.credits.count-rollback.patch
smb-client-remove-pointless-sc-send_io.pending-handling-in-smbd_post_send_iter.patch
smb-client-split-cached_fid-bitfields-to-avoid-shared-byte-rmw-races.patch
smb-client-split-out-smbd_ib_post_send.patch
smb-client-use-smbdirect_send_batch-processing.patch
smb-server-fix-last-send-credit-problem-causing-disconnects.patch
smb-server-fix-leak-of-active_num_conn-in-ksmbd_tcp_new_connection.patch
smb-server-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch
smb-server-let-send_done-handle-a-completion-without-ib_send_signaled.patch
smb-server-make-use-of-smbdirect_socket.recv_io.credits.available.patch
smb-server-make-use-of-smbdirect_socket.send_io.bcredits.patch
smb-smbdirect-introduce-smbdirect_socket.recv_io.credits.available.patch
smb-smbdirect-introduce-smbdirect_socket.send_io.bcredits.patch

26 files changed:
queue-6.19/driver-core-enforce-device_lock-for-driver_match_device.patch [new file with mode: 0644]
queue-6.19/ksmbd-add-chann_lock-to-protect-ksmbd_chann_list-xarray.patch [new file with mode: 0644]
queue-6.19/ksmbd-fix-infinite-loop-caused-by-next_smb2_rcv_hdr_off-reset-in-error-paths.patch [new file with mode: 0644]
queue-6.19/series
queue-6.19/smb-client-fix-last-send-credit-problem-causing-disconnects.patch [new file with mode: 0644]
queue-6.19/smb-client-introduce-and-use-smbd_-alloc-free-_send_io.patch [new file with mode: 0644]
queue-6.19/smb-client-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch [new file with mode: 0644]
queue-6.19/smb-client-let-send_done-handle-a-completion-without-ib_send_signaled.patch [new file with mode: 0644]
queue-6.19/smb-client-let-smbd_post_send-make-use-of-request-wr.patch [new file with mode: 0644]
queue-6.19/smb-client-let-smbd_post_send_negotiate_req-use-smbd_post_send.patch [new file with mode: 0644]
queue-6.19/smb-client-make-use-of-smbdirect_socket.recv_io.credits.available.patch [new file with mode: 0644]
queue-6.19/smb-client-make-use-of-smbdirect_socket.send_io.bcredits.patch [new file with mode: 0644]
queue-6.19/smb-client-port-and-use-the-wait_for_credits-logic-used-by-server.patch [new file with mode: 0644]
queue-6.19/smb-client-remove-pointless-sc-recv_io.credits.count-rollback.patch [new file with mode: 0644]
queue-6.19/smb-client-remove-pointless-sc-send_io.pending-handling-in-smbd_post_send_iter.patch [new file with mode: 0644]
queue-6.19/smb-client-split-cached_fid-bitfields-to-avoid-shared-byte-rmw-races.patch [new file with mode: 0644]
queue-6.19/smb-client-split-out-smbd_ib_post_send.patch [new file with mode: 0644]
queue-6.19/smb-client-use-smbdirect_send_batch-processing.patch [new file with mode: 0644]
queue-6.19/smb-server-fix-last-send-credit-problem-causing-disconnects.patch [new file with mode: 0644]
queue-6.19/smb-server-fix-leak-of-active_num_conn-in-ksmbd_tcp_new_connection.patch [new file with mode: 0644]
queue-6.19/smb-server-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch [new file with mode: 0644]
queue-6.19/smb-server-let-send_done-handle-a-completion-without-ib_send_signaled.patch [new file with mode: 0644]
queue-6.19/smb-server-make-use-of-smbdirect_socket.recv_io.credits.available.patch [new file with mode: 0644]
queue-6.19/smb-server-make-use-of-smbdirect_socket.send_io.bcredits.patch [new file with mode: 0644]
queue-6.19/smb-smbdirect-introduce-smbdirect_socket.recv_io.credits.available.patch [new file with mode: 0644]
queue-6.19/smb-smbdirect-introduce-smbdirect_socket.send_io.bcredits.patch [new file with mode: 0644]

diff --git a/queue-6.19/driver-core-enforce-device_lock-for-driver_match_device.patch b/queue-6.19/driver-core-enforce-device_lock-for-driver_match_device.patch
new file mode 100644 (file)
index 0000000..694a953
--- /dev/null
@@ -0,0 +1,88 @@
+From dc23806a7c47ec5f1293aba407fb69519f976ee0 Mon Sep 17 00:00:00 2001
+From: Gui-Dong Han <hanguidong02@gmail.com>
+Date: Wed, 14 Jan 2026 00:28:43 +0800
+Subject: driver core: enforce device_lock for driver_match_device()
+
+From: Gui-Dong Han <hanguidong02@gmail.com>
+
+commit dc23806a7c47ec5f1293aba407fb69519f976ee0 upstream.
+
+Currently, driver_match_device() is called from three sites. One site
+(__device_attach_driver) holds device_lock(dev), but the other two
+(bind_store and __driver_attach) do not. This inconsistency means that
+bus match() callbacks are not guaranteed to be called with the lock
+held.
+
+Fix this by introducing driver_match_device_locked(), which guarantees
+holding the device lock using a scoped guard. Replace the unlocked calls
+in bind_store() and __driver_attach() with this new helper. Also add a
+lock assertion to driver_match_device() to enforce this guarantee.
+
+This consistency also fixes a known race condition. The driver_override
+implementation relies on the device_lock, so the missing lock led to the
+use-after-free (UAF) reported in Bugzilla for buses using this field.
+
+Stress testing the two newly locked paths for 24 hours with
+CONFIG_PROVE_LOCKING and CONFIG_LOCKDEP enabled showed no UAF recurrence
+and no lockdep warnings.
+
+Cc: stable@vger.kernel.org
+Closes: https://bugzilla.kernel.org/show_bug.cgi?id=220789
+Suggested-by: Qiu-ji Chen <chenqiuji666@gmail.com>
+Signed-off-by: Gui-Dong Han <hanguidong02@gmail.com>
+Fixes: 49b420a13ff9 ("driver core: check bus->match without holding device lock")
+Reviewed-by: Danilo Krummrich <dakr@kernel.org>
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Reviewed-by: Rafael J. Wysocki (Intel) <rafael@kernel.org>
+Link: https://patch.msgid.link/20260113162843.12712-1-hanguidong02@gmail.com
+Signed-off-by: Danilo Krummrich <dakr@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/base/base.h |    9 +++++++++
+ drivers/base/bus.c  |    2 +-
+ drivers/base/dd.c   |    2 +-
+ 3 files changed, 11 insertions(+), 2 deletions(-)
+
+--- a/drivers/base/base.h
++++ b/drivers/base/base.h
+@@ -182,9 +182,18 @@ void device_set_deferred_probe_reason(co
+ static inline int driver_match_device(const struct device_driver *drv,
+                                     struct device *dev)
+ {
++      device_lock_assert(dev);
++
+       return drv->bus->match ? drv->bus->match(dev, drv) : 1;
+ }
++static inline int driver_match_device_locked(const struct device_driver *drv,
++                                           struct device *dev)
++{
++      guard(device)(dev);
++      return driver_match_device(drv, dev);
++}
++
+ static inline void dev_sync_state(struct device *dev)
+ {
+       if (dev->bus->sync_state)
+--- a/drivers/base/bus.c
++++ b/drivers/base/bus.c
+@@ -263,7 +263,7 @@ static ssize_t bind_store(struct device_
+       int err = -ENODEV;
+       dev = bus_find_device_by_name(bus, NULL, buf);
+-      if (dev && driver_match_device(drv, dev)) {
++      if (dev && driver_match_device_locked(drv, dev)) {
+               err = device_driver_attach(drv, dev);
+               if (!err) {
+                       /* success */
+--- a/drivers/base/dd.c
++++ b/drivers/base/dd.c
+@@ -1180,7 +1180,7 @@ static int __driver_attach(struct device
+        * is an error.
+        */
+-      ret = driver_match_device(drv, dev);
++      ret = driver_match_device_locked(drv, dev);
+       if (ret == 0) {
+               /* no match */
+               return 0;
diff --git a/queue-6.19/ksmbd-add-chann_lock-to-protect-ksmbd_chann_list-xarray.patch b/queue-6.19/ksmbd-add-chann_lock-to-protect-ksmbd_chann_list-xarray.patch
new file mode 100644 (file)
index 0000000..a434b3a
--- /dev/null
@@ -0,0 +1,110 @@
+From 4f3a06cc57976cafa8c6f716646be6c79a99e485 Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Mon, 9 Feb 2026 10:43:19 +0900
+Subject: ksmbd: add chann_lock to protect ksmbd_chann_list xarray
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit 4f3a06cc57976cafa8c6f716646be6c79a99e485 upstream.
+
+ksmbd_chann_list xarray lacks synchronization, allowing use-after-free in
+multi-channel sessions (between lookup_chann_list() and ksmbd_chann_del).
+
+Adds rw_semaphore chann_lock to struct ksmbd_session and protects
+all xa_load/xa_store/xa_erase accesses.
+
+Cc: stable@vger.kernel.org
+Reported-by: Igor Stepansky <igor.stepansky@orca.security>
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/mgmt/user_session.c |    5 +++++
+ fs/smb/server/mgmt/user_session.h |    1 +
+ fs/smb/server/smb2pdu.c           |   12 +++++++++++-
+ 3 files changed, 17 insertions(+), 1 deletion(-)
+
+--- a/fs/smb/server/mgmt/user_session.c
++++ b/fs/smb/server/mgmt/user_session.c
+@@ -32,12 +32,14 @@ static void free_channel_list(struct ksm
+       struct channel *chann;
+       unsigned long index;
++      down_write(&sess->chann_lock);
+       xa_for_each(&sess->ksmbd_chann_list, index, chann) {
+               xa_erase(&sess->ksmbd_chann_list, index);
+               kfree(chann);
+       }
+       xa_destroy(&sess->ksmbd_chann_list);
++      up_write(&sess->chann_lock);
+ }
+ static void __session_rpc_close(struct ksmbd_session *sess,
+@@ -220,7 +222,9 @@ static int ksmbd_chann_del(struct ksmbd_
+ {
+       struct channel *chann;
++      down_write(&sess->chann_lock);
+       chann = xa_erase(&sess->ksmbd_chann_list, (long)conn);
++      up_write(&sess->chann_lock);
+       if (!chann)
+               return -ENOENT;
+@@ -454,6 +458,7 @@ static struct ksmbd_session *__session_c
+       rwlock_init(&sess->tree_conns_lock);
+       atomic_set(&sess->refcnt, 2);
+       init_rwsem(&sess->rpc_lock);
++      init_rwsem(&sess->chann_lock);
+       ret = __init_smb2_session(sess);
+       if (ret)
+--- a/fs/smb/server/mgmt/user_session.h
++++ b/fs/smb/server/mgmt/user_session.h
+@@ -49,6 +49,7 @@ struct ksmbd_session {
+       char                            sess_key[CIFS_KEY_SIZE];
+       struct hlist_node               hlist;
++      struct rw_semaphore             chann_lock;
+       struct xarray                   ksmbd_chann_list;
+       struct xarray                   tree_conns;
+       struct ida                      tree_conn_ida;
+--- a/fs/smb/server/smb2pdu.c
++++ b/fs/smb/server/smb2pdu.c
+@@ -79,7 +79,13 @@ static inline bool check_session_id(stru
+ struct channel *lookup_chann_list(struct ksmbd_session *sess, struct ksmbd_conn *conn)
+ {
+-      return xa_load(&sess->ksmbd_chann_list, (long)conn);
++      struct channel *chann;
++
++      down_read(&sess->chann_lock);
++      chann = xa_load(&sess->ksmbd_chann_list, (long)conn);
++      up_read(&sess->chann_lock);
++
++      return chann;
+ }
+ /**
+@@ -1558,8 +1564,10 @@ binding_session:
+                               return -ENOMEM;
+                       chann->conn = conn;
++                      down_write(&sess->chann_lock);
+                       old = xa_store(&sess->ksmbd_chann_list, (long)conn, chann,
+                                       KSMBD_DEFAULT_GFP);
++                      up_write(&sess->chann_lock);
+                       if (xa_is_err(old)) {
+                               kfree(chann);
+                               return xa_err(old);
+@@ -1651,8 +1659,10 @@ binding_session:
+                               return -ENOMEM;
+                       chann->conn = conn;
++                      down_write(&sess->chann_lock);
+                       old = xa_store(&sess->ksmbd_chann_list, (long)conn,
+                                       chann, KSMBD_DEFAULT_GFP);
++                      up_write(&sess->chann_lock);
+                       if (xa_is_err(old)) {
+                               kfree(chann);
+                               return xa_err(old);
diff --git a/queue-6.19/ksmbd-fix-infinite-loop-caused-by-next_smb2_rcv_hdr_off-reset-in-error-paths.patch b/queue-6.19/ksmbd-fix-infinite-loop-caused-by-next_smb2_rcv_hdr_off-reset-in-error-paths.patch
new file mode 100644 (file)
index 0000000..10d4439
--- /dev/null
@@ -0,0 +1,62 @@
+From 010eb01ce23b34b50531448b0da391c7f05a72af Mon Sep 17 00:00:00 2001
+From: Namjae Jeon <linkinjeon@kernel.org>
+Date: Sat, 24 Jan 2026 10:55:46 +0900
+Subject: ksmbd: fix infinite loop caused by next_smb2_rcv_hdr_off reset in error paths
+
+From: Namjae Jeon <linkinjeon@kernel.org>
+
+commit 010eb01ce23b34b50531448b0da391c7f05a72af upstream.
+
+The problem occurs when a signed request fails smb2 signature verification
+check. In __process_request(), if check_sign_req() returns an error,
+set_smb2_rsp_status(work, STATUS_ACCESS_DENIED) is called.
+set_smb2_rsp_status() set work->next_smb2_rcv_hdr_off as zero. By resetting
+next_smb2_rcv_hdr_off to zero, the pointer to the next command in the chain
+is lost. Consequently, is_chained_smb2_message() continues to point to
+the same request header instead of advancing. If the header's NextCommand
+field is non-zero, the function returns true, causing __handle_ksmbd_work()
+to repeatedly process the same failed request in an infinite loop.
+This results in the kernel log being flooded with "bad smb2 signature"
+messages and high CPU usage.
+
+This patch fixes the issue by changing the return value from
+SERVER_HANDLER_CONTINUE to SERVER_HANDLER_ABORT. This ensures that
+the processing loop terminates immediately rather than attempting to
+continue from an invalidated offset.
+
+Reported-by: tianshuo han <hantianshuo233@gmail.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/server.c |    6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/fs/smb/server/server.c
++++ b/fs/smb/server/server.c
+@@ -126,21 +126,21 @@ static int __process_request(struct ksmb
+ andx_again:
+       if (command >= conn->max_cmds) {
+               conn->ops->set_rsp_status(work, STATUS_INVALID_PARAMETER);
+-              return SERVER_HANDLER_CONTINUE;
++              return SERVER_HANDLER_ABORT;
+       }
+       cmds = &conn->cmds[command];
+       if (!cmds->proc) {
+               ksmbd_debug(SMB, "*** not implemented yet cmd = %x\n", command);
+               conn->ops->set_rsp_status(work, STATUS_NOT_IMPLEMENTED);
+-              return SERVER_HANDLER_CONTINUE;
++              return SERVER_HANDLER_ABORT;
+       }
+       if (work->sess && conn->ops->is_sign_req(work, command)) {
+               ret = conn->ops->check_sign_req(work);
+               if (!ret) {
+                       conn->ops->set_rsp_status(work, STATUS_ACCESS_DENIED);
+-                      return SERVER_HANDLER_CONTINUE;
++                      return SERVER_HANDLER_ABORT;
+               }
+       }
index 440e4a47210ba540a1ba5062284516401a2681b2..ae7e939de2be5b558fcaadaf24e28b067f1d13bd 100644 (file)
@@ -1,2 +1,27 @@
 io_uring-io-wq-add-exit-on-idle-state.patch
 io_uring-allow-io-wq-workers-to-exit-when-unused.patch
+smb-client-split-cached_fid-bitfields-to-avoid-shared-byte-rmw-races.patch
+ksmbd-fix-infinite-loop-caused-by-next_smb2_rcv_hdr_off-reset-in-error-paths.patch
+ksmbd-add-chann_lock-to-protect-ksmbd_chann_list-xarray.patch
+smb-server-fix-leak-of-active_num_conn-in-ksmbd_tcp_new_connection.patch
+smb-smbdirect-introduce-smbdirect_socket.recv_io.credits.available.patch
+smb-smbdirect-introduce-smbdirect_socket.send_io.bcredits.patch
+smb-server-make-use-of-smbdirect_socket.recv_io.credits.available.patch
+smb-server-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch
+smb-server-make-use-of-smbdirect_socket.send_io.bcredits.patch
+smb-server-fix-last-send-credit-problem-causing-disconnects.patch
+smb-server-let-send_done-handle-a-completion-without-ib_send_signaled.patch
+smb-client-make-use-of-smbdirect_socket.recv_io.credits.available.patch
+smb-client-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch
+smb-client-let-smbd_post_send-make-use-of-request-wr.patch
+smb-client-remove-pointless-sc-recv_io.credits.count-rollback.patch
+smb-client-remove-pointless-sc-send_io.pending-handling-in-smbd_post_send_iter.patch
+smb-client-port-and-use-the-wait_for_credits-logic-used-by-server.patch
+smb-client-split-out-smbd_ib_post_send.patch
+smb-client-introduce-and-use-smbd_-alloc-free-_send_io.patch
+smb-client-use-smbdirect_send_batch-processing.patch
+smb-client-make-use-of-smbdirect_socket.send_io.bcredits.patch
+smb-client-fix-last-send-credit-problem-causing-disconnects.patch
+smb-client-let-smbd_post_send_negotiate_req-use-smbd_post_send.patch
+smb-client-let-send_done-handle-a-completion-without-ib_send_signaled.patch
+driver-core-enforce-device_lock-for-driver_match_device.patch
diff --git a/queue-6.19/smb-client-fix-last-send-credit-problem-causing-disconnects.patch b/queue-6.19/smb-client-fix-last-send-credit-problem-causing-disconnects.patch
new file mode 100644 (file)
index 0000000..f6b938b
--- /dev/null
@@ -0,0 +1,103 @@
+From 93ac432274e1361b4f6cd69e7c5d9b3ac21e13f5 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:58 +0100
+Subject: smb: client: fix last send credit problem causing disconnects
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 93ac432274e1361b4f6cd69e7c5d9b3ac21e13f5 upstream.
+
+When we are about to use the last send credit that was
+granted to us by the peer, we need to wait until
+we are ourself able to grant at least one credit
+to the peer. Otherwise it might not be possible
+for the peer to grant more credits.
+
+The following sections in MS-SMBD are related to this:
+
+3.1.5.1 Sending Upper Layer Messages
+...
+If Connection.SendCredits is 1 and the CreditsGranted field of the
+message is 0, stop processing.
+...
+
+3.1.5.9 Managing Credits Prior to Sending
+...
+If Connection.ReceiveCredits is zero, or if Connection.SendCredits is
+one and the Connection.SendQueue is not empty, the sender MUST allocate
+and post at least one receive of size Connection.MaxReceiveSize and MUST
+increment Connection.ReceiveCredits by the number allocated and posted.
+If no receives are posted, the processing MUST return a value of zero to
+indicate to the caller that no Send message can be currently performed.
+...
+
+This is a similar logic as we have in the server.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |   31 +++++++++++++++++++++++++++++--
+ 1 file changed, 29 insertions(+), 2 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -697,6 +697,15 @@ static void smbd_post_send_credits(struc
+       atomic_add(posted, &sc->recv_io.credits.available);
++      /*
++       * If the last send credit is waiting for credits
++       * it can grant we need to wake it up
++       */
++      if (posted &&
++          atomic_read(&sc->send_io.bcredits.count) == 0 &&
++          atomic_read(&sc->send_io.credits.count) == 0)
++              wake_up(&sc->send_io.credits.wait_queue);
++
+       /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */
+       if (atomic_read(&sc->recv_io.credits.count) <
+               sc->recv_io.credits.target - 1) {
+@@ -1394,6 +1403,26 @@ static int smbd_post_send_iter(struct sm
+               goto err_wait_credit;
+       }
++      new_credits = manage_credits_prior_sending(sc);
++      if (new_credits == 0 &&
++          atomic_read(&sc->send_io.credits.count) == 0 &&
++          atomic_read(&sc->recv_io.credits.count) == 0) {
++              queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
++              rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
++                                            atomic_read(&sc->send_io.credits.count) >= 1 ||
++                                            atomic_read(&sc->recv_io.credits.available) >= 1 ||
++                                            sc->status != SMBDIRECT_SOCKET_CONNECTED);
++              if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
++                      rc = -ENOTCONN;
++              if (rc < 0) {
++                      log_outgoing(ERR, "disconnected not sending on last credit\n");
++                      rc = -EAGAIN;
++                      goto err_wait_credit;
++              }
++
++              new_credits = manage_credits_prior_sending(sc);
++      }
++
+       request = smbd_alloc_send_io(sc);
+       if (IS_ERR(request)) {
+               rc = PTR_ERR(request);
+@@ -1448,8 +1477,6 @@ static int smbd_post_send_iter(struct sm
+       /* Fill in the packet header */
+       packet->credits_requested = cpu_to_le16(sp->send_credit_target);
+-
+-      new_credits = manage_credits_prior_sending(sc);
+       packet->credits_granted = cpu_to_le16(new_credits);
+       packet->flags = 0;
diff --git a/queue-6.19/smb-client-introduce-and-use-smbd_-alloc-free-_send_io.patch b/queue-6.19/smb-client-introduce-and-use-smbd_-alloc-free-_send_io.patch
new file mode 100644 (file)
index 0000000..370e5e1
--- /dev/null
@@ -0,0 +1,196 @@
+From dc77da0373529d43175984b390106be2d8f03609 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:55 +0100
+Subject: smb: client: introduce and use smbd_{alloc, free}_send_io()
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit dc77da0373529d43175984b390106be2d8f03609 upstream.
+
+This is basically a copy of smb_direct_{alloc,free}_sendmsg()
+in the server, with just using ib_dma_unmap_page() in all
+cases, which is the same as ib_dma_unmap_single().
+
+We'll use this logic in common code in future.
+(I basically backported it from my branch that
+as already has everything in common).
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |   87 ++++++++++++++++++++++++++++++----------------
+ 1 file changed, 58 insertions(+), 29 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -493,10 +493,54 @@ static inline void *smbdirect_recv_io_pa
+       return (void *)response->packet;
+ }
++static struct smbdirect_send_io *smbd_alloc_send_io(struct smbdirect_socket *sc)
++{
++      struct smbdirect_send_io *msg;
++
++      msg = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
++      if (!msg)
++              return ERR_PTR(-ENOMEM);
++      msg->socket = sc;
++      INIT_LIST_HEAD(&msg->sibling_list);
++      msg->num_sge = 0;
++
++      return msg;
++}
++
++static void smbd_free_send_io(struct smbdirect_send_io *msg)
++{
++      struct smbdirect_socket *sc = msg->socket;
++      size_t i;
++
++      /*
++       * The list needs to be empty!
++       * The caller should take care of it.
++       */
++      WARN_ON_ONCE(!list_empty(&msg->sibling_list));
++
++      /*
++       * Note we call ib_dma_unmap_page(), even if some sges are mapped using
++       * ib_dma_map_single().
++       *
++       * The difference between _single() and _page() only matters for the
++       * ib_dma_map_*() case.
++       *
++       * For the ib_dma_unmap_*() case it does not matter as both take the
++       * dma_addr_t and dma_unmap_single_attrs() is just an alias to
++       * dma_unmap_page_attrs().
++       */
++      for (i = 0; i < msg->num_sge; i++)
++              ib_dma_unmap_page(sc->ib.dev,
++                                msg->sge[i].addr,
++                                msg->sge[i].length,
++                                DMA_TO_DEVICE);
++
++      mempool_free(msg, sc->send_io.mem.pool);
++}
++
+ /* Called when a RDMA send is done */
+ static void send_done(struct ib_cq *cq, struct ib_wc *wc)
+ {
+-      int i;
+       struct smbdirect_send_io *request =
+               container_of(wc->wr_cqe, struct smbdirect_send_io, cqe);
+       struct smbdirect_socket *sc = request->socket;
+@@ -505,12 +549,8 @@ static void send_done(struct ib_cq *cq,
+       log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n",
+               request, ib_wc_status_msg(wc->status));
+-      for (i = 0; i < request->num_sge; i++)
+-              ib_dma_unmap_single(sc->ib.dev,
+-                      request->sge[i].addr,
+-                      request->sge[i].length,
+-                      DMA_TO_DEVICE);
+-      mempool_free(request, sc->send_io.mem.pool);
++      /* Note this frees wc->wr_cqe, but not wc */
++      smbd_free_send_io(request);
+       lcredits += 1;
+       if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
+@@ -963,15 +1003,13 @@ static int smbd_post_send_negotiate_req(
+ {
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+       struct ib_send_wr send_wr;
+-      int rc = -ENOMEM;
++      int rc;
+       struct smbdirect_send_io *request;
+       struct smbdirect_negotiate_req *packet;
+-      request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
+-      if (!request)
+-              return rc;
+-
+-      request->socket = sc;
++      request = smbd_alloc_send_io(sc);
++      if (IS_ERR(request))
++              return PTR_ERR(request);
+       packet = smbdirect_send_io_payload(request);
+       packet->min_version = cpu_to_le16(SMBDIRECT_V1);
+@@ -983,7 +1021,6 @@ static int smbd_post_send_negotiate_req(
+       packet->max_fragmented_size =
+               cpu_to_le32(sp->max_fragmented_recv_size);
+-      request->num_sge = 1;
+       request->sge[0].addr = ib_dma_map_single(
+                               sc->ib.dev, (void *)packet,
+                               sizeof(*packet), DMA_TO_DEVICE);
+@@ -991,6 +1028,7 @@ static int smbd_post_send_negotiate_req(
+               rc = -EIO;
+               goto dma_mapping_failed;
+       }
++      request->num_sge = 1;
+       request->sge[0].length = sizeof(*packet);
+       request->sge[0].lkey = sc->ib.pd->local_dma_lkey;
+@@ -1020,13 +1058,11 @@ static int smbd_post_send_negotiate_req(
+       /* if we reach here, post send failed */
+       log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
+       atomic_dec(&sc->send_io.pending.count);
+-      ib_dma_unmap_single(sc->ib.dev, request->sge[0].addr,
+-              request->sge[0].length, DMA_TO_DEVICE);
+       smbd_disconnect_rdma_connection(sc);
+ dma_mapping_failed:
+-      mempool_free(request, sc->send_io.mem.pool);
++      smbd_free_send_io(request);
+       return rc;
+ }
+@@ -1187,7 +1223,7 @@ static int smbd_post_send_iter(struct sm
+                              int *_remaining_data_length)
+ {
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+-      int i, rc;
++      int rc;
+       int header_length;
+       int data_length;
+       struct smbdirect_send_io *request;
+@@ -1208,13 +1244,12 @@ static int smbd_post_send_iter(struct sm
+               goto err_wait_credit;
+       }
+-      request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
+-      if (!request) {
+-              rc = -ENOMEM;
++      request = smbd_alloc_send_io(sc);
++      if (IS_ERR(request)) {
++              rc = PTR_ERR(request);
+               goto err_alloc;
+       }
+-      request->socket = sc;
+       memset(request->sge, 0, sizeof(request->sge));
+       /* Map the packet to DMA */
+@@ -1292,13 +1327,7 @@ static int smbd_post_send_iter(struct sm
+               return 0;
+ err_dma:
+-      for (i = 0; i < request->num_sge; i++)
+-              if (request->sge[i].addr)
+-                      ib_dma_unmap_single(sc->ib.dev,
+-                                          request->sge[i].addr,
+-                                          request->sge[i].length,
+-                                          DMA_TO_DEVICE);
+-      mempool_free(request, sc->send_io.mem.pool);
++      smbd_free_send_io(request);
+ err_alloc:
+       atomic_inc(&sc->send_io.credits.count);
diff --git a/queue-6.19/smb-client-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch b/queue-6.19/smb-client-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch
new file mode 100644 (file)
index 0000000..027ef97
--- /dev/null
@@ -0,0 +1,61 @@
+From defb3c05fee94b296eebe05aaea16d2664b00252 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:49 +0100
+Subject: smb: client: let recv_done() queue a refill when the peer is low on credits
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit defb3c05fee94b296eebe05aaea16d2664b00252 upstream.
+
+In captures I saw that Windows was granting 191 credits in a batch
+when its peer posted a lot of messages. We are asking for a
+credit target of 255 and 191 is 252*3/4.
+
+So we also use that logic in order to fill the
+recv buffers available to the peer.
+
+Fixes: 02548c477a90 ("smb: client: queue post_recv_credits_work also if the peer raises the credit target")
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |    7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -663,6 +663,7 @@ static void recv_done(struct ib_cq *cq,
+               container_of(wc->wr_cqe, struct smbdirect_recv_io, cqe);
+       struct smbdirect_socket *sc = response->socket;
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
++      int current_recv_credits;
+       u16 old_recv_credit_target;
+       u32 data_offset = 0;
+       u32 data_length = 0;
+@@ -747,7 +748,8 @@ static void recv_done(struct ib_cq *cq,
+               }
+               atomic_dec(&sc->recv_io.posted.count);
+-              atomic_dec(&sc->recv_io.credits.count);
++              current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count);
++
+               old_recv_credit_target = sc->recv_io.credits.target;
+               sc->recv_io.credits.target =
+                       le16_to_cpu(data_transfer->credits_requested);
+@@ -783,7 +785,8 @@ static void recv_done(struct ib_cq *cq,
+                * reassembly queue and wake up the reading thread
+                */
+               if (data_length) {
+-                      if (sc->recv_io.credits.target > old_recv_credit_target)
++                      if (current_recv_credits <= (sc->recv_io.credits.target / 4) ||
++                          sc->recv_io.credits.target > old_recv_credit_target)
+                               queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
+                       enqueue_reassembly(sc, response, data_length);
diff --git a/queue-6.19/smb-client-let-send_done-handle-a-completion-without-ib_send_signaled.patch b/queue-6.19/smb-client-let-send_done-handle-a-completion-without-ib_send_signaled.patch
new file mode 100644 (file)
index 0000000..81bf4c9
--- /dev/null
@@ -0,0 +1,73 @@
+From cf74fcdc43b322b6188a0750b5ee79e38be6d078 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:17:00 +0100
+Subject: smb: client: let send_done handle a completion without IB_SEND_SIGNALED
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit cf74fcdc43b322b6188a0750b5ee79e38be6d078 upstream.
+
+With smbdirect_send_batch processing we likely have requests without
+IB_SEND_SIGNALED, which will be destroyed in the final request
+that has IB_SEND_SIGNALED set.
+
+If the connection is broken all requests are signaled
+even without explicit IB_SEND_SIGNALED.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |   27 +++++++++++++++++++++++++++
+ 1 file changed, 27 insertions(+)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -554,6 +554,32 @@ static void send_done(struct ib_cq *cq,
+       log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n",
+               request, ib_wc_status_msg(wc->status));
++      if (unlikely(!(request->wr.send_flags & IB_SEND_SIGNALED))) {
++              /*
++               * This happens when smbdirect_send_io is a sibling
++               * before the final message, it is signaled on
++               * error anyway, so we need to skip
++               * smbdirect_connection_free_send_io here,
++               * otherwise is will destroy the memory
++               * of the siblings too, which will cause
++               * use after free problems for the others
++               * triggered from ib_drain_qp().
++               */
++              if (wc->status != IB_WC_SUCCESS)
++                      goto skip_free;
++
++              /*
++               * This should not happen!
++               * But we better just close the
++               * connection...
++               */
++              log_rdma_send(ERR,
++                      "unexpected send completion wc->status=%s (%d) wc->opcode=%d\n",
++                      ib_wc_status_msg(wc->status), wc->status, wc->opcode);
++              smbd_disconnect_rdma_connection(sc);
++              return;
++      }
++
+       /*
+        * Free possible siblings and then the main send_io
+        */
+@@ -567,6 +593,7 @@ static void send_done(struct ib_cq *cq,
+       lcredits += 1;
+       if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
++skip_free:
+               if (wc->status != IB_WC_WR_FLUSH_ERR)
+                       log_rdma_send(ERR, "wc->status=%s wc->opcode=%d\n",
+                               ib_wc_status_msg(wc->status), wc->opcode);
diff --git a/queue-6.19/smb-client-let-smbd_post_send-make-use-of-request-wr.patch b/queue-6.19/smb-client-let-smbd_post_send-make-use-of-request-wr.patch
new file mode 100644 (file)
index 0000000..67c9dfc
--- /dev/null
@@ -0,0 +1,57 @@
+From bf1656e12a9db2add716c7fb57b56967f69599fa Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:50 +0100
+Subject: smb: client: let smbd_post_send() make use of request->wr
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit bf1656e12a9db2add716c7fb57b56967f69599fa upstream.
+
+We don't need a stack variable in addition.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |   15 +++++++--------
+ 1 file changed, 7 insertions(+), 8 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -1105,7 +1105,6 @@ static int manage_keep_alive_before_send
+ static int smbd_post_send(struct smbdirect_socket *sc,
+               struct smbdirect_send_io *request)
+ {
+-      struct ib_send_wr send_wr;
+       int rc, i;
+       for (i = 0; i < request->num_sge; i++) {
+@@ -1121,14 +1120,14 @@ static int smbd_post_send(struct smbdire
+       request->cqe.done = send_done;
+-      send_wr.next = NULL;
+-      send_wr.wr_cqe = &request->cqe;
+-      send_wr.sg_list = request->sge;
+-      send_wr.num_sge = request->num_sge;
+-      send_wr.opcode = IB_WR_SEND;
+-      send_wr.send_flags = IB_SEND_SIGNALED;
++      request->wr.next = NULL;
++      request->wr.wr_cqe = &request->cqe;
++      request->wr.sg_list = request->sge;
++      request->wr.num_sge = request->num_sge;
++      request->wr.opcode = IB_WR_SEND;
++      request->wr.send_flags = IB_SEND_SIGNALED;
+-      rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
++      rc = ib_post_send(sc->ib.qp, &request->wr, NULL);
+       if (rc) {
+               log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
+               smbd_disconnect_rdma_connection(sc);
diff --git a/queue-6.19/smb-client-let-smbd_post_send_negotiate_req-use-smbd_post_send.patch b/queue-6.19/smb-client-let-smbd_post_send_negotiate_req-use-smbd_post_send.patch
new file mode 100644 (file)
index 0000000..99a42e5
--- /dev/null
@@ -0,0 +1,87 @@
+From 5b1c6149657af840a02885135c700ab42e6aa322 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:59 +0100
+Subject: smb: client: let smbd_post_send_negotiate_req() use smbd_post_send()
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 5b1c6149657af840a02885135c700ab42e6aa322 upstream.
+
+The server has similar logic and it makes sure that
+request->wr is used instead of a stack struct ib_send_wr send_wr.
+
+This makes sure send_done can see request->wr.send_flags
+as the next commit will check for IB_SEND_SIGNALED
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |   32 +++++++-------------------------
+ 1 file changed, 7 insertions(+), 25 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -35,6 +35,10 @@ static void enqueue_reassembly(
+ static struct smbdirect_recv_io *_get_first_reassembly(
+               struct smbdirect_socket *sc);
++static int smbd_post_send(struct smbdirect_socket *sc,
++                        struct smbdirect_send_batch *batch,
++                        struct smbdirect_send_io *request);
++
+ static int smbd_post_recv(
+               struct smbdirect_socket *sc,
+               struct smbdirect_recv_io *response);
+@@ -1021,7 +1025,6 @@ out1:
+ static int smbd_post_send_negotiate_req(struct smbdirect_socket *sc)
+ {
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+-      struct ib_send_wr send_wr;
+       int rc;
+       struct smbdirect_send_io *request;
+       struct smbdirect_negotiate_req *packet;
+@@ -1052,33 +1055,12 @@ static int smbd_post_send_negotiate_req(
+       request->sge[0].length = sizeof(*packet);
+       request->sge[0].lkey = sc->ib.pd->local_dma_lkey;
+-      ib_dma_sync_single_for_device(
+-              sc->ib.dev, request->sge[0].addr,
+-              request->sge[0].length, DMA_TO_DEVICE);
+-
+-      request->cqe.done = send_done;
+-
+-      send_wr.next = NULL;
+-      send_wr.wr_cqe = &request->cqe;
+-      send_wr.sg_list = request->sge;
+-      send_wr.num_sge = request->num_sge;
+-      send_wr.opcode = IB_WR_SEND;
+-      send_wr.send_flags = IB_SEND_SIGNALED;
+-
+-      log_rdma_send(INFO, "sge addr=0x%llx length=%u lkey=0x%x\n",
+-              request->sge[0].addr,
+-              request->sge[0].length, request->sge[0].lkey);
+-
+-      atomic_inc(&sc->send_io.pending.count);
+-      rc = ib_post_send(sc->ib.qp, &send_wr, NULL);
++      rc = smbd_post_send(sc, NULL, request);
+       if (!rc)
+               return 0;
+-      /* if we reach here, post send failed */
+-      log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
+-      atomic_dec(&sc->send_io.pending.count);
+-
+-      smbd_disconnect_rdma_connection(sc);
++      if (rc == -EAGAIN)
++              rc = -EIO;
+ dma_mapping_failed:
+       smbd_free_send_io(request);
diff --git a/queue-6.19/smb-client-make-use-of-smbdirect_socket.recv_io.credits.available.patch b/queue-6.19/smb-client-make-use-of-smbdirect_socket.recv_io.credits.available.patch
new file mode 100644 (file)
index 0000000..ca20f1b
--- /dev/null
@@ -0,0 +1,113 @@
+From 9911b1ed187a770a43950bf51f340ad4b7beecba Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:48 +0100
+Subject: smb: client: make use of smbdirect_socket.recv_io.credits.available
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 9911b1ed187a770a43950bf51f340ad4b7beecba upstream.
+
+The logic off managing recv credits by counting posted recv_io and
+granted credits is racy.
+
+That's because the peer might already consumed a credit,
+but between receiving the incoming recv at the hardware
+and processing the completion in the 'recv_done' functions
+we likely have a window where we grant credits, which
+don't really exist.
+
+So we better have a decicated counter for the
+available credits, which will be incremented
+when we posted new recv buffers and drained when
+we grant the credits to the peer.
+
+Fixes: 5fb9b459b368 ("smb: client: count the number of posted recv_io messages in order to calculated credits")
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |   34 ++++++++++++++++++++++++++++------
+ 1 file changed, 28 insertions(+), 6 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -618,6 +618,7 @@ static void smbd_post_send_credits(struc
+       struct smbdirect_recv_io *response;
+       struct smbdirect_socket *sc =
+               container_of(work, struct smbdirect_socket, recv_io.posted.refill_work);
++      int posted = 0;
+       if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
+               return;
+@@ -640,9 +641,12 @@ static void smbd_post_send_credits(struc
+                       }
+                       atomic_inc(&sc->recv_io.posted.count);
++                      posted += 1;
+               }
+       }
++      atomic_add(posted, &sc->recv_io.credits.available);
++
+       /* Promptly send an immediate packet as defined in [MS-SMBD] 3.1.1.1 */
+       if (atomic_read(&sc->recv_io.credits.count) <
+               sc->recv_io.credits.target - 1) {
+@@ -1033,19 +1037,38 @@ dma_mapping_failed:
+  */
+ static int manage_credits_prior_sending(struct smbdirect_socket *sc)
+ {
++      int missing;
++      int available;
+       int new_credits;
+       if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target)
+               return 0;
+-      new_credits = atomic_read(&sc->recv_io.posted.count);
+-      if (new_credits == 0)
++      missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count);
++      available = atomic_xchg(&sc->recv_io.credits.available, 0);
++      new_credits = (u16)min3(U16_MAX, missing, available);
++      if (new_credits <= 0) {
++              /*
++               * If credits are available, but not granted
++               * we need to re-add them again.
++               */
++              if (available)
++                      atomic_add(available, &sc->recv_io.credits.available);
+               return 0;
++      }
+-      new_credits -= atomic_read(&sc->recv_io.credits.count);
+-      if (new_credits <= 0)
+-              return 0;
++      if (new_credits < available) {
++              /*
++               * Readd the remaining available again.
++               */
++              available -= new_credits;
++              atomic_add(available, &sc->recv_io.credits.available);
++      }
++      /*
++       * Remember we granted the credits
++       */
++      atomic_add(new_credits, &sc->recv_io.credits.count);
+       return new_credits;
+ }
+@@ -1217,7 +1240,6 @@ wait_credit:
+       packet->credits_requested = cpu_to_le16(sp->send_credit_target);
+       new_credits = manage_credits_prior_sending(sc);
+-      atomic_add(new_credits, &sc->recv_io.credits.count);
+       packet->credits_granted = cpu_to_le16(new_credits);
+       packet->flags = 0;
diff --git a/queue-6.19/smb-client-make-use-of-smbdirect_socket.send_io.bcredits.patch b/queue-6.19/smb-client-make-use-of-smbdirect_socket.send_io.bcredits.patch
new file mode 100644 (file)
index 0000000..8acb436
--- /dev/null
@@ -0,0 +1,149 @@
+From 21538121efe6c8c5b51c742fa02cbe820bc48714 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:57 +0100
+Subject: smb: client: make use of smbdirect_socket.send_io.bcredits
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 21538121efe6c8c5b51c742fa02cbe820bc48714 upstream.
+
+It turns out that our code will corrupt the stream of
+reassabled data transfer messages when we trigger an
+immendiate (empty) send.
+
+In order to fix this we'll have a single 'batch' credit per
+connection. And code getting that credit is free to use
+as much messages until remaining_length reaches 0, then
+the batch credit it given back and the next logical send can
+happen.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |   58 +++++++++++++++++++++++++++++++++++++++++++---
+ 1 file changed, 55 insertions(+), 3 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -657,6 +657,7 @@ static bool process_negotiation_response
+                       sp->max_frmr_depth * PAGE_SIZE);
+       sp->max_frmr_depth = sp->max_read_write_size / PAGE_SIZE;
++      atomic_set(&sc->send_io.bcredits.count, 1);
+       sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
+       return true;
+ }
+@@ -1214,6 +1215,7 @@ static void smbd_send_batch_init(struct
+       batch->wr_cnt = 0;
+       batch->need_invalidate_rkey = need_invalidate_rkey;
+       batch->remote_key = remote_key;
++      batch->credit = 0;
+ }
+ static int smbd_send_batch_flush(struct smbdirect_socket *sc,
+@@ -1224,7 +1226,7 @@ static int smbd_send_batch_flush(struct
+       int ret = 0;
+       if (list_empty(&batch->msg_list))
+-              return 0;
++              goto release_credit;
+       first = list_first_entry(&batch->msg_list,
+                                struct smbdirect_send_io,
+@@ -1266,6 +1268,13 @@ static int smbd_send_batch_flush(struct
+               smbd_free_send_io(last);
+       }
++release_credit:
++      if (is_last && !ret && batch->credit) {
++              atomic_add(batch->credit, &sc->send_io.bcredits.count);
++              batch->credit = 0;
++              wake_up(&sc->send_io.bcredits.wait_queue);
++      }
++
+       return ret;
+ }
+@@ -1291,6 +1300,25 @@ static int wait_for_credits(struct smbdi
+       } while (true);
+ }
++static int wait_for_send_bcredit(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *batch)
++{
++      int ret;
++
++      if (batch->credit)
++              return 0;
++
++      ret = wait_for_credits(sc,
++                             &sc->send_io.bcredits.wait_queue,
++                             &sc->send_io.bcredits.count,
++                             1);
++      if (ret)
++              return ret;
++
++      batch->credit = 1;
++      return 0;
++}
++
+ static int wait_for_send_lcredit(struct smbdirect_socket *sc,
+                                struct smbdirect_send_batch *batch)
+ {
+@@ -1338,6 +1366,19 @@ static int smbd_post_send_iter(struct sm
+       struct smbdirect_send_io *request;
+       struct smbdirect_data_transfer *packet;
+       int new_credits = 0;
++      struct smbdirect_send_batch _batch;
++
++      if (!batch) {
++              smbd_send_batch_init(&_batch, false, 0);
++              batch = &_batch;
++      }
++
++      rc = wait_for_send_bcredit(sc, batch);
++      if (rc) {
++              log_outgoing(ERR, "disconnected not sending on wait_bcredit\n");
++              rc = -EAGAIN;
++              goto err_wait_bcredit;
++      }
+       rc = wait_for_send_lcredit(sc, batch);
+       if (rc) {
+@@ -1432,8 +1473,14 @@ static int smbd_post_send_iter(struct sm
+                    le32_to_cpu(packet->remaining_data_length));
+       rc = smbd_post_send(sc, batch, request);
+-      if (!rc)
+-              return 0;
++      if (!rc) {
++              if (batch != &_batch)
++                      return 0;
++
++              rc = smbd_send_batch_flush(sc, batch, true);
++              if (!rc)
++                      return 0;
++      }
+ err_dma:
+       smbd_free_send_io(request);
+@@ -1447,6 +1494,11 @@ err_wait_credit:
+       wake_up(&sc->send_io.lcredits.wait_queue);
+ err_wait_lcredit:
++      atomic_add(batch->credit, &sc->send_io.bcredits.count);
++      batch->credit = 0;
++      wake_up(&sc->send_io.bcredits.wait_queue);
++
++err_wait_bcredit:
+       return rc;
+ }
diff --git a/queue-6.19/smb-client-port-and-use-the-wait_for_credits-logic-used-by-server.patch b/queue-6.19/smb-client-port-and-use-the-wait_for_credits-logic-used-by-server.patch
new file mode 100644 (file)
index 0000000..1bc397c
--- /dev/null
@@ -0,0 +1,125 @@
+From bb848d205f7ac0141af52a5acb6dd116d9b71177 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:53 +0100
+Subject: smb: client: port and use the wait_for_credits logic used by server
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit bb848d205f7ac0141af52a5acb6dd116d9b71177 upstream.
+
+This simplifies the logic and prepares the use of
+smbdirect_send_batch in order to make sure
+all messages in a multi fragment send are grouped
+together.
+
+We'll add the smbdirect_send_batch processin
+in a later patch.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |   70 ++++++++++++++++++++++++++++------------------
+ 1 file changed, 43 insertions(+), 27 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -1137,6 +1137,44 @@ static int smbd_post_send(struct smbdire
+       return rc;
+ }
++static int wait_for_credits(struct smbdirect_socket *sc,
++                          wait_queue_head_t *waitq, atomic_t *total_credits,
++                          int needed)
++{
++      int ret;
++
++      do {
++              if (atomic_sub_return(needed, total_credits) >= 0)
++                      return 0;
++
++              atomic_add(needed, total_credits);
++              ret = wait_event_interruptible(*waitq,
++                                             atomic_read(total_credits) >= needed ||
++                                             sc->status != SMBDIRECT_SOCKET_CONNECTED);
++
++              if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
++                      return -ENOTCONN;
++              else if (ret < 0)
++                      return ret;
++      } while (true);
++}
++
++static int wait_for_send_lcredit(struct smbdirect_socket *sc)
++{
++      return wait_for_credits(sc,
++                              &sc->send_io.lcredits.wait_queue,
++                              &sc->send_io.lcredits.count,
++                              1);
++}
++
++static int wait_for_send_credits(struct smbdirect_socket *sc)
++{
++      return wait_for_credits(sc,
++                              &sc->send_io.credits.wait_queue,
++                              &sc->send_io.credits.count,
++                              1);
++}
++
+ static int smbd_post_send_iter(struct smbdirect_socket *sc,
+                              struct iov_iter *iter,
+                              int *_remaining_data_length)
+@@ -1149,41 +1187,19 @@ static int smbd_post_send_iter(struct sm
+       struct smbdirect_data_transfer *packet;
+       int new_credits = 0;
+-wait_lcredit:
+-      /* Wait for local send credits */
+-      rc = wait_event_interruptible(sc->send_io.lcredits.wait_queue,
+-              atomic_read(&sc->send_io.lcredits.count) > 0 ||
+-              sc->status != SMBDIRECT_SOCKET_CONNECTED);
+-      if (rc)
+-              goto err_wait_lcredit;
+-
+-      if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
+-              log_outgoing(ERR, "disconnected not sending on wait_credit\n");
++      rc = wait_for_send_lcredit(sc);
++      if (rc) {
++              log_outgoing(ERR, "disconnected not sending on wait_lcredit\n");
+               rc = -EAGAIN;
+               goto err_wait_lcredit;
+       }
+-      if (unlikely(atomic_dec_return(&sc->send_io.lcredits.count) < 0)) {
+-              atomic_inc(&sc->send_io.lcredits.count);
+-              goto wait_lcredit;
+-      }
+-
+-wait_credit:
+-      /* Wait for send credits. A SMBD packet needs one credit */
+-      rc = wait_event_interruptible(sc->send_io.credits.wait_queue,
+-              atomic_read(&sc->send_io.credits.count) > 0 ||
+-              sc->status != SMBDIRECT_SOCKET_CONNECTED);
+-      if (rc)
+-              goto err_wait_credit;
+-      if (sc->status != SMBDIRECT_SOCKET_CONNECTED) {
++      rc = wait_for_send_credits(sc);
++      if (rc) {
+               log_outgoing(ERR, "disconnected not sending on wait_credit\n");
+               rc = -EAGAIN;
+               goto err_wait_credit;
+       }
+-      if (unlikely(atomic_dec_return(&sc->send_io.credits.count) < 0)) {
+-              atomic_inc(&sc->send_io.credits.count);
+-              goto wait_credit;
+-      }
+       request = mempool_alloc(sc->send_io.mem.pool, GFP_KERNEL);
+       if (!request) {
diff --git a/queue-6.19/smb-client-remove-pointless-sc-recv_io.credits.count-rollback.patch b/queue-6.19/smb-client-remove-pointless-sc-recv_io.credits.count-rollback.patch
new file mode 100644 (file)
index 0000000..5e4d6ec
--- /dev/null
@@ -0,0 +1,44 @@
+From 6858531e5e8d68828eec349989cefce3f45a487f Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:51 +0100
+Subject: smb: client: remove pointless sc->recv_io.credits.count rollback
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 6858531e5e8d68828eec349989cefce3f45a487f upstream.
+
+We either reach this code path before we call
+new_credits = manage_credits_prior_sending(sc),
+which means new_credits is still 0
+or the connection is already broken as
+smbd_post_send() already called
+smbd_disconnect_rdma_connection().
+
+This will also simplify further changes.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |    3 ---
+ 1 file changed, 3 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -1288,9 +1288,6 @@ err_dma:
+                                           DMA_TO_DEVICE);
+       mempool_free(request, sc->send_io.mem.pool);
+-      /* roll back the granted receive credits */
+-      atomic_sub(new_credits, &sc->recv_io.credits.count);
+-
+ err_alloc:
+       atomic_inc(&sc->send_io.credits.count);
+       wake_up(&sc->send_io.credits.wait_queue);
diff --git a/queue-6.19/smb-client-remove-pointless-sc-send_io.pending-handling-in-smbd_post_send_iter.patch b/queue-6.19/smb-client-remove-pointless-sc-send_io.pending-handling-in-smbd_post_send_iter.patch
new file mode 100644 (file)
index 0000000..92c019a
--- /dev/null
@@ -0,0 +1,43 @@
+From 8bfe3fd33f36b987c8200b112646732b5f5cd8b3 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:52 +0100
+Subject: smb: client: remove pointless sc->send_io.pending handling in smbd_post_send_iter()
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 8bfe3fd33f36b987c8200b112646732b5f5cd8b3 upstream.
+
+If we reach this the connection is already broken as
+smbd_post_send() already called
+smbd_disconnect_rdma_connection().
+
+This will also simplify further changes.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |    5 -----
+ 1 file changed, 5 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -1274,11 +1274,6 @@ wait_credit:
+       if (!rc)
+               return 0;
+-      if (atomic_dec_and_test(&sc->send_io.pending.count))
+-              wake_up(&sc->send_io.pending.zero_wait_queue);
+-
+-      wake_up(&sc->send_io.pending.dec_wait_queue);
+-
+ err_dma:
+       for (i = 0; i < request->num_sge; i++)
+               if (request->sge[i].addr)
diff --git a/queue-6.19/smb-client-split-cached_fid-bitfields-to-avoid-shared-byte-rmw-races.patch b/queue-6.19/smb-client-split-cached_fid-bitfields-to-avoid-shared-byte-rmw-races.patch
new file mode 100644 (file)
index 0000000..69ecec4
--- /dev/null
@@ -0,0 +1,52 @@
+From ec306600d5ba7148c9dbf8f5a8f1f5c1a044a241 Mon Sep 17 00:00:00 2001
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+Date: Tue, 27 Jan 2026 13:01:28 -0300
+Subject: smb: client: split cached_fid bitfields to avoid shared-byte RMW races
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+
+commit ec306600d5ba7148c9dbf8f5a8f1f5c1a044a241 upstream.
+
+is_open, has_lease and on_list are stored in the same bitfield byte in
+struct cached_fid but are updated in different code paths that may run
+concurrently. Bitfield assignments generate byte read–modify–write
+operations (e.g. `orb $mask, addr` on x86_64), so updating one flag can
+restore stale values of the others.
+
+A possible interleaving is:
+    CPU1: load old byte (has_lease=1, on_list=1)
+    CPU2: clear both flags (store 0)
+    CPU1: RMW store (old | IS_OPEN) -> reintroduces cleared bits
+
+To avoid this class of races, convert these flags to separate bool
+fields.
+
+Cc: stable@vger.kernel.org
+Fixes: ebe98f1447bbc ("cifs: enable caching of directories for which a lease is held")
+Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/cached_dir.h |    8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/fs/smb/client/cached_dir.h
++++ b/fs/smb/client/cached_dir.h
+@@ -36,10 +36,10 @@ struct cached_fid {
+       struct list_head entry;
+       struct cached_fids *cfids;
+       const char *path;
+-      bool has_lease:1;
+-      bool is_open:1;
+-      bool on_list:1;
+-      bool file_all_info_is_valid:1;
++      bool has_lease;
++      bool is_open;
++      bool on_list;
++      bool file_all_info_is_valid;
+       unsigned long time; /* jiffies of when lease was taken */
+       unsigned long last_access_time; /* jiffies of when last accessed */
+       struct kref refcount;
diff --git a/queue-6.19/smb-client-split-out-smbd_ib_post_send.patch b/queue-6.19/smb-client-split-out-smbd_ib_post_send.patch
new file mode 100644 (file)
index 0000000..a0bdf94
--- /dev/null
@@ -0,0 +1,87 @@
+From bf30515caec590316e0d08208e4252eed4c160df Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:54 +0100
+Subject: smb: client: split out smbd_ib_post_send()
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit bf30515caec590316e0d08208e4252eed4c160df upstream.
+
+This is like smb_direct_post_send() in the server
+and will simplify porting the smbdirect_send_batch
+and credit related logic from the server.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |   33 +++++++++++++++++----------------
+ 1 file changed, 17 insertions(+), 16 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -1101,11 +1101,26 @@ static int manage_keep_alive_before_send
+       return 0;
+ }
++static int smbd_ib_post_send(struct smbdirect_socket *sc,
++                           struct ib_send_wr *wr)
++{
++      int ret;
++
++      atomic_inc(&sc->send_io.pending.count);
++      ret = ib_post_send(sc->ib.qp, wr, NULL);
++      if (ret) {
++              pr_err("failed to post send: %d\n", ret);
++              smbd_disconnect_rdma_connection(sc);
++              ret = -EAGAIN;
++      }
++      return ret;
++}
++
+ /* Post the send request */
+ static int smbd_post_send(struct smbdirect_socket *sc,
+               struct smbdirect_send_io *request)
+ {
+-      int rc, i;
++      int i;
+       for (i = 0; i < request->num_sge; i++) {
+               log_rdma_send(INFO,
+@@ -1126,15 +1141,7 @@ static int smbd_post_send(struct smbdire
+       request->wr.num_sge = request->num_sge;
+       request->wr.opcode = IB_WR_SEND;
+       request->wr.send_flags = IB_SEND_SIGNALED;
+-
+-      rc = ib_post_send(sc->ib.qp, &request->wr, NULL);
+-      if (rc) {
+-              log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
+-              smbd_disconnect_rdma_connection(sc);
+-              rc = -EAGAIN;
+-      }
+-
+-      return rc;
++      return smbd_ib_post_send(sc, &request->wr);
+ }
+ static int wait_for_credits(struct smbdirect_socket *sc,
+@@ -1280,12 +1287,6 @@ static int smbd_post_send_iter(struct sm
+                    le32_to_cpu(packet->data_length),
+                    le32_to_cpu(packet->remaining_data_length));
+-      /*
+-       * Now that we got a local and a remote credit
+-       * we add us as pending
+-       */
+-      atomic_inc(&sc->send_io.pending.count);
+-
+       rc = smbd_post_send(sc, request);
+       if (!rc)
+               return 0;
diff --git a/queue-6.19/smb-client-use-smbdirect_send_batch-processing.patch b/queue-6.19/smb-client-use-smbdirect_send_batch-processing.patch
new file mode 100644 (file)
index 0000000..431886a
--- /dev/null
@@ -0,0 +1,303 @@
+From 2c1ac39ce9cd4112f406775c626eef7f3eb4c481 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:56 +0100
+Subject: smb: client: use smbdirect_send_batch processing
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 2c1ac39ce9cd4112f406775c626eef7f3eb4c481 upstream.
+
+This will allow us to use similar logic as we have in
+the server soon, so that we can share common code later.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/client/smbdirect.c |  149 +++++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 135 insertions(+), 14 deletions(-)
+
+--- a/fs/smb/client/smbdirect.c
++++ b/fs/smb/client/smbdirect.c
+@@ -544,11 +544,20 @@ static void send_done(struct ib_cq *cq,
+       struct smbdirect_send_io *request =
+               container_of(wc->wr_cqe, struct smbdirect_send_io, cqe);
+       struct smbdirect_socket *sc = request->socket;
++      struct smbdirect_send_io *sibling, *next;
+       int lcredits = 0;
+       log_rdma_send(INFO, "smbdirect_send_io 0x%p completed wc->status=%s\n",
+               request, ib_wc_status_msg(wc->status));
++      /*
++       * Free possible siblings and then the main send_io
++       */
++      list_for_each_entry_safe(sibling, next, &request->sibling_list, sibling_list) {
++              list_del_init(&sibling->sibling_list);
++              smbd_free_send_io(sibling);
++              lcredits += 1;
++      }
+       /* Note this frees wc->wr_cqe, but not wc */
+       smbd_free_send_io(request);
+       lcredits += 1;
+@@ -1154,7 +1163,8 @@ static int smbd_ib_post_send(struct smbd
+ /* Post the send request */
+ static int smbd_post_send(struct smbdirect_socket *sc,
+-              struct smbdirect_send_io *request)
++                        struct smbdirect_send_batch *batch,
++                        struct smbdirect_send_io *request)
+ {
+       int i;
+@@ -1170,16 +1180,95 @@ static int smbd_post_send(struct smbdire
+       }
+       request->cqe.done = send_done;
+-
+       request->wr.next = NULL;
+-      request->wr.wr_cqe = &request->cqe;
+       request->wr.sg_list = request->sge;
+       request->wr.num_sge = request->num_sge;
+       request->wr.opcode = IB_WR_SEND;
++
++      if (batch) {
++              request->wr.wr_cqe = NULL;
++              request->wr.send_flags = 0;
++              if (!list_empty(&batch->msg_list)) {
++                      struct smbdirect_send_io *last;
++
++                      last = list_last_entry(&batch->msg_list,
++                                             struct smbdirect_send_io,
++                                             sibling_list);
++                      last->wr.next = &request->wr;
++              }
++              list_add_tail(&request->sibling_list, &batch->msg_list);
++              batch->wr_cnt++;
++              return 0;
++      }
++
++      request->wr.wr_cqe = &request->cqe;
+       request->wr.send_flags = IB_SEND_SIGNALED;
+       return smbd_ib_post_send(sc, &request->wr);
+ }
++static void smbd_send_batch_init(struct smbdirect_send_batch *batch,
++                               bool need_invalidate_rkey,
++                               unsigned int remote_key)
++{
++      INIT_LIST_HEAD(&batch->msg_list);
++      batch->wr_cnt = 0;
++      batch->need_invalidate_rkey = need_invalidate_rkey;
++      batch->remote_key = remote_key;
++}
++
++static int smbd_send_batch_flush(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *batch,
++                               bool is_last)
++{
++      struct smbdirect_send_io *first, *last;
++      int ret = 0;
++
++      if (list_empty(&batch->msg_list))
++              return 0;
++
++      first = list_first_entry(&batch->msg_list,
++                               struct smbdirect_send_io,
++                               sibling_list);
++      last = list_last_entry(&batch->msg_list,
++                             struct smbdirect_send_io,
++                             sibling_list);
++
++      if (batch->need_invalidate_rkey) {
++              first->wr.opcode = IB_WR_SEND_WITH_INV;
++              first->wr.ex.invalidate_rkey = batch->remote_key;
++              batch->need_invalidate_rkey = false;
++              batch->remote_key = 0;
++      }
++
++      last->wr.send_flags = IB_SEND_SIGNALED;
++      last->wr.wr_cqe = &last->cqe;
++
++      /*
++       * Remove last from batch->msg_list
++       * and splice the rest of batch->msg_list
++       * to last->sibling_list.
++       *
++       * batch->msg_list is a valid empty list
++       * at the end.
++       */
++      list_del_init(&last->sibling_list);
++      list_splice_tail_init(&batch->msg_list, &last->sibling_list);
++      batch->wr_cnt = 0;
++
++      ret = smbd_ib_post_send(sc, &first->wr);
++      if (ret) {
++              struct smbdirect_send_io *sibling, *next;
++
++              list_for_each_entry_safe(sibling, next, &last->sibling_list, sibling_list) {
++                      list_del_init(&sibling->sibling_list);
++                      smbd_free_send_io(sibling);
++              }
++              smbd_free_send_io(last);
++      }
++
++      return ret;
++}
++
+ static int wait_for_credits(struct smbdirect_socket *sc,
+                           wait_queue_head_t *waitq, atomic_t *total_credits,
+                           int needed)
+@@ -1202,16 +1291,35 @@ static int wait_for_credits(struct smbdi
+       } while (true);
+ }
+-static int wait_for_send_lcredit(struct smbdirect_socket *sc)
++static int wait_for_send_lcredit(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *batch)
+ {
++      if (batch && (atomic_read(&sc->send_io.lcredits.count) <= 1)) {
++              int ret;
++
++              ret = smbd_send_batch_flush(sc, batch, false);
++              if (ret)
++                      return ret;
++      }
++
+       return wait_for_credits(sc,
+                               &sc->send_io.lcredits.wait_queue,
+                               &sc->send_io.lcredits.count,
+                               1);
+ }
+-static int wait_for_send_credits(struct smbdirect_socket *sc)
++static int wait_for_send_credits(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *batch)
+ {
++      if (batch &&
++          (batch->wr_cnt >= 16 || atomic_read(&sc->send_io.credits.count) <= 1)) {
++              int ret;
++
++              ret = smbd_send_batch_flush(sc, batch, false);
++              if (ret)
++                      return ret;
++      }
++
+       return wait_for_credits(sc,
+                               &sc->send_io.credits.wait_queue,
+                               &sc->send_io.credits.count,
+@@ -1219,6 +1327,7 @@ static int wait_for_send_credits(struct
+ }
+ static int smbd_post_send_iter(struct smbdirect_socket *sc,
++                             struct smbdirect_send_batch *batch,
+                              struct iov_iter *iter,
+                              int *_remaining_data_length)
+ {
+@@ -1230,14 +1339,14 @@ static int smbd_post_send_iter(struct sm
+       struct smbdirect_data_transfer *packet;
+       int new_credits = 0;
+-      rc = wait_for_send_lcredit(sc);
++      rc = wait_for_send_lcredit(sc, batch);
+       if (rc) {
+               log_outgoing(ERR, "disconnected not sending on wait_lcredit\n");
+               rc = -EAGAIN;
+               goto err_wait_lcredit;
+       }
+-      rc = wait_for_send_credits(sc);
++      rc = wait_for_send_credits(sc, batch);
+       if (rc) {
+               log_outgoing(ERR, "disconnected not sending on wait_credit\n");
+               rc = -EAGAIN;
+@@ -1322,7 +1431,7 @@ static int smbd_post_send_iter(struct sm
+                    le32_to_cpu(packet->data_length),
+                    le32_to_cpu(packet->remaining_data_length));
+-      rc = smbd_post_send(sc, request);
++      rc = smbd_post_send(sc, batch, request);
+       if (!rc)
+               return 0;
+@@ -1351,10 +1460,11 @@ static int smbd_post_send_empty(struct s
+       int remaining_data_length = 0;
+       sc->statistics.send_empty++;
+-      return smbd_post_send_iter(sc, NULL, &remaining_data_length);
++      return smbd_post_send_iter(sc, NULL, NULL, &remaining_data_length);
+ }
+ static int smbd_post_send_full_iter(struct smbdirect_socket *sc,
++                                  struct smbdirect_send_batch *batch,
+                                   struct iov_iter *iter,
+                                   int *_remaining_data_length)
+ {
+@@ -1367,7 +1477,7 @@ static int smbd_post_send_full_iter(stru
+        */
+       while (iov_iter_count(iter) > 0) {
+-              rc = smbd_post_send_iter(sc, iter, _remaining_data_length);
++              rc = smbd_post_send_iter(sc, batch, iter, _remaining_data_length);
+               if (rc < 0)
+                       break;
+       }
+@@ -2289,8 +2399,10 @@ int smbd_send(struct TCP_Server_Info *se
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+       struct smb_rqst *rqst;
+       struct iov_iter iter;
++      struct smbdirect_send_batch batch;
+       unsigned int remaining_data_length, klen;
+       int rc, i, rqst_idx;
++      int error = 0;
+       if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+               return -EAGAIN;
+@@ -2315,6 +2427,7 @@ int smbd_send(struct TCP_Server_Info *se
+                       num_rqst, remaining_data_length);
+       rqst_idx = 0;
++      smbd_send_batch_init(&batch, false, 0);
+       do {
+               rqst = &rqst_array[rqst_idx];
+@@ -2333,20 +2446,28 @@ int smbd_send(struct TCP_Server_Info *se
+                       klen += rqst->rq_iov[i].iov_len;
+               iov_iter_kvec(&iter, ITER_SOURCE, rqst->rq_iov, rqst->rq_nvec, klen);
+-              rc = smbd_post_send_full_iter(sc, &iter, &remaining_data_length);
+-              if (rc < 0)
++              rc = smbd_post_send_full_iter(sc, &batch, &iter, &remaining_data_length);
++              if (rc < 0) {
++                      error = rc;
+                       break;
++              }
+               if (iov_iter_count(&rqst->rq_iter) > 0) {
+                       /* And then the data pages if there are any */
+-                      rc = smbd_post_send_full_iter(sc, &rqst->rq_iter,
++                      rc = smbd_post_send_full_iter(sc, &batch, &rqst->rq_iter,
+                                                     &remaining_data_length);
+-                      if (rc < 0)
++                      if (rc < 0) {
++                              error = rc;
+                               break;
++                      }
+               }
+       } while (++rqst_idx < num_rqst);
++      rc = smbd_send_batch_flush(sc, &batch, true);
++      if (unlikely(!rc && error))
++              rc = error;
++
+       /*
+        * As an optimization, we don't wait for individual I/O to finish
+        * before sending the next one.
diff --git a/queue-6.19/smb-server-fix-last-send-credit-problem-causing-disconnects.patch b/queue-6.19/smb-server-fix-last-send-credit-problem-causing-disconnects.patch
new file mode 100644 (file)
index 0000000..64a1fa1
--- /dev/null
@@ -0,0 +1,127 @@
+From 8cf2bbac6281434065f5f3aeab19c9c08ff755a2 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:46 +0100
+Subject: smb: server: fix last send credit problem causing disconnects
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 8cf2bbac6281434065f5f3aeab19c9c08ff755a2 upstream.
+
+When we are about to use the last send credit that was
+granted to us by the peer, we need to wait until
+we are ourself able to grant at least one credit
+to the peer. Otherwise it might not be possible
+for the peer to grant more credits.
+
+The following sections in MS-SMBD are related to this:
+
+3.1.5.1 Sending Upper Layer Messages
+...
+If Connection.SendCredits is 1 and the CreditsGranted field of the
+message is 0, stop processing.
+...
+
+3.1.5.9 Managing Credits Prior to Sending
+...
+If Connection.ReceiveCredits is zero, or if Connection.SendCredits is
+one and the Connection.SendQueue is not empty, the sender MUST allocate
+and post at least one receive of size Connection.MaxReceiveSize and MUST
+increment Connection.ReceiveCredits by the number allocated and posted.
+If no receives are posted, the processing MUST return a value of zero to
+indicate to the caller that no Send message can be currently performed.
+...
+
+This problem was found by running this on Windows 2025
+against ksmbd with required smb signing:
+'frametest.exe -r 4k -t 20 -n 2000' after
+'frametest.exe -w 4k -t 20 -n 2000'.
+
+Link: https://lore.kernel.org/linux-cifs/b58fa352-2386-4145-b42e-9b4b1d484e17@samba.org/
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/transport_rdma.c |   32 ++++++++++++++++++++++++++++++--
+ 1 file changed, 30 insertions(+), 2 deletions(-)
+
+--- a/fs/smb/server/transport_rdma.c
++++ b/fs/smb/server/transport_rdma.c
+@@ -1033,6 +1033,15 @@ static void smb_direct_post_recv_credits
+       atomic_add(credits, &sc->recv_io.credits.available);
++      /*
++       * If the last send credit is waiting for credits
++       * it can grant we need to wake it up
++       */
++      if (credits &&
++          atomic_read(&sc->send_io.bcredits.count) == 0 &&
++          atomic_read(&sc->send_io.credits.count) == 0)
++              wake_up(&sc->send_io.credits.wait_queue);
++
+       if (credits)
+               queue_work(sc->workqueue, &sc->idle.immediate_work);
+ }
+@@ -1306,6 +1315,7 @@ static int calc_rw_credits(struct smbdir
+ static int smb_direct_create_header(struct smbdirect_socket *sc,
+                                   int size, int remaining_data_length,
++                                  int new_credits,
+                                   struct smbdirect_send_io **sendmsg_out)
+ {
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+@@ -1321,7 +1331,7 @@ static int smb_direct_create_header(stru
+       /* Fill in the packet header */
+       packet = (struct smbdirect_data_transfer *)sendmsg->packet;
+       packet->credits_requested = cpu_to_le16(sp->send_credit_target);
+-      packet->credits_granted = cpu_to_le16(manage_credits_prior_sending(sc));
++      packet->credits_granted = cpu_to_le16(new_credits);
+       packet->flags = 0;
+       if (manage_keep_alive_before_sending(sc))
+@@ -1459,6 +1469,7 @@ static int smb_direct_post_send_data(str
+       int data_length;
+       struct scatterlist sg[SMBDIRECT_SEND_IO_MAX_SGE - 1];
+       struct smbdirect_send_batch _send_ctx;
++      int new_credits;
+       if (!send_ctx) {
+               smb_direct_send_ctx_init(&_send_ctx, false, 0);
+@@ -1477,12 +1488,29 @@ static int smb_direct_post_send_data(str
+       if (ret)
+               goto credit_failed;
++      new_credits = manage_credits_prior_sending(sc);
++      if (new_credits == 0 &&
++          atomic_read(&sc->send_io.credits.count) == 0 &&
++          atomic_read(&sc->recv_io.credits.count) == 0) {
++              queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
++              ret = wait_event_interruptible(sc->send_io.credits.wait_queue,
++                                             atomic_read(&sc->send_io.credits.count) >= 1 ||
++                                             atomic_read(&sc->recv_io.credits.available) >= 1 ||
++                                             sc->status != SMBDIRECT_SOCKET_CONNECTED);
++              if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
++                      ret = -ENOTCONN;
++              if (ret < 0)
++                      goto credit_failed;
++
++              new_credits = manage_credits_prior_sending(sc);
++      }
++
+       data_length = 0;
+       for (i = 0; i < niov; i++)
+               data_length += iov[i].iov_len;
+       ret = smb_direct_create_header(sc, data_length, remaining_data_length,
+-                                     &msg);
++                                     new_credits, &msg);
+       if (ret)
+               goto header_failed;
diff --git a/queue-6.19/smb-server-fix-leak-of-active_num_conn-in-ksmbd_tcp_new_connection.patch b/queue-6.19/smb-server-fix-leak-of-active_num_conn-in-ksmbd_tcp_new_connection.patch
new file mode 100644 (file)
index 0000000..1b10064
--- /dev/null
@@ -0,0 +1,44 @@
+From 77ffbcac4e569566d0092d5f22627dfc0896b553 Mon Sep 17 00:00:00 2001
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+Date: Wed, 4 Feb 2026 20:06:43 -0300
+Subject: smb: server: fix leak of active_num_conn in ksmbd_tcp_new_connection()
+
+From: Henrique Carvalho <henrique.carvalho@suse.com>
+
+commit 77ffbcac4e569566d0092d5f22627dfc0896b553 upstream.
+
+On kthread_run() failure in ksmbd_tcp_new_connection(), the transport is
+freed via free_transport(), which does not decrement active_num_conn,
+leaking this counter.
+
+Replace free_transport() with ksmbd_tcp_disconnect().
+
+Fixes: 0d0d4680db22e ("ksmbd: add max connections parameter")
+Cc: stable@vger.kernel.org
+Signed-off-by: Henrique Carvalho <henrique.carvalho@suse.com>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/transport_tcp.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/fs/smb/server/transport_tcp.c
++++ b/fs/smb/server/transport_tcp.c
+@@ -40,6 +40,7 @@ static const struct ksmbd_transport_ops
+ static void tcp_stop_kthread(struct task_struct *kthread);
+ static struct interface *alloc_iface(char *ifname);
++static void ksmbd_tcp_disconnect(struct ksmbd_transport *t);
+ #define KSMBD_TRANS(t)        (&(t)->transport)
+ #define TCP_TRANS(t)  ((struct tcp_transport *)container_of(t, \
+@@ -202,7 +203,7 @@ static int ksmbd_tcp_new_connection(stru
+       if (IS_ERR(handler)) {
+               pr_err("cannot start conn thread\n");
+               rc = PTR_ERR(handler);
+-              free_transport(t);
++              ksmbd_tcp_disconnect(KSMBD_TRANS(t));
+       }
+       return rc;
+ }
diff --git a/queue-6.19/smb-server-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch b/queue-6.19/smb-server-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch
new file mode 100644 (file)
index 0000000..6921ca9
--- /dev/null
@@ -0,0 +1,60 @@
+From 8106978d400cc88a99fb94927afe8fec7391ca3e Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:44 +0100
+Subject: smb: server: let recv_done() queue a refill when the peer is low on credits
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 8106978d400cc88a99fb94927afe8fec7391ca3e upstream.
+
+In captures I saw that Windows was granting 191 credits in a batch
+when its peer posted a lot of messages. We are asking for a
+credit target of 255 and 191 is 252*3/4.
+
+So we also use that logic in order to fill the
+recv buffers available to the peer.
+
+Fixes: a7eef6144c97 ("smb: server: queue post_recv_credits_work in put_recvmsg() and avoid count_avail_recvmsg")
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/transport_rdma.c |    6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/fs/smb/server/transport_rdma.c
++++ b/fs/smb/server/transport_rdma.c
+@@ -644,6 +644,7 @@ static void recv_done(struct ib_cq *cq,
+               struct smbdirect_data_transfer *data_transfer =
+                       (struct smbdirect_data_transfer *)recvmsg->packet;
+               u32 remaining_data_length, data_offset, data_length;
++              int current_recv_credits;
+               u16 old_recv_credit_target;
+               if (wc->byte_len <
+@@ -682,7 +683,7 @@ static void recv_done(struct ib_cq *cq,
+               }
+               atomic_dec(&sc->recv_io.posted.count);
+-              atomic_dec(&sc->recv_io.credits.count);
++              current_recv_credits = atomic_dec_return(&sc->recv_io.credits.count);
+               old_recv_credit_target = sc->recv_io.credits.target;
+               sc->recv_io.credits.target =
+@@ -702,7 +703,8 @@ static void recv_done(struct ib_cq *cq,
+                       wake_up(&sc->send_io.credits.wait_queue);
+               if (data_length) {
+-                      if (sc->recv_io.credits.target > old_recv_credit_target)
++                      if (current_recv_credits <= (sc->recv_io.credits.target / 4) ||
++                          sc->recv_io.credits.target > old_recv_credit_target)
+                               queue_work(sc->workqueue, &sc->recv_io.posted.refill_work);
+                       enqueue_reassembly(sc, recvmsg, (int)data_length);
diff --git a/queue-6.19/smb-server-let-send_done-handle-a-completion-without-ib_send_signaled.patch b/queue-6.19/smb-server-let-send_done-handle-a-completion-without-ib_send_signaled.patch
new file mode 100644 (file)
index 0000000..4041e66
--- /dev/null
@@ -0,0 +1,72 @@
+From 9da82dc73cb03e85d716a2609364572367a5ff47 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:47 +0100
+Subject: smb: server: let send_done handle a completion without IB_SEND_SIGNALED
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 9da82dc73cb03e85d716a2609364572367a5ff47 upstream.
+
+With smbdirect_send_batch processing we likely have requests without
+IB_SEND_SIGNALED, which will be destroyed in the final request
+that has IB_SEND_SIGNALED set.
+
+If the connection is broken all requests are signaled
+even without explicit IB_SEND_SIGNALED.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/transport_rdma.c |   26 ++++++++++++++++++++++++++
+ 1 file changed, 26 insertions(+)
+
+--- a/fs/smb/server/transport_rdma.c
++++ b/fs/smb/server/transport_rdma.c
+@@ -1059,6 +1059,31 @@ static void send_done(struct ib_cq *cq,
+                   ib_wc_status_msg(wc->status), wc->status,
+                   wc->opcode);
++      if (unlikely(!(sendmsg->wr.send_flags & IB_SEND_SIGNALED))) {
++              /*
++               * This happens when smbdirect_send_io is a sibling
++               * before the final message, it is signaled on
++               * error anyway, so we need to skip
++               * smbdirect_connection_free_send_io here,
++               * otherwise is will destroy the memory
++               * of the siblings too, which will cause
++               * use after free problems for the others
++               * triggered from ib_drain_qp().
++               */
++              if (wc->status != IB_WC_SUCCESS)
++                      goto skip_free;
++
++              /*
++               * This should not happen!
++               * But we better just close the
++               * connection...
++               */
++              pr_err("unexpected send completion wc->status=%s (%d) wc->opcode=%d\n",
++                     ib_wc_status_msg(wc->status), wc->status, wc->opcode);
++              smb_direct_disconnect_rdma_connection(sc);
++              return;
++      }
++
+       /*
+        * Free possible siblings and then the main send_io
+        */
+@@ -1072,6 +1097,7 @@ static void send_done(struct ib_cq *cq,
+       lcredits += 1;
+       if (wc->status != IB_WC_SUCCESS || wc->opcode != IB_WC_SEND) {
++skip_free:
+               pr_err("Send error. status='%s (%d)', opcode=%d\n",
+                      ib_wc_status_msg(wc->status), wc->status,
+                      wc->opcode);
diff --git a/queue-6.19/smb-server-make-use-of-smbdirect_socket.recv_io.credits.available.patch b/queue-6.19/smb-server-make-use-of-smbdirect_socket.recv_io.credits.available.patch
new file mode 100644 (file)
index 0000000..6bfd870
--- /dev/null
@@ -0,0 +1,95 @@
+From 26ad87a2cfb8c1384620d1693a166ed87303046e Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:43 +0100
+Subject: smb: server: make use of smbdirect_socket.recv_io.credits.available
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 26ad87a2cfb8c1384620d1693a166ed87303046e upstream.
+
+The logic off managing recv credits by counting posted recv_io and
+granted credits is racy.
+
+That's because the peer might already consumed a credit,
+but between receiving the incoming recv at the hardware
+and processing the completion in the 'recv_done' functions
+we likely have a window where we grant credits, which
+don't really exist.
+
+So we better have a decicated counter for the
+available credits, which will be incremented
+when we posted new recv buffers and drained when
+we grant the credits to the peer.
+
+This fixes regression Namjae reported with
+the 6.18 release.
+
+Fixes: 89b021a72663 ("smb: server: manage recv credits by counting posted recv_io and granted credits")
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/transport_rdma.c |   30 +++++++++++++++++++++++++-----
+ 1 file changed, 25 insertions(+), 5 deletions(-)
+
+--- a/fs/smb/server/transport_rdma.c
++++ b/fs/smb/server/transport_rdma.c
+@@ -1028,6 +1028,8 @@ static void smb_direct_post_recv_credits
+               }
+       }
++      atomic_add(credits, &sc->recv_io.credits.available);
++
+       if (credits)
+               queue_work(sc->workqueue, &sc->idle.immediate_work);
+ }
+@@ -1074,19 +1076,37 @@ static void send_done(struct ib_cq *cq,
+ static int manage_credits_prior_sending(struct smbdirect_socket *sc)
+ {
++      int missing;
++      int available;
+       int new_credits;
+       if (atomic_read(&sc->recv_io.credits.count) >= sc->recv_io.credits.target)
+               return 0;
+-      new_credits = atomic_read(&sc->recv_io.posted.count);
+-      if (new_credits == 0)
++      missing = (int)sc->recv_io.credits.target - atomic_read(&sc->recv_io.credits.count);
++      available = atomic_xchg(&sc->recv_io.credits.available, 0);
++      new_credits = (u16)min3(U16_MAX, missing, available);
++      if (new_credits <= 0) {
++              /*
++               * If credits are available, but not granted
++               * we need to re-add them again.
++               */
++              if (available)
++                      atomic_add(available, &sc->recv_io.credits.available);
+               return 0;
++      }
+-      new_credits -= atomic_read(&sc->recv_io.credits.count);
+-      if (new_credits <= 0)
+-              return 0;
++      if (new_credits < available) {
++              /*
++               * Readd the remaining available again.
++               */
++              available -= new_credits;
++              atomic_add(available, &sc->recv_io.credits.available);
++      }
++      /*
++       * Remember we granted the credits
++       */
+       atomic_add(new_credits, &sc->recv_io.credits.count);
+       return new_credits;
+ }
diff --git a/queue-6.19/smb-server-make-use-of-smbdirect_socket.send_io.bcredits.patch b/queue-6.19/smb-server-make-use-of-smbdirect_socket.send_io.bcredits.patch
new file mode 100644 (file)
index 0000000..8d2ff04
--- /dev/null
@@ -0,0 +1,153 @@
+From 34abd408c8ba24d7c97bd02ba874d8c714f49db1 Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:45 +0100
+Subject: smb: server: make use of smbdirect_socket.send_io.bcredits
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 34abd408c8ba24d7c97bd02ba874d8c714f49db1 upstream.
+
+It turns out that our code will corrupt the stream of
+reassabled data transfer messages when we trigger an
+immendiate (empty) send.
+
+In order to fix this we'll have a single 'batch' credit per
+connection. And code getting that credit is free to use
+as much messages until remaining_length reaches 0, then
+the batch credit it given back and the next logical send can
+happen.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/server/transport_rdma.c |   53 +++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 51 insertions(+), 2 deletions(-)
+
+--- a/fs/smb/server/transport_rdma.c
++++ b/fs/smb/server/transport_rdma.c
+@@ -221,6 +221,7 @@ static void smb_direct_disconnect_wake_u
+        * in order to notice the broken connection.
+        */
+       wake_up_all(&sc->status_wait);
++      wake_up_all(&sc->send_io.bcredits.wait_queue);
+       wake_up_all(&sc->send_io.lcredits.wait_queue);
+       wake_up_all(&sc->send_io.credits.wait_queue);
+       wake_up_all(&sc->send_io.pending.zero_wait_queue);
+@@ -1152,6 +1153,7 @@ static void smb_direct_send_ctx_init(str
+       send_ctx->wr_cnt = 0;
+       send_ctx->need_invalidate_rkey = need_invalidate_rkey;
+       send_ctx->remote_key = remote_key;
++      send_ctx->credit = 0;
+ }
+ static int smb_direct_flush_send_list(struct smbdirect_socket *sc,
+@@ -1159,10 +1161,10 @@ static int smb_direct_flush_send_list(st
+                                     bool is_last)
+ {
+       struct smbdirect_send_io *first, *last;
+-      int ret;
++      int ret = 0;
+       if (list_empty(&send_ctx->msg_list))
+-              return 0;
++              goto release_credit;
+       first = list_first_entry(&send_ctx->msg_list,
+                                struct smbdirect_send_io,
+@@ -1204,6 +1206,13 @@ static int smb_direct_flush_send_list(st
+               smb_direct_free_sendmsg(sc, last);
+       }
++release_credit:
++      if (is_last && !ret && send_ctx->credit) {
++              atomic_add(send_ctx->credit, &sc->send_io.bcredits.count);
++              send_ctx->credit = 0;
++              wake_up(&sc->send_io.bcredits.wait_queue);
++      }
++
+       return ret;
+ }
+@@ -1229,6 +1238,25 @@ static int wait_for_credits(struct smbdi
+       } while (true);
+ }
++static int wait_for_send_bcredit(struct smbdirect_socket *sc,
++                               struct smbdirect_send_batch *send_ctx)
++{
++      int ret;
++
++      if (send_ctx->credit)
++              return 0;
++
++      ret = wait_for_credits(sc,
++                             &sc->send_io.bcredits.wait_queue,
++                             &sc->send_io.bcredits.count,
++                             1);
++      if (ret)
++              return ret;
++
++      send_ctx->credit = 1;
++      return 0;
++}
++
+ static int wait_for_send_lcredit(struct smbdirect_socket *sc,
+                                struct smbdirect_send_batch *send_ctx)
+ {
+@@ -1430,6 +1458,16 @@ static int smb_direct_post_send_data(str
+       struct smbdirect_send_io *msg;
+       int data_length;
+       struct scatterlist sg[SMBDIRECT_SEND_IO_MAX_SGE - 1];
++      struct smbdirect_send_batch _send_ctx;
++
++      if (!send_ctx) {
++              smb_direct_send_ctx_init(&_send_ctx, false, 0);
++              send_ctx = &_send_ctx;
++      }
++
++      ret = wait_for_send_bcredit(sc, send_ctx);
++      if (ret)
++              goto bcredit_failed;
+       ret = wait_for_send_lcredit(sc, send_ctx);
+       if (ret)
+@@ -1482,6 +1520,13 @@ static int smb_direct_post_send_data(str
+       ret = post_sendmsg(sc, send_ctx, msg);
+       if (ret)
+               goto err;
++
++      if (send_ctx == &_send_ctx) {
++              ret = smb_direct_flush_send_list(sc, send_ctx, true);
++              if (ret)
++                      goto err;
++      }
++
+       return 0;
+ err:
+       smb_direct_free_sendmsg(sc, msg);
+@@ -1490,6 +1535,9 @@ header_failed:
+ credit_failed:
+       atomic_inc(&sc->send_io.lcredits.count);
+ lcredit_failed:
++      atomic_add(send_ctx->credit, &sc->send_io.bcredits.count);
++      send_ctx->credit = 0;
++bcredit_failed:
+       return ret;
+ }
+@@ -1961,6 +2009,7 @@ static int smb_direct_send_negotiate_res
+               resp->max_fragmented_size =
+                               cpu_to_le32(sp->max_fragmented_recv_size);
++              atomic_set(&sc->send_io.bcredits.count, 1);
+               sc->recv_io.expected = SMBDIRECT_EXPECT_DATA_TRANSFER;
+               sc->status = SMBDIRECT_SOCKET_CONNECTED;
+       }
diff --git a/queue-6.19/smb-smbdirect-introduce-smbdirect_socket.recv_io.credits.available.patch b/queue-6.19/smb-smbdirect-introduce-smbdirect_socket.recv_io.credits.available.patch
new file mode 100644 (file)
index 0000000..0bf6eb8
--- /dev/null
@@ -0,0 +1,58 @@
+From 6e3c5052f9686192e178806e017b7377155f4bab Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:41 +0100
+Subject: smb: smbdirect: introduce smbdirect_socket.recv_io.credits.available
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 6e3c5052f9686192e178806e017b7377155f4bab upstream.
+
+The logic off managing recv credits by counting posted recv_io and
+granted credits is racy.
+
+That's because the peer might already consumed a credit,
+but between receiving the incoming recv at the hardware
+and processing the completion in the 'recv_done' functions
+we likely have a window where we grant credits, which
+don't really exist.
+
+So we better have a decicated counter for the
+available credits, which will be incremented
+when we posted new recv buffers and drained when
+we grant the credits to the peer.
+
+Fixes: 5fb9b459b368 ("smb: client: count the number of posted recv_io messages in order to calculated credits")
+Fixes: 89b021a72663 ("smb: server: manage recv credits by counting posted recv_io and granted credits")
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/common/smbdirect/smbdirect_socket.h |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/fs/smb/common/smbdirect/smbdirect_socket.h
++++ b/fs/smb/common/smbdirect/smbdirect_socket.h
+@@ -239,6 +239,7 @@ struct smbdirect_socket {
+                */
+               struct {
+                       u16 target;
++                      atomic_t available;
+                       atomic_t count;
+               } credits;
+@@ -387,6 +388,7 @@ static __always_inline void smbdirect_so
+       INIT_WORK(&sc->recv_io.posted.refill_work, __smbdirect_socket_disabled_work);
+       disable_work_sync(&sc->recv_io.posted.refill_work);
++      atomic_set(&sc->recv_io.credits.available, 0);
+       atomic_set(&sc->recv_io.credits.count, 0);
+       INIT_LIST_HEAD(&sc->recv_io.reassembly.list);
diff --git a/queue-6.19/smb-smbdirect-introduce-smbdirect_socket.send_io.bcredits.patch b/queue-6.19/smb-smbdirect-introduce-smbdirect_socket.send_io.bcredits.patch
new file mode 100644 (file)
index 0000000..603182d
--- /dev/null
@@ -0,0 +1,73 @@
+From 8e94268b21c8235d430ce1aa6dc0b15952744b9b Mon Sep 17 00:00:00 2001
+From: Stefan Metzmacher <metze@samba.org>
+Date: Thu, 22 Jan 2026 18:16:42 +0100
+Subject: smb: smbdirect: introduce smbdirect_socket.send_io.bcredits.*
+
+From: Stefan Metzmacher <metze@samba.org>
+
+commit 8e94268b21c8235d430ce1aa6dc0b15952744b9b upstream.
+
+It turns out that our code will corrupt the stream of
+reassabled data transfer messages when we trigger an
+immendiate (empty) send.
+
+In order to fix this we'll have a single 'batch' credit per
+connection. And code getting that credit is free to use
+as much messages until remaining_length reaches 0, then
+the batch credit it given back and the next logical send can
+happen.
+
+Cc: <stable@vger.kernel.org> # 6.18.x
+Cc: Steve French <smfrench@gmail.com>
+Cc: Tom Talpey <tom@talpey.com>
+Cc: Long Li <longli@microsoft.com>
+Cc: Namjae Jeon <linkinjeon@kernel.org>
+Cc: linux-cifs@vger.kernel.org
+Cc: samba-technical@lists.samba.org
+Signed-off-by: Stefan Metzmacher <metze@samba.org>
+Acked-by: Namjae Jeon <linkinjeon@kernel.org>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/smb/common/smbdirect/smbdirect_socket.h |   16 ++++++++++++++++
+ 1 file changed, 16 insertions(+)
+
+--- a/fs/smb/common/smbdirect/smbdirect_socket.h
++++ b/fs/smb/common/smbdirect/smbdirect_socket.h
+@@ -163,6 +163,17 @@ struct smbdirect_socket {
+               } mem;
+               /*
++               * This is a coordination for smbdirect_send_batch.
++               *
++               * There's only one possible credit, which means
++               * only one instance is running at a time.
++               */
++              struct {
++                      atomic_t count;
++                      wait_queue_head_t wait_queue;
++              } bcredits;
++
++              /*
+                * The local credit state for ib_post_send()
+                */
+               struct {
+@@ -371,6 +382,9 @@ static __always_inline void smbdirect_so
+       INIT_DELAYED_WORK(&sc->idle.timer_work, __smbdirect_socket_disabled_work);
+       disable_delayed_work_sync(&sc->idle.timer_work);
++      atomic_set(&sc->send_io.bcredits.count, 0);
++      init_waitqueue_head(&sc->send_io.bcredits.wait_queue);
++
+       atomic_set(&sc->send_io.lcredits.count, 0);
+       init_waitqueue_head(&sc->send_io.lcredits.wait_queue);
+@@ -485,6 +499,8 @@ struct smbdirect_send_batch {
+        */
+       bool need_invalidate_rkey;
+       u32 remote_key;
++
++      int credit;
+ };
+ struct smbdirect_recv_io {