cifs: Add support for failover in cifs_reconnect_tcon()
authorPaulo Alcantara <palcantara@suse.de>
Wed, 14 Nov 2018 19:24:29 +0000 (17:24 -0200)
committerSteve French <stfrench@microsoft.com>
Fri, 28 Dec 2018 16:13:11 +0000 (10:13 -0600)
After a successful failover, the cifs_reconnect_tcon() function will
make sure to reconnect every tcon to new target server.

Same as previous commit but for SMB1 codepath.

Signed-off-by: Paulo Alcantara <palcantara@suse.de>
Reviewed-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifssmb.c

index f82fd34..b1f49c1 100644 (file)
@@ -44,6 +44,9 @@
 #include "cifs_debug.h"
 #include "fscache.h"
 #include "smbdirect.h"
+#ifdef CONFIG_CIFS_DFS_UPCALL
+#include "dfs_cache.h"
+#endif
 
 #ifdef CONFIG_CIFS_POSIX
 static struct {
@@ -118,6 +121,77 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
         */
 }
 
+#ifdef CONFIG_CIFS_DFS_UPCALL
+static int __cifs_reconnect_tcon(const struct nls_table *nlsc,
+                                struct cifs_tcon *tcon)
+{
+       int rc;
+       struct dfs_cache_tgt_list tl;
+       struct dfs_cache_tgt_iterator *it = NULL;
+       char tree[MAX_TREE_SIZE + 1];
+       const char *tcp_host;
+       size_t tcp_host_len;
+       const char *dfs_host;
+       size_t dfs_host_len;
+
+       if (tcon->ipc) {
+               snprintf(tree, sizeof(tree), "\\\\%s\\IPC$",
+                        tcon->ses->server->hostname);
+               return CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
+       }
+
+       if (!tcon->dfs_path)
+               return CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc);
+
+       rc = dfs_cache_noreq_find(tcon->dfs_path + 1, NULL, &tl);
+       if (rc)
+               return rc;
+
+       extract_unc_hostname(tcon->ses->server->hostname, &tcp_host,
+                            &tcp_host_len);
+
+       for (it = dfs_cache_get_tgt_iterator(&tl); it;
+            it = dfs_cache_get_next_tgt(&tl, it)) {
+               const char *tgt = dfs_cache_get_tgt_name(it);
+
+               extract_unc_hostname(tgt, &dfs_host, &dfs_host_len);
+
+               if (dfs_host_len != tcp_host_len
+                   || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
+                       cifs_dbg(FYI, "%s: skipping %.*s, doesn't match %.*s",
+                                __func__,
+                                (int)dfs_host_len, dfs_host,
+                                (int)tcp_host_len, tcp_host);
+                       continue;
+               }
+
+               snprintf(tree, sizeof(tree), "\\%s", tgt);
+
+               rc = CIFSTCon(0, tcon->ses, tree, tcon, nlsc);
+               if (!rc)
+                       break;
+               if (rc == -EREMOTE)
+                       break;
+       }
+
+       if (!rc) {
+               if (it)
+                       rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1,
+                                                           it);
+               else
+                       rc = -ENOENT;
+       }
+       dfs_cache_free_tgts(&tl);
+       return rc;
+}
+#else
+static inline int __cifs_reconnect_tcon(const struct nls_table *nlsc,
+                                       struct cifs_tcon *tcon)
+{
+       return CIFSTCon(0, tcon->ses, tcon->treeName, tcon, nlsc);
+}
+#endif
+
 /* reconnect the socket, tcon, and smb session if needed */
 static int
 cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
@@ -126,6 +200,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
        struct cifs_ses *ses;
        struct TCP_Server_Info *server;
        struct nls_table *nls_codepage;
+       int retries;
 
        /*
         * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for
@@ -152,9 +227,12 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
                }
        }
 
+       retries = server->nr_targets;
+
        /*
-        * Give demultiplex thread up to 10 seconds to reconnect, should be
-        * greater than cifs socket timeout which is 7 seconds
+        * Give demultiplex thread up to 10 seconds to each target available for
+        * reconnect -- should be greater than cifs socket timeout which is 7
+        * seconds.
         */
        while (server->tcpStatus == CifsNeedReconnect) {
                rc = wait_event_interruptible_timeout(server->response_q,
@@ -170,6 +248,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
                if (server->tcpStatus != CifsNeedReconnect)
                        break;
 
+               if (--retries)
+                       continue;
+
                /*
                 * on "soft" mounts we wait once. Hard mounts keep
                 * retrying until process is killed or server comes
@@ -179,6 +260,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
                        cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
                        return -EHOSTDOWN;
                }
+               retries = server->nr_targets;
        }
 
        if (!ses->need_reconnect && !tcon->need_reconnect)
@@ -214,7 +296,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
        }
 
        cifs_mark_open_files_invalid(tcon);
-       rc = CIFSTCon(0, ses, tcon->treeName, tcon, nls_codepage);
+       rc = __cifs_reconnect_tcon(nls_codepage, tcon);
        mutex_unlock(&ses->session_mutex);
        cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);