]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
smb: client: fix smbdirect keep alive handling to match the documentation
authorStefan Metzmacher <metze@samba.org>
Thu, 14 Aug 2025 17:41:12 +0000 (19:41 +0200)
committerSteve French <stfrench@microsoft.com>
Sun, 28 Sep 2025 23:29:49 +0000 (18:29 -0500)
We setup the first timer with the negotiate timeout and set
KEEP_ALIVE_PENDING, so that the expired timer disconnects.

On every incoming message we need to reset the timer to the keepalive
interval (120s).

On SMBDIRECT_FLAG_RESPONSE_REQUESTED we need to schedule a response
instead of setting KEEP_ALIVE_PENDING. Doing both would mean
we would also set SMBDIRECT_FLAG_RESPONSE_REQUESTED in that
response. If both ends would do that we'd play ping pong in
a busy loop.

If we move to KEEP_ALIVE_SENT and send the keepalive request
with SMBDIRECT_FLAG_RESPONSE_REQUESTED, we need to setup the
timer with keepalive timeout (5s) in order to disconnect
if no incoming message reset the timer.

The fired timer sets KEEP_ALIVE_PENDING and also
setup timer with keepalive timeout (5s) in order to disconnect
if no incoming message reset the timer.
We do that before queueing the send_immediate_work
and have that timer in case we didn't reach the send code
that typically sets the timer to keepalive timeout.

Cc: Steve French <smfrench@gmail.com>
Cc: Tom Talpey <tom@talpey.com>
Cc: Long Li <longli@microsoft.com>
Cc: linux-cifs@vger.kernel.org
Cc: samba-technical@lists.samba.org
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/smbdirect.c

index d53e39e91e1056968d7f14ddeb846e9b12aceb7f..5863dc7e021a5c1ae6e5b7e720da4bc4494e7076 100644 (file)
@@ -610,6 +610,14 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
                response->sge.length,
                DMA_FROM_DEVICE);
 
+       /*
+        * Reset timer to the keepalive interval in
+        * order to trigger our next keepalive message.
+        */
+       info->keep_alive_requested = KEEP_ALIVE_NONE;
+       mod_delayed_work(info->workqueue, &info->idle_timer_work,
+                        msecs_to_jiffies(sp->keepalive_interval_msec));
+
        switch (sc->recv_io.expected) {
        /* SMBD negotiation response */
        case SMBDIRECT_EXPECT_NEGOTIATE_REP:
@@ -684,11 +692,11 @@ static void recv_done(struct ib_cq *cq, struct ib_wc *wc)
                             le32_to_cpu(data_transfer->data_length),
                             le32_to_cpu(data_transfer->remaining_data_length));
 
-               /* Send a KEEP_ALIVE response right away if requested */
-               info->keep_alive_requested = KEEP_ALIVE_NONE;
+               /* Send an immediate response right away if requested */
                if (le16_to_cpu(data_transfer->flags) &
                                SMBDIRECT_FLAG_RESPONSE_REQUESTED) {
-                       info->keep_alive_requested = KEEP_ALIVE_PENDING;
+                       log_keep_alive(INFO, "schedule send of immediate response\n");
+                       queue_work(info->workqueue, &info->send_immediate_work);
                }
 
                /*
@@ -987,8 +995,17 @@ static int manage_credits_prior_sending(struct smbd_connection *info)
  */
 static int manage_keep_alive_before_sending(struct smbd_connection *info)
 {
+       struct smbdirect_socket *sc = &info->socket;
+       struct smbdirect_socket_parameters *sp = &sc->parameters;
+
        if (info->keep_alive_requested == KEEP_ALIVE_PENDING) {
                info->keep_alive_requested = KEEP_ALIVE_SENT;
+               /*
+                * Now use the keepalive timeout (instead of keepalive interval)
+                * in order to wait for a response
+                */
+               mod_delayed_work(info->workqueue, &info->idle_timer_work,
+                                msecs_to_jiffies(sp->keepalive_timeout_msec));
                return 1;
        }
        return 0;
@@ -999,7 +1016,6 @@ static int smbd_post_send(struct smbd_connection *info,
                struct smbdirect_send_io *request)
 {
        struct smbdirect_socket *sc = &info->socket;
-       struct smbdirect_socket_parameters *sp = &sc->parameters;
        struct ib_send_wr send_wr;
        int rc, i;
 
@@ -1028,10 +1044,7 @@ static int smbd_post_send(struct smbd_connection *info,
                log_rdma_send(ERR, "ib_post_send failed rc=%d\n", rc);
                smbd_disconnect_rdma_connection(info);
                rc = -EAGAIN;
-       } else
-               /* Reset timer for idle connection after packet is sent */
-               mod_delayed_work(info->workqueue, &info->idle_timer_work,
-                       msecs_to_jiffies(sp->keepalive_interval_msec));
+       }
 
        return rc;
 }
@@ -1486,12 +1499,18 @@ static void idle_connection_timer(struct work_struct *work)
                return;
        }
 
+       if (sc->status != SMBDIRECT_SOCKET_CONNECTED)
+               return;
+
+       /*
+        * Now use the keepalive timeout (instead of keepalive interval)
+        * in order to wait for a response
+        */
+       info->keep_alive_requested = KEEP_ALIVE_PENDING;
+       mod_delayed_work(info->workqueue, &info->idle_timer_work,
+                        msecs_to_jiffies(sp->keepalive_timeout_msec));
        log_keep_alive(INFO, "schedule send of empty idle message\n");
        queue_work(info->workqueue, &info->send_immediate_work);
-
-       /* Setup the next idle timeout work */
-       queue_delayed_work(info->workqueue, &info->idle_timer_work,
-                       msecs_to_jiffies(sp->keepalive_interval_msec));
 }
 
 /*
@@ -1881,8 +1900,13 @@ static struct smbd_connection *_smbd_get_connection(
 
        INIT_WORK(&info->send_immediate_work, send_immediate_empty_message);
        INIT_DELAYED_WORK(&info->idle_timer_work, idle_connection_timer);
-       queue_delayed_work(info->workqueue, &info->idle_timer_work,
-               msecs_to_jiffies(sp->keepalive_interval_msec));
+       /*
+        * start with the negotiate timeout and KEEP_ALIVE_PENDING
+        * so that the timer will cause a disconnect.
+        */
+       info->keep_alive_requested = KEEP_ALIVE_PENDING;
+       mod_delayed_work(info->workqueue, &info->idle_timer_work,
+                        msecs_to_jiffies(sp->negotiate_timeout_msec));
 
        INIT_WORK(&sc->recv_io.posted.refill_work, smbd_post_send_credits);