cifs: avoid parallel session setups on same channel
authorShyam Prasad N <sprasad@microsoft.com>
Fri, 8 Apr 2022 13:31:37 +0000 (13:31 +0000)
committerSteve French <stfrench@microsoft.com>
Tue, 24 May 2022 19:16:32 +0000 (14:16 -0500)
After allowing channels to reconnect in parallel, it now
becomes important to take care that multiple processes do not
call negotiate/session setup in parallel on the same channel.

This change avoids that by marking a channel as "in_reconnect".
During session setup if the channel in question has this flag
set, we return immediately.

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

index c098735d41c6ea27b8247cfdd30fcdd9f2fc1ede..1dd995efd5b8623b5f4eb79bd16bc9cd5abbc25c 100644 (file)
@@ -419,6 +419,8 @@ skip_rdma:
                        spin_lock(&ses->chan_lock);
                        if (CIFS_CHAN_NEEDS_RECONNECT(ses, 0))
                                seq_puts(m, "\tPrimary channel: DISCONNECTED ");
+                       if (CIFS_CHAN_IN_RECONNECT(ses, 0))
+                               seq_puts(m, "\t[RECONNECTING] ");
 
                        if (ses->chan_count > 1) {
                                seq_printf(m, "\n\n\tExtra Channels: %zu ",
@@ -427,6 +429,8 @@ skip_rdma:
                                        cifs_dump_channel(m, j, &ses->chans[j]);
                                        if (CIFS_CHAN_NEEDS_RECONNECT(ses, j))
                                                seq_puts(m, "\tDISCONNECTED ");
+                                       if (CIFS_CHAN_IN_RECONNECT(ses, j))
+                                               seq_puts(m, "\t[RECONNECTING] ");
                                }
                        }
                        spin_unlock(&ses->chan_lock);
index 711cf51ac14f9647a8edaed06cbf2609d885f3aa..8880a26f234f4d2c69c4e6379222f014ae6aec68 100644 (file)
@@ -922,6 +922,7 @@ struct cifs_server_iface {
 };
 
 struct cifs_chan {
+       unsigned int in_reconnect : 1; /* if session setup in progress for this channel */
        struct TCP_Server_Info *server;
        __u8 signkey[SMB3_SIGN_KEY_SIZE];
 };
@@ -984,12 +985,16 @@ struct cifs_ses {
 #define CIFS_MAX_CHANNELS 16
 #define CIFS_ALL_CHANNELS_SET(ses)     \
        ((1UL << (ses)->chan_count) - 1)
+#define CIFS_ALL_CHANS_GOOD(ses)               \
+       (!(ses)->chans_need_reconnect)
 #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_IN_RECONNECT(ses, index)     \
+       ((ses)->chans[(index)].in_reconnect)
 
        struct cifs_chan chans[CIFS_MAX_CHANNELS];
        size_t chan_count;
index 0df3b24a0bf4cc5c36b80596618fdc1e6f0cdad4..3b7366ec03c76230ff5c28e42c5e929424bca192 100644 (file)
@@ -619,6 +619,15 @@ unsigned int
 cifs_ses_get_chan_index(struct cifs_ses *ses,
                        struct TCP_Server_Info *server);
 void
+cifs_chan_set_in_reconnect(struct cifs_ses *ses,
+                            struct TCP_Server_Info *server);
+void
+cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
+                              struct TCP_Server_Info *server);
+bool
+cifs_chan_in_reconnect(struct cifs_ses *ses,
+                         struct TCP_Server_Info *server);
+void
 cifs_chan_set_need_reconnect(struct cifs_ses *ses,
                             struct TCP_Server_Info *server);
 void
index df4bcc581559ffed18bda44a8c3555471646b71d..199b076f7a6429b3071c41858bfc452f820a2221 100644 (file)
@@ -3995,17 +3995,27 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
        int rc = -ENOSYS;
        bool is_binding = false;
 
-       /* only send once per connect */
-       spin_lock(&ses->chan_lock);
-       is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
-       spin_unlock(&ses->chan_lock);
 
        spin_lock(&cifs_tcp_ses_lock);
-       if (ses->ses_status == SES_EXITING) {
+       if (ses->ses_status != SES_GOOD &&
+           ses->ses_status != SES_NEW &&
+           ses->ses_status != SES_NEED_RECON) {
                spin_unlock(&cifs_tcp_ses_lock);
                return 0;
        }
 
+       /* only send once per connect */
+       spin_lock(&ses->chan_lock);
+       if (CIFS_ALL_CHANS_GOOD(ses) ||
+           cifs_chan_in_reconnect(ses, server)) {
+               spin_unlock(&ses->chan_lock);
+               spin_unlock(&cifs_tcp_ses_lock);
+               return 0;
+       }
+       is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
+       cifs_chan_set_in_reconnect(ses, server);
+       spin_unlock(&ses->chan_lock);
+
        if (!is_binding)
                ses->ses_status = SES_IN_SETUP;
        spin_unlock(&cifs_tcp_ses_lock);
@@ -4035,16 +4045,19 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
                spin_lock(&cifs_tcp_ses_lock);
                if (ses->ses_status == SES_IN_SETUP)
                        ses->ses_status = SES_NEED_RECON;
+               spin_lock(&ses->chan_lock);
+               cifs_chan_clear_in_reconnect(ses, server);
+               spin_unlock(&ses->chan_lock);
                spin_unlock(&cifs_tcp_ses_lock);
        } else {
                spin_lock(&cifs_tcp_ses_lock);
                if (ses->ses_status == SES_IN_SETUP)
                        ses->ses_status = SES_GOOD;
-               spin_unlock(&cifs_tcp_ses_lock);
-
                spin_lock(&ses->chan_lock);
+               cifs_chan_clear_in_reconnect(ses, server);
                cifs_chan_clear_need_reconnect(ses, server);
                spin_unlock(&ses->chan_lock);
+               spin_unlock(&cifs_tcp_ses_lock);
        }
 
        return rc;
index 32f478c7a66d86242363ff664b4a632ab277bc52..7c453f8701eb0f543925f66a0755f65bf7ad1b5b 100644 (file)
@@ -85,6 +85,33 @@ cifs_ses_get_chan_index(struct cifs_ses *ses,
        return 0;
 }
 
+void
+cifs_chan_set_in_reconnect(struct cifs_ses *ses,
+                            struct TCP_Server_Info *server)
+{
+       unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+
+       ses->chans[chan_index].in_reconnect = true;
+}
+
+void
+cifs_chan_clear_in_reconnect(struct cifs_ses *ses,
+                            struct TCP_Server_Info *server)
+{
+       unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+
+       ses->chans[chan_index].in_reconnect = false;
+}
+
+bool
+cifs_chan_in_reconnect(struct cifs_ses *ses,
+                         struct TCP_Server_Info *server)
+{
+       unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+
+       return CIFS_CHAN_IN_RECONNECT(ses, chan_index);
+}
+
 void
 cifs_chan_set_need_reconnect(struct cifs_ses *ses,
                             struct TCP_Server_Info *server)