forked from Minki/linux
cifs: Add support for failover in smb2_reconnect()
After a successful failover in cifs_reconnect(), the smb2_reconnect() function will make sure to reconnect every tcon to new target server. For SMB2+. Signed-off-by: Paulo Alcantara <palcantara@suse.de> Signed-off-by: Aurelien Aptel <aaptel@suse.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
2332440714
commit
a3a53b7603
@ -565,6 +565,8 @@ void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
|
||||
extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
|
||||
unsigned int *len, unsigned int *offset);
|
||||
|
||||
void extract_unc_hostname(const char *unc, const char **h, size_t *len);
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
|
||||
const char *old_path,
|
||||
|
@ -952,3 +952,20 @@ void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
|
||||
else if (page == 0)
|
||||
*len = rqst->rq_pagesz - rqst->rq_offset;
|
||||
}
|
||||
|
||||
void extract_unc_hostname(const char *unc, const char **h, size_t *len)
|
||||
{
|
||||
const char *end;
|
||||
|
||||
/* skip initial slashes */
|
||||
while (*unc && (*unc == '\\' || *unc == '/'))
|
||||
unc++;
|
||||
|
||||
end = unc;
|
||||
|
||||
while (*end && !(*end == '\\' || *end == '/'))
|
||||
end++;
|
||||
|
||||
*h = unc;
|
||||
*len = end - unc;
|
||||
}
|
||||
|
@ -50,6 +50,9 @@
|
||||
#include "cifs_spnego.h"
|
||||
#include "smbdirect.h"
|
||||
#include "trace.h"
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
#include "dfs_cache.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The following table defines the expected "StructureSize" of SMB2 requests
|
||||
@ -152,6 +155,77 @@ out:
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
static int __smb2_reconnect(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 SMB2_tcon(0, tcon->ses, tree, tcon, nlsc);
|
||||
}
|
||||
|
||||
if (!tcon->dfs_path)
|
||||
return SMB2_tcon(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 = SMB2_tcon(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 __smb2_reconnect(const struct nls_table *nlsc,
|
||||
struct cifs_tcon *tcon)
|
||||
{
|
||||
return SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nlsc);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
||||
{
|
||||
@ -159,6 +233,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
||||
struct nls_table *nls_codepage;
|
||||
struct cifs_ses *ses;
|
||||
struct TCP_Server_Info *server;
|
||||
int retries;
|
||||
|
||||
/*
|
||||
* SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
|
||||
@ -192,9 +267,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
||||
ses = tcon->ses;
|
||||
server = ses->server;
|
||||
|
||||
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) {
|
||||
/*
|
||||
@ -225,6 +303,9 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
||||
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
|
||||
@ -234,6 +315,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
||||
cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n");
|
||||
return -EHOSTDOWN;
|
||||
}
|
||||
retries = server->nr_targets;
|
||||
}
|
||||
|
||||
if (!tcon->ses->need_reconnect && !tcon->need_reconnect)
|
||||
@ -271,7 +353,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon)
|
||||
if (tcon->use_persistent)
|
||||
tcon->need_reopen_files = true;
|
||||
|
||||
rc = SMB2_tcon(0, tcon->ses, tcon->treeName, tcon, nls_codepage);
|
||||
rc = __smb2_reconnect(nls_codepage, tcon);
|
||||
mutex_unlock(&tcon->ses->session_mutex);
|
||||
|
||||
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
|
||||
|
Loading…
Reference in New Issue
Block a user