cifs: reconnect only the connection and not smb session where possible
authorShyam Prasad N <sprasad@microsoft.com>
Mon, 19 Jul 2021 14:14:46 +0000 (14:14 +0000)
committerSteve French <stfrench@microsoft.com>
Mon, 3 Jan 2022 02:38:46 +0000 (20:38 -0600)
With the new per-channel bitmask for reconnect, we have an option to
reconnect the tcp session associated with the channel without reconnecting
the smb session. i.e. if there are still channels to operate on, we can
continue to use the smb session and tcon.

However, there are cases where it makes sense to reconnect the smb session
even when there are active channels underneath. For example for
SMB session expiry.

With this patch, we'll have an option to do either, and use the correct
option for specific cases.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c
fs/cifs/smb2ops.c

index 76b4adc..23d76ae 100644 (file)
@@ -973,6 +973,8 @@ struct cifs_ses {
        ((1UL << (ses)->chan_count) - 1)
 #define CIFS_ALL_CHANS_NEED_RECONNECT(ses)     \
        ((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses))
        ((1UL << (ses)->chan_count) - 1)
 #define CIFS_ALL_CHANS_NEED_RECONNECT(ses)     \
        ((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses))
+#define CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses) \
+       ((ses)->chans_need_reconnect = CIFS_ALL_CHANNELS_SET(ses))
 #define CIFS_CHAN_NEEDS_RECONNECT(ses, index)  \
        test_bit((index), &(ses)->chans_need_reconnect)
 
 #define CIFS_CHAN_NEEDS_RECONNECT(ses, index)  \
        test_bit((index), &(ses)->chans_need_reconnect)
 
index 09356a9..e0dc147 100644 (file)
@@ -131,7 +131,8 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
                        struct smb_hdr *in_buf ,
                        struct smb_hdr *out_buf,
                        int *bytes_returned);
                        struct smb_hdr *in_buf ,
                        struct smb_hdr *out_buf,
                        int *bytes_returned);
-extern int cifs_reconnect(struct TCP_Server_Info *server);
+extern int cifs_reconnect(struct TCP_Server_Info *server,
+                         bool mark_smb_session);
 extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
 extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
 extern bool backup_cred(struct cifs_sb_info *);
 extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
 extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
 extern bool backup_cred(struct cifs_sb_info *);
index 8b3e5be..7b1d0d7 100644 (file)
@@ -1439,7 +1439,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 
        if (server->ops->is_session_expired &&
            server->ops->is_session_expired(buf)) {
 
        if (server->ops->is_session_expired &&
            server->ops->is_session_expired(buf)) {
-               cifs_reconnect(server);
+               cifs_reconnect(server, true);
                return -1;
        }
 
                return -1;
        }
 
index 9ee5856..7b478f5 100644 (file)
@@ -166,8 +166,11 @@ static void cifs_resolve_server(struct work_struct *work)
  * Mark all sessions and tcons for reconnect.
  *
  * @server needs to be previously set to CifsNeedReconnect.
  * Mark all sessions and tcons for reconnect.
  *
  * @server needs to be previously set to CifsNeedReconnect.
+ *
  */
  */
-static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server)
+static void
+cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
+                                     bool mark_smb_session)
 {
        unsigned int num_sessions = 0;
        struct cifs_ses *ses;
 {
        unsigned int num_sessions = 0;
        struct cifs_ses *ses;
@@ -193,13 +196,16 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
        spin_lock(&cifs_tcp_ses_lock);
        list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
                spin_lock(&ses->chan_lock);
        spin_lock(&cifs_tcp_ses_lock);
        list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
                spin_lock(&ses->chan_lock);
-               if (cifs_chan_needs_reconnect(ses, server))
+               if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))
                        goto next_session;
 
                        goto next_session;
 
-               cifs_chan_set_need_reconnect(ses, server);
+               if (mark_smb_session)
+                       CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses);
+               else
+                       cifs_chan_set_need_reconnect(ses, server);
 
                /* If all channels need reconnect, then tcon needs reconnect */
 
                /* If all channels need reconnect, then tcon needs reconnect */
-               if (!CIFS_ALL_CHANS_NEED_RECONNECT(ses))
+               if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses))
                        goto next_session;
 
                num_sessions++;
                        goto next_session;
 
                num_sessions++;
@@ -271,16 +277,16 @@ next_session:
 
 static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets)
 {
 
 static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets)
 {
-       spin_lock(&GlobalMid_Lock);
+       spin_lock(&cifs_tcp_ses_lock);
        server->nr_targets = num_targets;
        if (server->tcpStatus == CifsExiting) {
                /* the demux thread will exit normally next time through the loop */
        server->nr_targets = num_targets;
        if (server->tcpStatus == CifsExiting) {
                /* the demux thread will exit normally next time through the loop */
-               spin_unlock(&GlobalMid_Lock);
+               spin_unlock(&cifs_tcp_ses_lock);
                wake_up(&server->response_q);
                return false;
        }
        server->tcpStatus = CifsNeedReconnect;
                wake_up(&server->response_q);
                return false;
        }
        server->tcpStatus = CifsNeedReconnect;
-       spin_unlock(&GlobalMid_Lock);
+       spin_unlock(&cifs_tcp_ses_lock);
        return true;
 }
 
        return true;
 }
 
@@ -291,15 +297,21 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
  * mark all smb sessions as reconnecting for tcp session
  * reconnect tcp session
  * wake up waiters on reconnection? - (not needed currently)
  * mark all smb sessions as reconnecting for tcp session
  * reconnect tcp session
  * wake up waiters on reconnection? - (not needed currently)
+ *
+ * if mark_smb_session is passed as true, unconditionally mark
+ * the smb session (and tcon) for reconnect as well. This value
+ * doesn't really matter for non-multichannel scenario.
+ *
  */
  */
-static int __cifs_reconnect(struct TCP_Server_Info *server)
+static int __cifs_reconnect(struct TCP_Server_Info *server,
+                           bool mark_smb_session)
 {
        int rc = 0;
 
        if (!cifs_tcp_ses_needs_reconnect(server, 1))
                return 0;
 
 {
        int rc = 0;
 
        if (!cifs_tcp_ses_needs_reconnect(server, 1))
                return 0;
 
-       cifs_mark_tcp_ses_conns_for_reconnect(server);
+       cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
 
        do {
                try_to_freeze();
 
        do {
                try_to_freeze();
@@ -322,10 +334,10 @@ static int __cifs_reconnect(struct TCP_Server_Info *server)
                } else {
                        atomic_inc(&tcpSesReconnectCount);
                        set_credits(server, 1);
                } else {
                        atomic_inc(&tcpSesReconnectCount);
                        set_credits(server, 1);
-                       spin_lock(&GlobalMid_Lock);
+                       spin_lock(&cifs_tcp_ses_lock);
                        if (server->tcpStatus != CifsExiting)
                                server->tcpStatus = CifsNeedNegotiate;
                        if (server->tcpStatus != CifsExiting)
                                server->tcpStatus = CifsNeedNegotiate;
-                       spin_unlock(&GlobalMid_Lock);
+                       spin_unlock(&cifs_tcp_ses_lock);
                        cifs_swn_reset_server_dstaddr(server);
                        mutex_unlock(&server->srv_mutex);
                }
                        cifs_swn_reset_server_dstaddr(server);
                        mutex_unlock(&server->srv_mutex);
                }
@@ -394,7 +406,9 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_
        return rc;
 }
 
        return rc;
 }
 
-static int reconnect_dfs_server(struct TCP_Server_Info *server)
+static int
+reconnect_dfs_server(struct TCP_Server_Info *server,
+                    bool mark_smb_session)
 {
        int rc = 0;
        const char *refpath = server->current_fullpath + 1;
 {
        int rc = 0;
        const char *refpath = server->current_fullpath + 1;
@@ -418,7 +432,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
        if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
                return 0;
 
        if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
                return 0;
 
-       cifs_mark_tcp_ses_conns_for_reconnect(server);
+       cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
 
        do {
                try_to_freeze();
 
        do {
                try_to_freeze();
@@ -439,10 +453,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
                 */
                atomic_inc(&tcpSesReconnectCount);
                set_credits(server, 1);
                 */
                atomic_inc(&tcpSesReconnectCount);
                set_credits(server, 1);
-               spin_lock(&GlobalMid_Lock);
+               spin_lock(&cifs_tcp_ses_lock);
                if (server->tcpStatus != CifsExiting)
                        server->tcpStatus = CifsNeedNegotiate;
                if (server->tcpStatus != CifsExiting)
                        server->tcpStatus = CifsNeedNegotiate;
-               spin_unlock(&GlobalMid_Lock);
+               spin_unlock(&cifs_tcp_ses_lock);
                cifs_swn_reset_server_dstaddr(server);
                mutex_unlock(&server->srv_mutex);
        } while (server->tcpStatus == CifsNeedReconnect);
                cifs_swn_reset_server_dstaddr(server);
                mutex_unlock(&server->srv_mutex);
        } while (server->tcpStatus == CifsNeedReconnect);
@@ -460,22 +474,22 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
        return rc;
 }
 
        return rc;
 }
 
-int cifs_reconnect(struct TCP_Server_Info *server)
+int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
 {
        /* If tcp session is not an dfs connection, then reconnect to last target server */
        spin_lock(&cifs_tcp_ses_lock);
        if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) {
                spin_unlock(&cifs_tcp_ses_lock);
 {
        /* If tcp session is not an dfs connection, then reconnect to last target server */
        spin_lock(&cifs_tcp_ses_lock);
        if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) {
                spin_unlock(&cifs_tcp_ses_lock);
-               return __cifs_reconnect(server);
+               return __cifs_reconnect(server, mark_smb_session);
        }
        spin_unlock(&cifs_tcp_ses_lock);
 
        }
        spin_unlock(&cifs_tcp_ses_lock);
 
-       return reconnect_dfs_server(server);
+       return reconnect_dfs_server(server, mark_smb_session);
 }
 #else
 }
 #else
-int cifs_reconnect(struct TCP_Server_Info *server)
+int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
 {
 {
-       return __cifs_reconnect(server);
+       return __cifs_reconnect(server, mark_smb_session);
 }
 #endif
 
 }
 #endif
 
@@ -563,7 +577,7 @@ server_unresponsive(struct TCP_Server_Info *server)
            time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
                cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
                         (3 * server->echo_interval) / HZ);
            time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
                cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
                         (3 * server->echo_interval) / HZ);
-               cifs_reconnect(server);
+               cifs_reconnect(server, false);
                return true;
        }
 
                return true;
        }
 
@@ -599,7 +613,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
 
                /* reconnect if no credits and no requests in flight */
                if (zero_credits(server)) {
 
                /* reconnect if no credits and no requests in flight */
                if (zero_credits(server)) {
-                       cifs_reconnect(server);
+                       cifs_reconnect(server, false);
                        return -ECONNABORTED;
                }
 
                        return -ECONNABORTED;
                }
 
@@ -614,7 +628,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
                        return -ESHUTDOWN;
 
                if (server->tcpStatus == CifsNeedReconnect) {
                        return -ESHUTDOWN;
 
                if (server->tcpStatus == CifsNeedReconnect) {
-                       cifs_reconnect(server);
+                       cifs_reconnect(server, false);
                        return -ECONNABORTED;
                }
 
                        return -ECONNABORTED;
                }
 
@@ -633,7 +647,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
 
                if (length <= 0) {
                        cifs_dbg(FYI, "Received no data or error: %d\n", length);
 
                if (length <= 0) {
                        cifs_dbg(FYI, "Received no data or error: %d\n", length);
-                       cifs_reconnect(server);
+                       cifs_reconnect(server, false);
                        return -ECONNABORTED;
                }
        }
                        return -ECONNABORTED;
                }
        }
@@ -712,11 +726,11 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
                 * initialize frame).
                 */
                cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
                 * initialize frame).
                 */
                cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
-               cifs_reconnect(server);
+               cifs_reconnect(server, true);
                break;
        default:
                cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type);
                break;
        default:
                cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type);
-               cifs_reconnect(server);
+               cifs_reconnect(server, true);
        }
 
        return false;
        }
 
        return false;
@@ -889,7 +903,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
        if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
                server->vals->header_preamble_size) {
                cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
        if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
                server->vals->header_preamble_size) {
                cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
-               cifs_reconnect(server);
+               cifs_reconnect(server, true);
                return -ECONNABORTED;
        }
 
                return -ECONNABORTED;
        }
 
@@ -936,7 +950,7 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
 
        if (server->ops->is_session_expired &&
            server->ops->is_session_expired(buf)) {
 
        if (server->ops->is_session_expired &&
            server->ops->is_session_expired(buf)) {
-               cifs_reconnect(server);
+               cifs_reconnect(server, true);
                return -1;
        }
 
                return -1;
        }
 
@@ -1040,7 +1054,7 @@ next_pdu:
                    server->vals->header_preamble_size) {
                        cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
                                 server->pdu_size);
                    server->vals->header_preamble_size) {
                        cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
                                 server->pdu_size);
-                       cifs_reconnect(server);
+                       cifs_reconnect(server, true);
                        continue;
                }
 
                        continue;
                }
 
@@ -1092,7 +1106,7 @@ next_pdu:
                    server->ops->is_status_io_timeout(buf)) {
                        num_io_timeout++;
                        if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) {
                    server->ops->is_status_io_timeout(buf)) {
                        num_io_timeout++;
                        if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) {
-                               cifs_reconnect(server);
+                               cifs_reconnect(server, false);
                                num_io_timeout = 0;
                                continue;
                        }
                                num_io_timeout = 0;
                                continue;
                        }
index c97dd97..b33b0f3 100644 (file)
@@ -4810,7 +4810,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
        if (server->ops->is_session_expired &&
            server->ops->is_session_expired(buf)) {
                if (!is_offloaded)
        if (server->ops->is_session_expired &&
            server->ops->is_session_expired(buf)) {
                if (!is_offloaded)
-                       cifs_reconnect(server);
+                       cifs_reconnect(server, true);
                return -1;
        }
 
                return -1;
        }
 
@@ -5223,13 +5223,13 @@ smb3_receive_transform(struct TCP_Server_Info *server,
                                                sizeof(struct smb2_hdr)) {
                cifs_server_dbg(VFS, "Transform message is too small (%u)\n",
                         pdu_length);
                                                sizeof(struct smb2_hdr)) {
                cifs_server_dbg(VFS, "Transform message is too small (%u)\n",
                         pdu_length);
-               cifs_reconnect(server);
+               cifs_reconnect(server, true);
                return -ECONNABORTED;
        }
 
        if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) {
                cifs_server_dbg(VFS, "Transform message is broken\n");
                return -ECONNABORTED;
        }
 
        if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) {
                cifs_server_dbg(VFS, "Transform message is broken\n");
-               cifs_reconnect(server);
+               cifs_reconnect(server, true);
                return -ECONNABORTED;
        }
 
                return -ECONNABORTED;
        }