cifs: during reconnect, update interface if necessary
authorShyam Prasad N <sprasad@microsoft.com>
Mon, 3 Jan 2022 08:47:30 +0000 (08:47 +0000)
committerSteve French <stfrench@microsoft.com>
Thu, 23 Jun 2022 00:51:43 +0000 (19:51 -0500)
Going forward, the plan is to periodically query the server
for it's interfaces (when multichannel is enabled).

This change allows checking for inactive interfaces during
reconnect, and reconnect to a new interface if necessary.

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

index 3b7366e..00c4a8a 100644 (file)
@@ -636,6 +636,11 @@ cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
 bool
 cifs_chan_needs_reconnect(struct cifs_ses *ses,
                          struct TCP_Server_Info *server);
+bool
+cifs_chan_is_iface_active(struct cifs_ses *ses,
+                         struct TCP_Server_Info *server);
+int
+cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server);
 
 void extract_unc_hostname(const char *unc, const char **h, size_t *len);
 int copy_path_name(char *dst, const char *src);
index 248fe18..d8aae25 100644 (file)
@@ -232,6 +232,10 @@ 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) {
+               /* check if iface is still active */
+               if (!cifs_chan_is_iface_active(ses, server))
+                       cifs_chan_update_iface(ses, server);
+
                spin_lock(&ses->chan_lock);
                if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))
                        goto next_session;
index d7b6e56..7c26ee7 100644 (file)
@@ -146,6 +146,16 @@ cifs_chan_needs_reconnect(struct cifs_ses *ses,
        return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
 }
 
+bool
+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);
+
+       return ses->chans[chan_index].iface &&
+               ses->chans[chan_index].iface->is_active;
+}
+
 /* returns number of channels added */
 int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
 {
@@ -245,6 +255,75 @@ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
 }
 
 /*
+ * update the iface for the channel if necessary.
+ * will return 0 when iface is updated. 1 otherwise
+ * Must be called with chan_lock held.
+ */
+int
+cifs_chan_update_iface(struct cifs_ses *ses, struct TCP_Server_Info *server)
+{
+       unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
+       struct cifs_server_iface *iface = NULL;
+       struct cifs_server_iface *old_iface = NULL;
+       int rc = 0;
+
+       /* primary channel. This can never go away */
+       if (!chan_index)
+               return 0;
+
+       if (ses->chans[chan_index].iface) {
+               old_iface = ses->chans[chan_index].iface;
+               if (old_iface->is_active)
+                       return 1;
+       }
+
+       spin_lock(&ses->iface_lock);
+
+       /* then look for a new one */
+       list_for_each_entry(iface, &ses->iface_list, iface_head) {
+               if (!iface->is_active ||
+                   (is_ses_using_iface(ses, iface) &&
+                    !iface->rss_capable)) {
+                       continue;
+               }
+               kref_get(&iface->refcount);
+       }
+
+       if (!list_entry_is_head(iface, &ses->iface_list, iface_head)) {
+               rc = 1;
+               iface = NULL;
+               cifs_dbg(FYI, "unable to find a suitable iface\n");
+       }
+
+       ses->chans[chan_index].iface = iface;
+
+       /* now drop the ref to the current iface */
+       if (old_iface && iface) {
+               kref_put(&old_iface->refcount, release_iface);
+               cifs_dbg(FYI, "replacing iface: %pIS with %pIS\n",
+                        &old_iface->sockaddr,
+                        &iface->sockaddr);
+       } else if (old_iface) {
+               kref_put(&old_iface->refcount, release_iface);
+               cifs_dbg(FYI, "releasing ref to iface: %pIS\n",
+                        &old_iface->sockaddr);
+       } else {
+               WARN_ON(!iface);
+               cifs_dbg(FYI, "adding new iface: %pIS\n", &iface->sockaddr);
+       }
+
+       spin_unlock(&ses->iface_lock);
+
+       /* No iface is found. if secondary chan, drop connection */
+       if (!iface && CIFS_SERVER_IS_CHAN(server)) {
+               cifs_put_tcp_session(server, false);
+               ses->chans[chan_index].server = NULL;
+       }
+
+       return rc;
+}
+
+/*
  * If server is a channel of ses, return the corresponding enclosing
  * cifs_chan otherwise return NULL.
  */