cifs: serialize other channels when query server interfaces is pending
authorShyam Prasad N <sprasad@microsoft.com>
Mon, 2 Jun 2025 17:07:15 +0000 (22:37 +0530)
committerSteve French <stfrench@microsoft.com>
Tue, 3 Jun 2025 23:42:47 +0000 (18:42 -0500)
Today, during smb2_reconnect, session_mutex is released as soon as
the tcon is reconnected and is in a good state. However, in case
multichannel is enabled, there is also a query of server interfaces that
follows. We've seen that this query can race with reconnects of other
channels, causing them to step on each other with reconnects.

This change extends the hold of session_mutex till after the query of
server interfaces is complete. In order to avoid recursive smb2_reconnect
checks during query ioctl, this change also introduces a session flag
for sessions where such a query is in progress.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Cc: stable@vger.kernel.org
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsglob.h
fs/smb/client/smb2pdu.c

index ad7dd16db3e91ebe6ee9cab78fbd7ffc86ceb20e..45e94e18f4d59a7cefad33b890865b0cd2c17b46 100644 (file)
@@ -1085,6 +1085,7 @@ struct cifs_chan {
 };
 
 #define CIFS_SES_FLAG_SCALE_CHANNELS (0x1)
+#define CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES (0x2)
 
 /*
  * Session structure.  One of these for each uid session with a particular host
index 2bd814cd612bff5edbda97a5304e2f2cca29ce33..96b32d2760719721da9b5ee611fb42b525fb1458 100644 (file)
@@ -412,14 +412,19 @@ skip_sess_setup:
        if (!rc &&
            (server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL) &&
            server->ops->query_server_interfaces) {
-               mutex_unlock(&ses->session_mutex);
-
                /*
-                * query server network interfaces, in case they change
+                * query server network interfaces, in case they change.
+                * Also mark the session as pending this update while the query
+                * is in progress. This will be used to avoid calling
+                * smb2_reconnect recursively.
                 */
+               ses->flags |= CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES;
                xid = get_xid();
                rc = server->ops->query_server_interfaces(xid, tcon, false);
                free_xid(xid);
+               ses->flags &= ~CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES;
+
+               mutex_unlock(&ses->session_mutex);
 
                if (rc == -EOPNOTSUPP && ses->chan_count > 1) {
                        /*
@@ -561,11 +566,18 @@ static int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon,
                               struct TCP_Server_Info *server,
                               void **request_buf, unsigned int *total_len)
 {
-       /* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */
-       if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) {
+       /*
+        * Skip reconnect in one of the following cases:
+        * 1. For FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs
+        * 2. For FSCTL_QUERY_NETWORK_INTERFACE_INFO IOCTL when called from
+        * smb2_reconnect (indicated by CIFS_SES_FLAG_SCALE_CHANNELS ses flag)
+        */
+       if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO ||
+           (opcode == FSCTL_QUERY_NETWORK_INTERFACE_INFO &&
+            (tcon->ses->flags & CIFS_SES_FLAGS_PENDING_QUERY_INTERFACES)))
                return __smb2_plain_req_init(SMB2_IOCTL, tcon, server,
                                             request_buf, total_len);
-       }
+
        return smb2_plain_req_init(SMB2_IOCTL, tcon, server,
                                   request_buf, total_len);
 }