From 0f68b87cc9f8bb6957bb3f41acc1f039f5d776cd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 13 Feb 2026 11:09:59 +0100 Subject: [PATCH] 6.18-stable patches 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 --- ...-device_lock-for-driver_match_device.patch | 88 +++++ ...k-to-protect-ksmbd_chann_list-xarray.patch | 110 +++++++ ...mb2_rcv_hdr_off-reset-in-error-paths.patch | 62 ++++ queue-6.18/series | 25 ++ ...d-credit-problem-causing-disconnects.patch | 108 ++++++ ...ce-and-use-smbd_-alloc-free-_send_io.patch | 201 ++++++++++++ ...fill-when-the-peer-is-low-on-credits.patch | 61 ++++ ...-completion-without-ib_send_signaled.patch | 78 +++++ ...mbd_post_send-make-use-of-request-wr.patch | 62 ++++ ...end_negotiate_req-use-smbd_post_send.patch | 92 ++++++ ...ect_socket.recv_io.credits.available.patch | 113 +++++++ ...of-smbdirect_socket.send_io.bcredits.patch | 154 +++++++++ ...ait_for_credits-logic-used-by-server.patch | 130 ++++++++ ...ss-sc-recv_io.credits.count-rollback.patch | 49 +++ ...ding-handling-in-smbd_post_send_iter.patch | 48 +++ ...ields-to-avoid-shared-byte-rmw-races.patch | 52 +++ ...b-client-split-out-smbd_ib_post_send.patch | 87 +++++ ...-use-smbdirect_send_batch-processing.patch | 308 ++++++++++++++++++ ...d-credit-problem-causing-disconnects.patch | 127 ++++++++ ...num_conn-in-ksmbd_tcp_new_connection.patch | 44 +++ ...fill-when-the-peer-is-low-on-credits.patch | 60 ++++ ...-completion-without-ib_send_signaled.patch | 72 ++++ ...ect_socket.recv_io.credits.available.patch | 95 ++++++ ...of-smbdirect_socket.send_io.bcredits.patch | 153 +++++++++ ...ect_socket.recv_io.credits.available.patch | 58 ++++ ...ce-smbdirect_socket.send_io.bcredits.patch | 73 +++++ 26 files changed, 2510 insertions(+) create mode 100644 queue-6.18/driver-core-enforce-device_lock-for-driver_match_device.patch create mode 100644 queue-6.18/ksmbd-add-chann_lock-to-protect-ksmbd_chann_list-xarray.patch create mode 100644 queue-6.18/ksmbd-fix-infinite-loop-caused-by-next_smb2_rcv_hdr_off-reset-in-error-paths.patch create mode 100644 queue-6.18/smb-client-fix-last-send-credit-problem-causing-disconnects.patch create mode 100644 queue-6.18/smb-client-introduce-and-use-smbd_-alloc-free-_send_io.patch create mode 100644 queue-6.18/smb-client-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch create mode 100644 queue-6.18/smb-client-let-send_done-handle-a-completion-without-ib_send_signaled.patch create mode 100644 queue-6.18/smb-client-let-smbd_post_send-make-use-of-request-wr.patch create mode 100644 queue-6.18/smb-client-let-smbd_post_send_negotiate_req-use-smbd_post_send.patch create mode 100644 queue-6.18/smb-client-make-use-of-smbdirect_socket.recv_io.credits.available.patch create mode 100644 queue-6.18/smb-client-make-use-of-smbdirect_socket.send_io.bcredits.patch create mode 100644 queue-6.18/smb-client-port-and-use-the-wait_for_credits-logic-used-by-server.patch create mode 100644 queue-6.18/smb-client-remove-pointless-sc-recv_io.credits.count-rollback.patch create mode 100644 queue-6.18/smb-client-remove-pointless-sc-send_io.pending-handling-in-smbd_post_send_iter.patch create mode 100644 queue-6.18/smb-client-split-cached_fid-bitfields-to-avoid-shared-byte-rmw-races.patch create mode 100644 queue-6.18/smb-client-split-out-smbd_ib_post_send.patch create mode 100644 queue-6.18/smb-client-use-smbdirect_send_batch-processing.patch create mode 100644 queue-6.18/smb-server-fix-last-send-credit-problem-causing-disconnects.patch create mode 100644 queue-6.18/smb-server-fix-leak-of-active_num_conn-in-ksmbd_tcp_new_connection.patch create mode 100644 queue-6.18/smb-server-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch create mode 100644 queue-6.18/smb-server-let-send_done-handle-a-completion-without-ib_send_signaled.patch create mode 100644 queue-6.18/smb-server-make-use-of-smbdirect_socket.recv_io.credits.available.patch create mode 100644 queue-6.18/smb-server-make-use-of-smbdirect_socket.send_io.bcredits.patch create mode 100644 queue-6.18/smb-smbdirect-introduce-smbdirect_socket.recv_io.credits.available.patch create mode 100644 queue-6.18/smb-smbdirect-introduce-smbdirect_socket.send_io.bcredits.patch diff --git a/queue-6.18/driver-core-enforce-device_lock-for-driver_match_device.patch b/queue-6.18/driver-core-enforce-device_lock-for-driver_match_device.patch new file mode 100644 index 0000000000..b3ad12eb37 --- /dev/null +++ b/queue-6.18/driver-core-enforce-device_lock-for-driver_match_device.patch @@ -0,0 +1,88 @@ +From dc23806a7c47ec5f1293aba407fb69519f976ee0 Mon Sep 17 00:00:00 2001 +From: Gui-Dong Han +Date: Wed, 14 Jan 2026 00:28:43 +0800 +Subject: driver core: enforce device_lock for driver_match_device() + +From: Gui-Dong Han + +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 +Signed-off-by: Gui-Dong Han +Fixes: 49b420a13ff9 ("driver core: check bus->match without holding device lock") +Reviewed-by: Danilo Krummrich +Reviewed-by: Greg Kroah-Hartman +Reviewed-by: Rafael J. Wysocki (Intel) +Link: https://patch.msgid.link/20260113162843.12712-1-hanguidong02@gmail.com +Signed-off-by: Danilo Krummrich +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -166,9 +166,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 +@@ -1170,7 +1170,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.18/ksmbd-add-chann_lock-to-protect-ksmbd_chann_list-xarray.patch b/queue-6.18/ksmbd-add-chann_lock-to-protect-ksmbd_chann_list-xarray.patch new file mode 100644 index 0000000000..6f31a61b1d --- /dev/null +++ b/queue-6.18/ksmbd-add-chann_lock-to-protect-ksmbd_chann_list-xarray.patch @@ -0,0 +1,110 @@ +From 4f3a06cc57976cafa8c6f716646be6c79a99e485 Mon Sep 17 00:00:00 2001 +From: Namjae Jeon +Date: Mon, 9 Feb 2026 10:43:19 +0900 +Subject: ksmbd: add chann_lock to protect ksmbd_chann_list xarray + +From: Namjae Jeon + +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 +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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; + } + + /** +@@ -1563,8 +1569,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); +@@ -1661,8 +1669,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.18/ksmbd-fix-infinite-loop-caused-by-next_smb2_rcv_hdr_off-reset-in-error-paths.patch b/queue-6.18/ksmbd-fix-infinite-loop-caused-by-next_smb2_rcv_hdr_off-reset-in-error-paths.patch new file mode 100644 index 0000000000..10d44390ec --- /dev/null +++ b/queue-6.18/ksmbd-fix-infinite-loop-caused-by-next_smb2_rcv_hdr_off-reset-in-error-paths.patch @@ -0,0 +1,62 @@ +From 010eb01ce23b34b50531448b0da391c7f05a72af Mon Sep 17 00:00:00 2001 +From: Namjae Jeon +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 + +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 +Cc: stable@vger.kernel.org +Signed-off-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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; + } + } + diff --git a/queue-6.18/series b/queue-6.18/series index 440e4a4721..ae7e939de2 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -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.18/smb-client-fix-last-send-credit-problem-causing-disconnects.patch b/queue-6.18/smb-client-fix-last-send-credit-problem-causing-disconnects.patch new file mode 100644 index 0000000000..334e359de6 --- /dev/null +++ b/queue-6.18/smb-client-fix-last-send-credit-problem-causing-disconnects.patch @@ -0,0 +1,108 @@ +From 93ac432274e1361b4f6cd69e7c5d9b3ac21e13f5 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:58 +0100 +Subject: smb: client: fix last send credit problem causing disconnects + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index dbb2d939bc44..20faa6d7f514 100644 +--- a/fs/smb/client/smbdirect.c ++++ b/fs/smb/client/smbdirect.c +@@ -697,6 +697,15 @@ static void smbd_post_send_credits(struct work_struct *work) + + 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 smbdirect_socket *sc, + 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 smbdirect_socket *sc, + + /* 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; +-- +2.53.0 + diff --git a/queue-6.18/smb-client-introduce-and-use-smbd_-alloc-free-_send_io.patch b/queue-6.18/smb-client-introduce-and-use-smbd_-alloc-free-_send_io.patch new file mode 100644 index 0000000000..96884411c9 --- /dev/null +++ b/queue-6.18/smb-client-introduce-and-use-smbd_-alloc-free-_send_io.patch @@ -0,0 +1,201 @@ +From dc77da0373529d43175984b390106be2d8f03609 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:55 +0100 +Subject: smb: client: introduce and use smbd_{alloc, free}_send_io() + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 87 ++++++++++++++++++++++++++------------- + 1 file changed, 58 insertions(+), 29 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index 75c0ac9cc65c..6cb40da7e589 100644 +--- a/fs/smb/client/smbdirect.c ++++ b/fs/smb/client/smbdirect.c +@@ -493,10 +493,54 @@ static inline void *smbdirect_recv_io_payload(struct smbdirect_recv_io *response + 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, struct ib_wc *wc) + 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 *sc) + { + 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(struct smbdirect_socket *sc) + 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(struct smbdirect_socket *sc) + 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(struct smbdirect_socket *sc) + /* 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 smbdirect_socket *sc, + 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 smbdirect_socket *sc, + 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 smbdirect_socket *sc, + 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); +-- +2.53.0 + diff --git a/queue-6.18/smb-client-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch b/queue-6.18/smb-client-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch new file mode 100644 index 0000000000..027ef97c7d --- /dev/null +++ b/queue-6.18/smb-client-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch @@ -0,0 +1,61 @@ +From defb3c05fee94b296eebe05aaea16d2664b00252 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +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 + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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.18/smb-client-let-send_done-handle-a-completion-without-ib_send_signaled.patch b/queue-6.18/smb-client-let-send_done-handle-a-completion-without-ib_send_signaled.patch new file mode 100644 index 0000000000..b078bc5938 --- /dev/null +++ b/queue-6.18/smb-client-let-send_done-handle-a-completion-without-ib_send_signaled.patch @@ -0,0 +1,78 @@ +From cf74fcdc43b322b6188a0750b5ee79e38be6d078 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +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 + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index 88fefb901c27..01d55bcc6d0f 100644 +--- a/fs/smb/client/smbdirect.c ++++ b/fs/smb/client/smbdirect.c +@@ -554,6 +554,32 @@ static void send_done(struct ib_cq *cq, struct ib_wc *wc) + 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, struct ib_wc *wc) + 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); +-- +2.53.0 + diff --git a/queue-6.18/smb-client-let-smbd_post_send-make-use-of-request-wr.patch b/queue-6.18/smb-client-let-smbd_post_send-make-use-of-request-wr.patch new file mode 100644 index 0000000000..ef67ed86d8 --- /dev/null +++ b/queue-6.18/smb-client-let-smbd_post_send-make-use-of-request-wr.patch @@ -0,0 +1,62 @@ +From bf1656e12a9db2add716c7fb57b56967f69599fa Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:50 +0100 +Subject: smb: client: let smbd_post_send() make use of request->wr + +From: Stefan Metzmacher + +commit bf1656e12a9db2add716c7fb57b56967f69599fa upstream. + +We don't need a stack variable in addition. + +Cc: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index 61693b4a83fc..f2ae35a9f047 100644 +--- a/fs/smb/client/smbdirect.c ++++ b/fs/smb/client/smbdirect.c +@@ -1105,7 +1105,6 @@ static int manage_keep_alive_before_sending(struct smbdirect_socket *sc) + 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 smbdirect_socket *sc, + + 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); +-- +2.53.0 + diff --git a/queue-6.18/smb-client-let-smbd_post_send_negotiate_req-use-smbd_post_send.patch b/queue-6.18/smb-client-let-smbd_post_send_negotiate_req-use-smbd_post_send.patch new file mode 100644 index 0000000000..822726d722 --- /dev/null +++ b/queue-6.18/smb-client-let-smbd_post_send_negotiate_req-use-smbd_post_send.patch @@ -0,0 +1,92 @@ +From 5b1c6149657af840a02885135c700ab42e6aa322 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +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 + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 32 +++++++------------------------- + 1 file changed, 7 insertions(+), 25 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index 20faa6d7f514..88fefb901c27 100644 +--- 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 @@ static int smbd_ia_open( + 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(struct smbdirect_socket *sc) + 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); +-- +2.53.0 + diff --git a/queue-6.18/smb-client-make-use-of-smbdirect_socket.recv_io.credits.available.patch b/queue-6.18/smb-client-make-use-of-smbdirect_socket.recv_io.credits.available.patch new file mode 100644 index 0000000000..ca20f1be1d --- /dev/null +++ b/queue-6.18/smb-client-make-use-of-smbdirect_socket.recv_io.credits.available.patch @@ -0,0 +1,113 @@ +From 9911b1ed187a770a43950bf51f340ad4b7beecba Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:48 +0100 +Subject: smb: client: make use of smbdirect_socket.recv_io.credits.available + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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.18/smb-client-make-use-of-smbdirect_socket.send_io.bcredits.patch b/queue-6.18/smb-client-make-use-of-smbdirect_socket.send_io.bcredits.patch new file mode 100644 index 0000000000..f8c99dc27a --- /dev/null +++ b/queue-6.18/smb-client-make-use-of-smbdirect_socket.send_io.bcredits.patch @@ -0,0 +1,154 @@ +From 21538121efe6c8c5b51c742fa02cbe820bc48714 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:57 +0100 +Subject: smb: client: make use of smbdirect_socket.send_io.bcredits + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 58 +++++++++++++++++++++++++++++++++++++-- + 1 file changed, 55 insertions(+), 3 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index ef3b237bccc1..dbb2d939bc44 100644 +--- 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 smbdirect_send_batch *batch, + 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 smbdirect_socket *sc, + 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 smbdirect_socket *sc, + 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 smbdirect_socket *sc, + } 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 smbdirect_socket *sc, + 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 smbdirect_socket *sc, + 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 @@ static int smbd_post_send_iter(struct smbdirect_socket *sc, + 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; + } + +-- +2.53.0 + diff --git a/queue-6.18/smb-client-port-and-use-the-wait_for_credits-logic-used-by-server.patch b/queue-6.18/smb-client-port-and-use-the-wait_for_credits-logic-used-by-server.patch new file mode 100644 index 0000000000..fe74af1638 --- /dev/null +++ b/queue-6.18/smb-client-port-and-use-the-wait_for_credits-logic-used-by-server.patch @@ -0,0 +1,130 @@ +From bb848d205f7ac0141af52a5acb6dd116d9b71177 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +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 + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 70 ++++++++++++++++++++++++--------------- + 1 file changed, 43 insertions(+), 27 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index cfbe8ce0db42..405931ce3978 100644 +--- a/fs/smb/client/smbdirect.c ++++ b/fs/smb/client/smbdirect.c +@@ -1137,6 +1137,44 @@ static int smbd_post_send(struct smbdirect_socket *sc, + 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 smbdirect_socket *sc, + 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) { +-- +2.53.0 + diff --git a/queue-6.18/smb-client-remove-pointless-sc-recv_io.credits.count-rollback.patch b/queue-6.18/smb-client-remove-pointless-sc-recv_io.credits.count-rollback.patch new file mode 100644 index 0000000000..da7527bfa9 --- /dev/null +++ b/queue-6.18/smb-client-remove-pointless-sc-recv_io.credits.count-rollback.patch @@ -0,0 +1,49 @@ +From 6858531e5e8d68828eec349989cefce3f45a487f Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:51 +0100 +Subject: smb: client: remove pointless sc->recv_io.credits.count rollback + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 3 --- + 1 file changed, 3 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index f2ae35a9f047..c9fcd35e0c77 100644 +--- a/fs/smb/client/smbdirect.c ++++ b/fs/smb/client/smbdirect.c +@@ -1288,9 +1288,6 @@ static int smbd_post_send_iter(struct smbdirect_socket *sc, + 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); +-- +2.53.0 + diff --git a/queue-6.18/smb-client-remove-pointless-sc-send_io.pending-handling-in-smbd_post_send_iter.patch b/queue-6.18/smb-client-remove-pointless-sc-send_io.pending-handling-in-smbd_post_send_iter.patch new file mode 100644 index 0000000000..9b6978f2b6 --- /dev/null +++ b/queue-6.18/smb-client-remove-pointless-sc-send_io.pending-handling-in-smbd_post_send_iter.patch @@ -0,0 +1,48 @@ +From 8bfe3fd33f36b987c8200b112646732b5f5cd8b3 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +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 + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index c9fcd35e0c77..cfbe8ce0db42 100644 +--- a/fs/smb/client/smbdirect.c ++++ b/fs/smb/client/smbdirect.c +@@ -1274,11 +1274,6 @@ static int smbd_post_send_iter(struct smbdirect_socket *sc, + 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) +-- +2.53.0 + diff --git a/queue-6.18/smb-client-split-cached_fid-bitfields-to-avoid-shared-byte-rmw-races.patch b/queue-6.18/smb-client-split-cached_fid-bitfields-to-avoid-shared-byte-rmw-races.patch new file mode 100644 index 0000000000..69ecec47fd --- /dev/null +++ b/queue-6.18/smb-client-split-cached_fid-bitfields-to-avoid-shared-byte-rmw-races.patch @@ -0,0 +1,52 @@ +From ec306600d5ba7148c9dbf8f5a8f1f5c1a044a241 Mon Sep 17 00:00:00 2001 +From: Henrique Carvalho +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 + +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 +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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.18/smb-client-split-out-smbd_ib_post_send.patch b/queue-6.18/smb-client-split-out-smbd_ib_post_send.patch new file mode 100644 index 0000000000..a0bdf946ca --- /dev/null +++ b/queue-6.18/smb-client-split-out-smbd_ib_post_send.patch @@ -0,0 +1,87 @@ +From bf30515caec590316e0d08208e4252eed4c160df Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:54 +0100 +Subject: smb: client: split out smbd_ib_post_send() + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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.18/smb-client-use-smbdirect_send_batch-processing.patch b/queue-6.18/smb-client-use-smbdirect_send_batch-processing.patch new file mode 100644 index 0000000000..c56dda0448 --- /dev/null +++ b/queue-6.18/smb-client-use-smbdirect_send_batch-processing.patch @@ -0,0 +1,308 @@ +From 2c1ac39ce9cd4112f406775c626eef7f3eb4c481 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:56 +0100 +Subject: smb: client: use smbdirect_send_batch processing + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + fs/smb/client/smbdirect.c | 149 ++++++++++++++++++++++++++++++++++---- + 1 file changed, 135 insertions(+), 14 deletions(-) + +diff --git a/fs/smb/client/smbdirect.c b/fs/smb/client/smbdirect.c +index 6cb40da7e589..ef3b237bccc1 100644 +--- 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 ib_wc *wc) + 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 smbdirect_socket *sc, + + /* 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 smbdirect_socket *sc, + } + + 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 smbdirect_socket *sc, + } 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 smbdirect_socket *sc) + } + + 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 smbdirect_socket *sc, + 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 smbdirect_socket *sc, + 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 smbdirect_socket *sc) + 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(struct smbdirect_socket *sc, + */ + + 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 *server, + 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 *server, + 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 *server, + 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. +-- +2.53.0 + diff --git a/queue-6.18/smb-server-fix-last-send-credit-problem-causing-disconnects.patch b/queue-6.18/smb-server-fix-last-send-credit-problem-causing-disconnects.patch new file mode 100644 index 0000000000..ed54ac23f8 --- /dev/null +++ b/queue-6.18/smb-server-fix-last-send-credit-problem-causing-disconnects.patch @@ -0,0 +1,127 @@ +From 8cf2bbac6281434065f5f3aeab19c9c08ff755a2 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:46 +0100 +Subject: smb: server: fix last send credit problem causing disconnects + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Namjae Jeon +Cc: Steve French +Cc: Tom Talpey +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -931,6 +931,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); + } +@@ -1204,6 +1213,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; +@@ -1219,7 +1229,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)) +@@ -1357,6 +1367,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); +@@ -1375,12 +1386,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.18/smb-server-fix-leak-of-active_num_conn-in-ksmbd_tcp_new_connection.patch b/queue-6.18/smb-server-fix-leak-of-active_num_conn-in-ksmbd_tcp_new_connection.patch new file mode 100644 index 0000000000..3d37ffc05a --- /dev/null +++ b/queue-6.18/smb-server-fix-leak-of-active_num_conn-in-ksmbd_tcp_new_connection.patch @@ -0,0 +1,44 @@ +From 77ffbcac4e569566d0092d5f22627dfc0896b553 Mon Sep 17 00:00:00 2001 +From: Henrique Carvalho +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 + +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 +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -41,6 +41,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, \ +@@ -216,7 +217,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.18/smb-server-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch b/queue-6.18/smb-server-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch new file mode 100644 index 0000000000..cc1e111430 --- /dev/null +++ b/queue-6.18/smb-server-let-recv_done-queue-a-refill-when-the-peer-is-low-on-credits.patch @@ -0,0 +1,60 @@ +From 8106978d400cc88a99fb94927afe8fec7391ca3e Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +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 + +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: # 6.18.x +Cc: Namjae Jeon +Cc: Steve French +Cc: Tom Talpey +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -661,6 +661,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 < +@@ -699,7 +700,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 = +@@ -719,7 +720,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.18/smb-server-let-send_done-handle-a-completion-without-ib_send_signaled.patch b/queue-6.18/smb-server-let-send_done-handle-a-completion-without-ib_send_signaled.patch new file mode 100644 index 0000000000..adecc5531b --- /dev/null +++ b/queue-6.18/smb-server-let-send_done-handle-a-completion-without-ib_send_signaled.patch @@ -0,0 +1,72 @@ +From 9da82dc73cb03e85d716a2609364572367a5ff47 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +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 + +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: # 6.18.x +Cc: Namjae Jeon +Cc: Steve French +Cc: Tom Talpey +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -957,6 +957,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 + */ +@@ -970,6 +995,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.18/smb-server-make-use-of-smbdirect_socket.recv_io.credits.available.patch b/queue-6.18/smb-server-make-use-of-smbdirect_socket.recv_io.credits.available.patch new file mode 100644 index 0000000000..193d4ea44f --- /dev/null +++ b/queue-6.18/smb-server-make-use-of-smbdirect_socket.recv_io.credits.available.patch @@ -0,0 +1,95 @@ +From 26ad87a2cfb8c1384620d1693a166ed87303046e Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:43 +0100 +Subject: smb: server: make use of smbdirect_socket.recv_io.credits.available + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Namjae Jeon +Cc: Steve French +Cc: Tom Talpey +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -926,6 +926,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); + } +@@ -972,19 +974,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.18/smb-server-make-use-of-smbdirect_socket.send_io.bcredits.patch b/queue-6.18/smb-server-make-use-of-smbdirect_socket.send_io.bcredits.patch new file mode 100644 index 0000000000..58c05743d7 --- /dev/null +++ b/queue-6.18/smb-server-make-use-of-smbdirect_socket.send_io.bcredits.patch @@ -0,0 +1,153 @@ +From 34abd408c8ba24d7c97bd02ba874d8c714f49db1 Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:45 +0100 +Subject: smb: server: make use of smbdirect_socket.send_io.bcredits + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Namjae Jeon +Cc: Steve French +Cc: Tom Talpey +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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); +@@ -1050,6 +1051,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, +@@ -1057,10 +1059,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, +@@ -1102,6 +1104,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; + } + +@@ -1127,6 +1136,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) + { +@@ -1328,6 +1356,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) +@@ -1380,6 +1418,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); +@@ -1388,6 +1433,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; + } + +@@ -1849,6 +1897,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.18/smb-smbdirect-introduce-smbdirect_socket.recv_io.credits.available.patch b/queue-6.18/smb-smbdirect-introduce-smbdirect_socket.recv_io.credits.available.patch new file mode 100644 index 0000000000..4481286212 --- /dev/null +++ b/queue-6.18/smb-smbdirect-introduce-smbdirect_socket.recv_io.credits.available.patch @@ -0,0 +1,58 @@ +From 6e3c5052f9686192e178806e017b7377155f4bab Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:41 +0100 +Subject: smb: smbdirect: introduce smbdirect_socket.recv_io.credits.available + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -231,6 +231,7 @@ struct smbdirect_socket { + */ + struct { + u16 target; ++ atomic_t available; + atomic_t count; + } credits; + +@@ -375,6 +376,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.18/smb-smbdirect-introduce-smbdirect_socket.send_io.bcredits.patch b/queue-6.18/smb-smbdirect-introduce-smbdirect_socket.send_io.bcredits.patch new file mode 100644 index 0000000000..778964c200 --- /dev/null +++ b/queue-6.18/smb-smbdirect-introduce-smbdirect_socket.send_io.bcredits.patch @@ -0,0 +1,73 @@ +From 8e94268b21c8235d430ce1aa6dc0b15952744b9b Mon Sep 17 00:00:00 2001 +From: Stefan Metzmacher +Date: Thu, 22 Jan 2026 18:16:42 +0100 +Subject: smb: smbdirect: introduce smbdirect_socket.send_io.bcredits.* + +From: Stefan Metzmacher + +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: # 6.18.x +Cc: Steve French +Cc: Tom Talpey +Cc: Long Li +Cc: Namjae Jeon +Cc: linux-cifs@vger.kernel.org +Cc: samba-technical@lists.samba.org +Signed-off-by: Stefan Metzmacher +Acked-by: Namjae Jeon +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman +--- + 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 +@@ -155,6 +155,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 { +@@ -359,6 +370,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); + +@@ -473,6 +487,8 @@ struct smbdirect_send_batch { + */ + bool need_invalidate_rkey; + u32 remote_key; ++ ++ int credit; + }; + + struct smbdirect_recv_io { -- 2.47.3