cifs: handle cases where a channel is closed
authorSteve French <stfrench@microsoft.com>
Fri, 26 Jan 2024 05:57:13 +0000 (23:57 -0600)
committerSteve French <stfrench@microsoft.com>
Sat, 27 Jan 2024 06:14:31 +0000 (00:14 -0600)
So far, SMB multichannel could only scale up, but not
scale down the number of channels. In this series of
patch, we now allow the client to deal with the case
of multichannel disabled on the server when the share
is mounted. With that change, we now need the ability
to scale down the channels.

This change allows the client to deal with cases of
missing channels more gracefully.

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
fs/cifs/smb2transport.c

index 5efe98b6543a23cfe5f8a132ce636ac98cd16d76..a197182646f30425058d0e1258a8080b85469fdb 100644 (file)
@@ -136,6 +136,11 @@ cifs_dump_channel(struct seq_file *m, int i, struct cifs_chan *chan)
 {
        struct TCP_Server_Info *server = chan->server;
 
+       if (!server) {
+               seq_printf(m, "\n\n\t\tChannel: %d DISABLED", i+1);
+               return;
+       }
+
        seq_printf(m, "\n\n\t\tChannel: %d ConnectionId: 0x%llx"
                   "\n\t\tNumber of credits: %d,%d,%d Dialect 0x%x"
                   "\n\t\tTCP status: %d Instance: %d"
index cc3dffddcc2ac924fa03892cf44d3f48639857f3..cf25929d1c33a4de1bef8a5a4962260309e0b71a 100644 (file)
@@ -1053,6 +1053,7 @@ struct cifs_ses {
        spinlock_t chan_lock;
        /* ========= begin: protected by chan_lock ======== */
 #define CIFS_MAX_CHANNELS 16
+#define CIFS_INVAL_CHAN_INDEX (-1)
 #define CIFS_ALL_CHANNELS_SET(ses)     \
        ((1UL << (ses)->chan_count) - 1)
 #define CIFS_ALL_CHANS_GOOD(ses)               \
index 2a7321a991bfdccf0d6ac40c1cce028cfa537fe0..2b00d0c982f48eddd877cb9d61a6b30f12748a60 100644 (file)
@@ -618,7 +618,7 @@ bool is_server_using_iface(struct TCP_Server_Info *server,
 bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
 void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
 
-unsigned int
+int
 cifs_ses_get_chan_index(struct cifs_ses *ses,
                        struct TCP_Server_Info *server);
 void
index 37f586107db0c6f2c493af34324a4af479d5c9c0..3b80a6002d06cf67984543df8e9f427d9d28c7a8 100644 (file)
@@ -173,8 +173,12 @@ cifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
        list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
                spin_lock(&ses->chan_lock);
                for (i = 0; i < ses->chan_count; i++) {
+                       if (!ses->chans[i].server)
+                               continue;
+
                        spin_lock(&ses->chans[i].server->srv_lock);
-                       ses->chans[i].server->tcpStatus = CifsNeedReconnect;
+                       if (ses->chans[i].server->tcpStatus != CifsExiting)
+                               ses->chans[i].server->tcpStatus = CifsNeedReconnect;
                        spin_unlock(&ses->chans[i].server->srv_lock);
                }
                spin_unlock(&ses->chan_lock);
index 829c7688a7b72df1fd21e7ea75f8041491a486b1..d13a2461371051c9b9e6152a5bb232287dbd94c7 100644 (file)
@@ -69,7 +69,7 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
 
 /* channel helper functions. assumed that chan_lock is held by caller. */
 
-unsigned int
+int
 cifs_ses_get_chan_index(struct cifs_ses *ses,
                        struct TCP_Server_Info *server)
 {
@@ -85,14 +85,17 @@ cifs_ses_get_chan_index(struct cifs_ses *ses,
                cifs_dbg(VFS, "unable to get chan index for server: 0x%llx",
                         server->conn_id);
        WARN_ON(1);
-       return 0;
+       return CIFS_INVAL_CHAN_INDEX;
 }
 
 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);
+       int chan_index = cifs_ses_get_chan_index(ses, server);
+
+       if (chan_index == CIFS_INVAL_CHAN_INDEX)
+               return;
 
        ses->chans[chan_index].in_reconnect = true;
 }
@@ -102,6 +105,8 @@ 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);
+       if (chan_index == CIFS_INVAL_CHAN_INDEX)
+               return;
 
        ses->chans[chan_index].in_reconnect = false;
 }
@@ -111,6 +116,8 @@ cifs_chan_in_reconnect(struct cifs_ses *ses,
                          struct TCP_Server_Info *server)
 {
        unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+       if (chan_index == CIFS_INVAL_CHAN_INDEX)
+               return true;    /* err on the safer side */
 
        return CIFS_CHAN_IN_RECONNECT(ses, chan_index);
 }
@@ -120,6 +127,8 @@ cifs_chan_set_need_reconnect(struct cifs_ses *ses,
                             struct TCP_Server_Info *server)
 {
        unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+       if (chan_index == CIFS_INVAL_CHAN_INDEX)
+               return;
 
        set_bit(chan_index, &ses->chans_need_reconnect);
        cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
@@ -131,6 +140,8 @@ cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
                               struct TCP_Server_Info *server)
 {
        unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+       if (chan_index == CIFS_INVAL_CHAN_INDEX)
+               return;
 
        clear_bit(chan_index, &ses->chans_need_reconnect);
        cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
@@ -142,6 +153,8 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses,
                          struct TCP_Server_Info *server)
 {
        unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+       if (chan_index == CIFS_INVAL_CHAN_INDEX)
+               return true;    /* err on the safer side */
 
        return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
 }
@@ -151,6 +164,8 @@ cifs_chan_is_iface_active(struct cifs_ses *ses,
                          struct TCP_Server_Info *server)
 {
        unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+       if (chan_index == CIFS_INVAL_CHAN_INDEX)
+               return true;    /* err on the safer side */
 
        return ses->chans[chan_index].iface &&
                ses->chans[chan_index].iface->is_active;
@@ -269,7 +284,7 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
 
        spin_lock(&ses->chan_lock);
        chan_index = cifs_ses_get_chan_index(ses, server);
-       if (!chan_index) {
+       if (chan_index == CIFS_INVAL_CHAN_INDEX) {
                spin_unlock(&ses->chan_lock);
                return 0;
        }
@@ -319,6 +334,11 @@ cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
 
        spin_lock(&ses->chan_lock);
        chan_index = cifs_ses_get_chan_index(ses, server);
+       if (chan_index == CIFS_INVAL_CHAN_INDEX) {
+               spin_unlock(&ses->chan_lock);
+               return 0;
+       }
+
        ses->chans[chan_index].iface = iface;
 
        /* No iface is found. if secondary chan, drop connection */
index 23c50ed7d4b590d9802ba15c9f9b2d263ac86753..84ea67301303c45c805fafc9bb2f5648655ec728 100644 (file)
@@ -413,7 +413,13 @@ generate_smb3signingkey(struct cifs_ses *ses,
                      ses->ses_status == SES_GOOD);
 
        chan_index = cifs_ses_get_chan_index(ses, server);
-       /* TODO: introduce ref counting for channels when the can be freed */
+       if (chan_index == CIFS_INVAL_CHAN_INDEX) {
+               spin_unlock(&ses->chan_lock);
+               spin_unlock(&ses->ses_lock);
+
+               return -EINVAL;
+       }
+
        spin_unlock(&ses->chan_lock);
        spin_unlock(&ses->ses_lock);