Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a3a53b76 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

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: default avatarPaulo Alcantara <palcantara@suse.de>
Signed-off-by: default avatarAurelien Aptel <aaptel@suse.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 23324407
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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,
+17 −0
Original line number Diff line number Diff line
@@ -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;
}
+85 −3
Original line number Diff line number Diff line
@@ -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 @@ smb2_hdr_assemble(struct smb2_sync_hdr *shdr, __le16 smb2_cmd,
	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);