Linux-CIFS Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH v4 0/6] multichannel
@ 2019-09-20  5:01 Aurelien Aptel
  2019-09-20  5:01 ` [PATCH v4 1/6] cifs: add multichannel mount options and data structs Aurelien Aptel
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Aurelien Aptel @ 2019-09-20  5:01 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, Aurelien Aptel

This patchset adds multichannel support. It is a cleaned up version of
v3. I've removed debug messages and split the patch in multiple commits.

It still needs more reconnection testing and some tweaks (use 3.0
instead of 3.1.1 as minimum requirement).

Long Li sucessfully tried the patchset with RDMA (opening RDMA
channels).

Aurelien Aptel (6):
  cifs: add multichannel mount options and data structs
  cifs: add server param
  cifs: switch servers depending on binding state
  cifs: sort interface list by speed
  cifs: try opening channels after mounting
  cifs: mention if an interface has a channel connected to it

 fs/cifs/cifs_debug.c    |   6 +-
 fs/cifs/cifs_spnego.c   |   2 +-
 fs/cifs/cifsfs.c        |   3 +
 fs/cifs/cifsglob.h      |  29 ++++++-
 fs/cifs/cifsproto.h     |   8 ++
 fs/cifs/connect.c       |  83 +++++++++++++++----
 fs/cifs/sess.c          | 216 +++++++++++++++++++++++++++++++++++++++++++++++-
 fs/cifs/smb2misc.c      |  37 ++++++---
 fs/cifs/smb2ops.c       |  13 ++-
 fs/cifs/smb2pdu.c       | 106 ++++++++++++++----------
 fs/cifs/smb2proto.h     |   3 +-
 fs/cifs/smb2transport.c | 150 ++++++++++++++++++++++++---------
 fs/cifs/transport.c     |  14 +++-
 13 files changed, 551 insertions(+), 119 deletions(-)

-- 
2.16.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 1/6] cifs: add multichannel mount options and data structs
  2019-09-20  5:01 [PATCH v4 0/6] multichannel Aurelien Aptel
@ 2019-09-20  5:01 ` Aurelien Aptel
  2019-09-20  5:01 ` [PATCH v4 2/6] cifs: add server param Aurelien Aptel
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Aurelien Aptel @ 2019-09-20  5:01 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, Aurelien Aptel

adds:
- [no]multichannel to enable/disable multichannel
- max_channels=N to control how many channels to create

these options are then stored in the volume struct.

- store channels and max_channels in cifs_ses

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
---
 fs/cifs/cifsfs.c   |  3 +++
 fs/cifs/cifsglob.h | 14 ++++++++++++++
 fs/cifs/connect.c  | 37 +++++++++++++++++++++++++++++++++++--
 3 files changed, 52 insertions(+), 2 deletions(-)

diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 69601a9b29ad..fb4d2152b1d4 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -577,6 +577,9 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
 	/* convert actimeo and display it in seconds */
 	seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);
 
+	if (tcon->ses->chan_max > 1)
+		seq_printf(s, ",multichannel,max_channel=%zu", tcon->ses->chan_max);
+
 	return 0;
 }
 
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 54e204589cb9..2418a1948321 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -590,6 +590,9 @@ struct smb_vol {
 	bool resilient:1; /* noresilient not required since not fored for CA */
 	bool domainauto:1;
 	bool rdma:1;
+	bool multichannel:1;
+	bool use_client_guid:1;
+	u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* reuse existing guid from master channel */
 	unsigned int bsize;
 	unsigned int rsize;
 	unsigned int wsize;
@@ -606,6 +609,7 @@ struct smb_vol {
 	__u64 snapshot_time; /* needed for timewarp tokens */
 	__u32 handle_timeout; /* persistent and durable handle timeout in ms */
 	unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
+	unsigned int max_channels;
 	__u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
 	bool rootfs:1; /* if it's a SMB root file system */
 };
@@ -952,6 +956,11 @@ struct cifs_server_iface {
 	struct sockaddr_storage sockaddr;
 };
 
+struct cifs_chan {
+	struct TCP_Server_Info *server;
+	__u8 signkey[SMB3_SIGN_KEY_SIZE];
+};
+
 /*
  * Session structure.  One of these for each uid session with a particular host
  */
@@ -1001,6 +1010,11 @@ struct cifs_ses {
 	struct cifs_server_iface *iface_list;
 	size_t iface_count;
 	unsigned long iface_last_update; /* jiffies */
+
+#define CIFS_MAX_CHANNELS 16
+	struct cifs_chan chans[CIFS_MAX_CHANNELS];
+	size_t chan_count;
+	size_t chan_max;
 };
 
 static inline bool
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 2850c3ce4391..fb95fd90b578 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -97,6 +97,7 @@ enum {
 	Opt_persistent, Opt_nopersistent,
 	Opt_resilient, Opt_noresilient,
 	Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs,
+	Opt_multichannel, Opt_nomultichannel,
 	Opt_compress,
 
 	/* Mount options which take numeric value */
@@ -106,7 +107,7 @@ enum {
 	Opt_min_enc_offload,
 	Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo,
 	Opt_echo_interval, Opt_max_credits, Opt_handletimeout,
-	Opt_snapshot,
+	Opt_snapshot, Opt_max_channels,
 
 	/* Mount options which take string value */
 	Opt_user, Opt_pass, Opt_ip,
@@ -199,6 +200,8 @@ static const match_table_t cifs_mount_option_tokens = {
 	{ Opt_noresilient, "noresilienthandles"},
 	{ Opt_domainauto, "domainauto"},
 	{ Opt_rdma, "rdma"},
+	{ Opt_multichannel, "multichannel" },
+	{ Opt_nomultichannel, "nomultichannel" },
 
 	{ Opt_backupuid, "backupuid=%s" },
 	{ Opt_backupgid, "backupgid=%s" },
@@ -218,6 +221,7 @@ static const match_table_t cifs_mount_option_tokens = {
 	{ Opt_echo_interval, "echo_interval=%s" },
 	{ Opt_max_credits, "max_credits=%s" },
 	{ Opt_snapshot, "snapshot=%s" },
+	{ Opt_max_channels, "max_channels=%s" },
 	{ Opt_compress, "compress=%s" },
 
 	{ Opt_blank_user, "user=" },
@@ -1664,6 +1668,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 
 	vol->echo_interval = SMB_ECHO_INTERVAL_DEFAULT;
 
+	/* default to no multichannel (single server connection) */
+	vol->multichannel = false;
+	vol->max_channels = 1;
+
 	if (!mountdata)
 		goto cifs_parse_mount_err;
 
@@ -1957,6 +1965,12 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 		case Opt_rdma:
 			vol->rdma = true;
 			break;
+		case Opt_multichannel:
+			vol->multichannel = true;
+			break;
+		case Opt_nomultichannel:
+			vol->multichannel = false;
+			break;
 		case Opt_compress:
 			vol->compression = UNKNOWN_TYPE;
 			cifs_dbg(VFS,
@@ -2120,6 +2134,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
 			}
 			vol->max_credits = option;
 			break;
+		case Opt_max_channels:
+			if (get_option_ul(args, &option) || option < 1 ||
+				option > CIFS_MAX_CHANNELS) {
+				cifs_dbg(VFS, "%s: Invalid max_channels value, needs to be 1-%d\n",
+					 __func__, CIFS_MAX_CHANNELS);
+				goto cifs_parse_mount_err;
+			}
+			vol->max_channels = option;
+			break;
 
 		/* String Arguments */
 
@@ -2764,7 +2787,10 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
 	       sizeof(tcp_ses->srcaddr));
 	memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
 		sizeof(tcp_ses->dstaddr));
-	generate_random_uuid(tcp_ses->client_guid);
+	if (volume_info->use_client_guid)
+		memcpy(tcp_ses->client_guid, volume_info->client_guid, SMB2_CLIENT_GUID_SIZE);
+	else
+		generate_random_uuid(tcp_ses->client_guid);
 	/*
 	 * at this point we are the only ones with the pointer
 	 * to the struct since the kernel thread not created yet
@@ -2853,6 +2879,13 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol)
 	    vol->sectype != ses->sectype)
 		return 0;
 
+	/*
+	 * If an existing session is limited to less channels than
+	 * requested, it should not be reused
+	 */
+	if (ses->chan_max < vol->max_channels)
+		return 0;
+
 	switch (ses->sectype) {
 	case Kerberos:
 		if (!uid_eq(vol->cred_uid, ses->cred_uid))
-- 
2.16.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 2/6] cifs: add server param
  2019-09-20  5:01 [PATCH v4 0/6] multichannel Aurelien Aptel
  2019-09-20  5:01 ` [PATCH v4 1/6] cifs: add multichannel mount options and data structs Aurelien Aptel
@ 2019-09-20  5:01 ` Aurelien Aptel
  2019-09-20  5:01 ` [PATCH v4 3/6] cifs: switch servers depending on binding state Aurelien Aptel
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Aurelien Aptel @ 2019-09-20  5:01 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, Aurelien Aptel

pass server pointer instead of using ses-server

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
---
 fs/cifs/cifsglob.h      |  3 ++-
 fs/cifs/cifsproto.h     |  1 +
 fs/cifs/smb2proto.h     |  3 ++-
 fs/cifs/smb2transport.c | 24 ++++++++++++------------
 fs/cifs/transport.c     |  4 ++--
 5 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 2418a1948321..e980e7b0d79d 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -230,7 +230,8 @@ struct smb_version_operations {
 	bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
 	/* setup request: allocate mid, sign message */
 	struct mid_q_entry *(*setup_request)(struct cifs_ses *,
-						struct smb_rqst *);
+					     struct TCP_Server_Info *,
+					     struct smb_rqst *);
 	/* setup async request: allocate mid, sign message */
 	struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *,
 						struct smb_rqst *);
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 99b1b1ef558c..ee3e6d6556a7 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -109,6 +109,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
 extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
 			    char *in_buf, int flags);
 extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *,
+				struct TCP_Server_Info *,
 				struct smb_rqst *);
 extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
 						struct smb_rqst *);
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 67a91b11fd59..804b6dc5546b 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -46,7 +46,8 @@ extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *);
 extern int smb2_check_receive(struct mid_q_entry *mid,
 			      struct TCP_Server_Info *server, bool log_error);
 extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
-			      struct smb_rqst *rqst);
+					      struct TCP_Server_Info *,
+					      struct smb_rqst *rqst);
 extern struct mid_q_entry *smb2_setup_async_request(
 			struct TCP_Server_Info *server, struct smb_rqst *rqst);
 extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index 148d7942c796..d7d89741f3f0 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -610,18 +610,18 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
 }
 
 static int
-smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
+smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server, struct smb2_sync_hdr *shdr,
 		   struct mid_q_entry **mid)
 {
-	if (ses->server->tcpStatus == CifsExiting)
+	if (server->tcpStatus == CifsExiting)
 		return -ENOENT;
 
-	if (ses->server->tcpStatus == CifsNeedReconnect) {
+	if (server->tcpStatus == CifsNeedReconnect) {
 		cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
 		return -EAGAIN;
 	}
 
-	if (ses->server->tcpStatus == CifsNeedNegotiate &&
+	if (server->tcpStatus == CifsNeedNegotiate &&
 	   shdr->Command != SMB2_NEGOTIATE)
 		return -EAGAIN;
 
@@ -638,11 +638,11 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
 		/* else ok - we are shutting down the session */
 	}
 
-	*mid = smb2_mid_entry_alloc(shdr, ses->server);
+	*mid = smb2_mid_entry_alloc(shdr, server);
 	if (*mid == NULL)
 		return -ENOMEM;
 	spin_lock(&GlobalMid_Lock);
-	list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q);
+	list_add_tail(&(*mid)->qhead, &server->pending_mid_q);
 	spin_unlock(&GlobalMid_Lock);
 
 	return 0;
@@ -675,24 +675,24 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
 }
 
 struct mid_q_entry *
-smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
+smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, struct smb_rqst *rqst)
 {
 	int rc;
 	struct smb2_sync_hdr *shdr =
 			(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
 	struct mid_q_entry *mid;
 
-	smb2_seq_num_into_buf(ses->server, shdr);
+	smb2_seq_num_into_buf(server, shdr);
 
-	rc = smb2_get_mid_entry(ses, shdr, &mid);
+	rc = smb2_get_mid_entry(ses, server, shdr, &mid);
 	if (rc) {
-		revert_current_mid_from_hdr(ses->server, shdr);
+		revert_current_mid_from_hdr(server, shdr);
 		return ERR_PTR(rc);
 	}
 
-	rc = smb2_sign_rqst(rqst, ses->server);
+	rc = smb2_sign_rqst(rqst, server);
 	if (rc) {
-		revert_current_mid_from_hdr(ses->server, shdr);
+		revert_current_mid_from_hdr(server, shdr);
 		cifs_delete_mid(mid);
 		return ERR_PTR(rc);
 	}
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index 308ad0f495e1..afe66f9cb15e 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -923,7 +923,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
 }
 
 struct mid_q_entry *
-cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
+cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored, struct smb_rqst *rqst)
 {
 	int rc;
 	struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
@@ -1040,7 +1040,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
 	}
 
 	for (i = 0; i < num_rqst; i++) {
-		midQ[i] = server->ops->setup_request(ses, &rqst[i]);
+		midQ[i] = server->ops->setup_request(ses, server, &rqst[i]);
 		if (IS_ERR(midQ[i])) {
 			revert_current_mid(server, i);
 			for (j = 0; j < i; j++)
-- 
2.16.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 3/6] cifs: switch servers depending on binding state
  2019-09-20  5:01 [PATCH v4 0/6] multichannel Aurelien Aptel
  2019-09-20  5:01 ` [PATCH v4 1/6] cifs: add multichannel mount options and data structs Aurelien Aptel
  2019-09-20  5:01 ` [PATCH v4 2/6] cifs: add server param Aurelien Aptel
@ 2019-09-20  5:01 ` Aurelien Aptel
  2019-09-20  5:01 ` [PATCH v4 4/6] cifs: sort interface list by speed Aurelien Aptel
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Aurelien Aptel @ 2019-09-20  5:01 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, Aurelien Aptel

Currently a lot of the code to initialize a connection & session uses
the cifs_ses as input. But depending on if we are opening a new session
or a new channel we need to use different server pointers.

Add a "binding" flag in cifs_ses a helper function that returns the
current server a session should use (only in the sess establishment
code path)

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
---
 fs/cifs/cifs_spnego.c |  2 +-
 fs/cifs/cifsglob.h    | 10 ++++++++++
 fs/cifs/connect.c     |  4 ++--
 fs/cifs/sess.c        |  5 +++--
 fs/cifs/smb2ops.c     |  2 +-
 fs/cifs/smb2pdu.c     | 31 ++++++++++++++++---------------
 6 files changed, 33 insertions(+), 21 deletions(-)

diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
index 7f01c6e60791..7b9b876b513b 100644
--- a/fs/cifs/cifs_spnego.c
+++ b/fs/cifs/cifs_spnego.c
@@ -98,7 +98,7 @@ struct key_type cifs_spnego_key_type = {
 struct key *
 cifs_get_spnego_key(struct cifs_ses *sesInfo)
 {
-	struct TCP_Server_Info *server = sesInfo->server;
+	struct TCP_Server_Info *server = cifs_ses_server(sesInfo);
 	struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
 	struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
 	char *description, *dp;
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index e980e7b0d79d..57c6d322a877 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -992,6 +992,7 @@ struct cifs_ses {
 	bool sign;		/* is signing required? */
 	bool need_reconnect:1; /* connection reset, uid now invalid */
 	bool domainAuto:1;
+	bool binding:1; /* are we binding the session? */
 	__u16 session_flags;
 	__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
 	__u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
@@ -1018,6 +1019,15 @@ struct cifs_ses {
 	size_t chan_max;
 };
 
+static inline
+struct TCP_Server_Info *cifs_ses_server(struct cifs_ses *ses)
+{
+	if (ses->binding)
+		return ses->chans[ses->chan_count].server;
+	else
+		return ses->server;
+}
+
 static inline bool
 cap_unix(struct cifs_ses *ses)
 {
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index fb95fd90b578..3e150cc8a5ae 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -5163,7 +5163,7 @@ int
 cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
 {
 	int rc = 0;
-	struct TCP_Server_Info *server = ses->server;
+	struct TCP_Server_Info *server = cifs_ses_server(ses);
 
 	if (!server->ops->need_neg || !server->ops->negotiate)
 		return -ENOSYS;
@@ -5190,7 +5190,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
 		   struct nls_table *nls_info)
 {
 	int rc = -ENOSYS;
-	struct TCP_Server_Info *server = ses->server;
+	struct TCP_Server_Info *server = cifs_ses_server(ses);
 
 	ses->capabilities = server->capabilities;
 	if (linuxExtEnabled == 0)
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 4c764ff7edd2..3c1c3999a414 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -342,6 +342,7 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
 void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
 					 struct cifs_ses *ses)
 {
+	struct TCP_Server_Info *server = cifs_ses_server(ses);
 	NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;
 	__u32 flags;
 
@@ -354,9 +355,9 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
 		NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
 		NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
 		NTLMSSP_NEGOTIATE_SEAL;
-	if (ses->server->sign)
+	if (server->sign)
 		flags |= NTLMSSP_NEGOTIATE_SIGN;
-	if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
+	if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
 		flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
 
 	sec_blob->NegotiateFlags = cpu_to_le32(flags);
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index eaed18061314..1ca35672350c 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -315,7 +315,7 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 {
 	int rc;
 
-	ses->server->CurrentMid = 0;
+	cifs_ses_server(ses)->CurrentMid = 0;
 	rc = SMB2_negotiate(xid, ses);
 	/* BB we probably don't need to retry with modern servers */
 	if (rc == -EAGAIN)
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 87066f1af12c..9637f6e1d3ba 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -789,7 +789,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 	struct kvec rsp_iov;
 	int rc = 0;
 	int resp_buftype;
-	struct TCP_Server_Info *server = ses->server;
+	struct TCP_Server_Info *server = cifs_ses_server(ses);
 	int blob_offset, blob_length;
 	char *security_blob;
 	int flags = CIFS_NEG_OP;
@@ -811,7 +811,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 	memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
 	memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
 
-	if (strcmp(ses->server->vals->version_string,
+	if (strcmp(server->vals->version_string,
 		   SMB3ANY_VERSION_STRING) == 0) {
 		req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
 		req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
@@ -827,7 +827,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
 		total_len += 8;
 	} else {
 		/* otherwise send specific dialect */
-		req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
+		req->Dialects[0] = cpu_to_le16(server->vals->protocol_id);
 		req->DialectCount = cpu_to_le16(1);
 		total_len += 2;
 	}
@@ -1169,7 +1169,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
 	int rc;
 	struct cifs_ses *ses = sess_data->ses;
 	struct smb2_sess_setup_req *req;
-	struct TCP_Server_Info *server = ses->server;
+	struct TCP_Server_Info *server = cifs_ses_server(ses);
 	unsigned int total_len;
 
 	rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, (void **) &req,
@@ -1256,22 +1256,23 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
 {
 	int rc = 0;
 	struct cifs_ses *ses = sess_data->ses;
+	struct TCP_Server_Info *server = cifs_ses_server(ses);
 
-	mutex_lock(&ses->server->srv_mutex);
-	if (ses->server->ops->generate_signingkey) {
-		rc = ses->server->ops->generate_signingkey(ses);
+	mutex_lock(&server->srv_mutex);
+	if (server->ops->generate_signingkey) {
+		rc = server->ops->generate_signingkey(ses);
 		if (rc) {
 			cifs_dbg(FYI,
 				"SMB3 session key generation failed\n");
-			mutex_unlock(&ses->server->srv_mutex);
+			mutex_unlock(&server->srv_mutex);
 			return rc;
 		}
 	}
-	if (!ses->server->session_estab) {
-		ses->server->sequence_number = 0x2;
-		ses->server->session_estab = true;
+	if (!server->session_estab) {
+		server->sequence_number = 0x2;
+		server->session_estab = true;
 	}
-	mutex_unlock(&ses->server->srv_mutex);
+	mutex_unlock(&server->srv_mutex);
 
 	cifs_dbg(FYI, "SMB2/3 session established successfully\n");
 	spin_lock(&GlobalMid_Lock);
@@ -1507,7 +1508,7 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
 {
 	int type;
 
-	type = smb2_select_sectype(ses->server, ses->sectype);
+	type = smb2_select_sectype(cifs_ses_server(ses), ses->sectype);
 	cifs_dbg(FYI, "sess setup type %d\n", type);
 	if (type == Unspecified) {
 		cifs_dbg(VFS,
@@ -1535,7 +1536,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
 		const struct nls_table *nls_cp)
 {
 	int rc = 0;
-	struct TCP_Server_Info *server = ses->server;
+	struct TCP_Server_Info *server = cifs_ses_server(ses);
 	struct SMB2_sess_data *sess_data;
 
 	cifs_dbg(FYI, "Session Setup\n");
@@ -1561,7 +1562,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
 	/*
 	 * Initialize the session hash with the server one.
 	 */
-	memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash,
+	memcpy(ses->preauth_sha_hash, server->preauth_sha_hash,
 	       SMB2_PREAUTH_HASH_SIZE);
 
 	while (sess_data->func)
-- 
2.16.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 4/6] cifs: sort interface list by speed
  2019-09-20  5:01 [PATCH v4 0/6] multichannel Aurelien Aptel
                   ` (2 preceding siblings ...)
  2019-09-20  5:01 ` [PATCH v4 3/6] cifs: switch servers depending on binding state Aurelien Aptel
@ 2019-09-20  5:01 ` Aurelien Aptel
  2019-09-20  5:01 ` [PATCH v4 5/6] cifs: try opening channels after mounting Aurelien Aptel
  2019-09-20  5:01 ` [PATCH v4 6/6] cifs: mention if an interface has a channel connected to it Aurelien Aptel
  5 siblings, 0 replies; 8+ messages in thread
From: Aurelien Aptel @ 2019-09-20  5:01 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, Aurelien Aptel

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
---
 fs/cifs/smb2ops.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 1ca35672350c..0e66dc1aa1c9 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -10,6 +10,7 @@
 #include <linux/falloc.h>
 #include <linux/scatterlist.h>
 #include <linux/uuid.h>
+#include <linux/sort.h>
 #include <crypto/aead.h>
 #include "cifsglob.h"
 #include "smb2pdu.h"
@@ -558,6 +559,13 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
 	return rc;
 }
 
+static int compare_iface(const void *ia, const void *ib)
+{
+	const struct cifs_server_iface *a = (struct cifs_server_iface *)ia;
+	const struct cifs_server_iface *b = (struct cifs_server_iface *)ib;
+
+	return a->speed == b->speed ? 0 : (a->speed > b->speed ? -1 : 1);
+}
 
 static int
 SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
@@ -587,6 +595,9 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
 	if (rc)
 		goto out;
 
+	/* sort interfaces from fastest to slowest */
+	sort(iface_list, iface_count, sizeof(*iface_list), compare_iface, NULL);
+
 	spin_lock(&ses->iface_lock);
 	kfree(ses->iface_list);
 	ses->iface_list = iface_list;
-- 
2.16.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 5/6] cifs: try opening channels after mounting
  2019-09-20  5:01 [PATCH v4 0/6] multichannel Aurelien Aptel
                   ` (3 preceding siblings ...)
  2019-09-20  5:01 ` [PATCH v4 4/6] cifs: sort interface list by speed Aurelien Aptel
@ 2019-09-20  5:01 ` Aurelien Aptel
  2019-09-20  7:30   ` Steve French
  2019-09-20  5:01 ` [PATCH v4 6/6] cifs: mention if an interface has a channel connected to it Aurelien Aptel
  5 siblings, 1 reply; 8+ messages in thread
From: Aurelien Aptel @ 2019-09-20  5:01 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, Aurelien Aptel

After doing mount() sucessfully we call cifs_try_adding_channels()
which will open as many channels as it can.

The master connection become the first channel.

Each channel has its own signing key which must be used only after the
channel has been opened.

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
---
 fs/cifs/cifsglob.h      |   2 +
 fs/cifs/cifsproto.h     |   7 ++
 fs/cifs/connect.c       |  40 ++++++---
 fs/cifs/sess.c          | 211 ++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/smb2misc.c      |  37 ++++++---
 fs/cifs/smb2pdu.c       |  75 ++++++++++-------
 fs/cifs/smb2transport.c | 126 +++++++++++++++++++++++------
 fs/cifs/transport.c     |  10 ++-
 8 files changed, 430 insertions(+), 78 deletions(-)

diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 57c6d322a877..98b3fad66c55 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -999,6 +999,8 @@ struct cifs_ses {
 	__u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
 	__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
 
+	__u8 binding_preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
+
 	/*
 	 * Network interfaces available on the server this session is
 	 * connected to.
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index ee3e6d6556a7..4cda8bba308b 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -242,6 +242,7 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
 					 struct tcon_link *tlink,
 					 struct cifs_pending_open *open);
 extern void cifs_del_pending_open(struct cifs_pending_open *open);
+extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb_vol *vol);
 extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
 				 int from_reconnect);
 extern void cifs_put_tcon(struct cifs_tcon *tcon);
@@ -583,6 +584,12 @@ 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);
+int cifs_try_adding_channels(struct cifs_ses *ses);
+int cifs_ses_add_channel(struct cifs_ses *ses,
+				struct cifs_server_iface *iface);
+bool is_server_using_iface(struct TCP_Server_Info *server,
+			   struct cifs_server_iface *iface);
+bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
 
 void extract_unc_hostname(const char *unc, const char **h, size_t *len);
 int copy_path_name(char *dst, const char *src);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 3e150cc8a5ae..9d6805062841 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -2728,7 +2728,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
 		send_sig(SIGKILL, task, 1);
 }
 
-static struct TCP_Server_Info *
+struct TCP_Server_Info *
 cifs_get_tcp_session(struct smb_vol *volume_info)
 {
 	struct TCP_Server_Info *tcp_ses = NULL;
@@ -3302,14 +3302,25 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
 	ses->sectype = volume_info->sectype;
 	ses->sign = volume_info->sign;
 	mutex_lock(&ses->session_mutex);
+
+	/* add server as first channel */
+	ses->chans[0].server = server;
+	ses->chan_count = 1;
+	ses->chan_max = volume_info->multichannel ? volume_info->max_channels:1;
+
 	rc = cifs_negotiate_protocol(xid, ses);
 	if (!rc)
 		rc = cifs_setup_session(xid, ses, volume_info->local_nls);
+
+	/* each channel uses a different signing key */
+	memcpy(ses->chans[0].signkey, ses->smb3signingkey,
+	       sizeof(ses->smb3signingkey));
+
 	mutex_unlock(&ses->session_mutex);
 	if (rc)
 		goto get_ses_fail;
 
-	/* success, put it on the list */
+	/* success, put it on the list and add it as first channel */
 	spin_lock(&cifs_tcp_ses_lock);
 	list_add(&ses->smb_ses_list, &server->smb_ses_list);
 	spin_unlock(&cifs_tcp_ses_lock);
@@ -4918,6 +4929,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
 	cifs_autodisable_serverino(cifs_sb);
 out:
 	free_xid(xid);
+	cifs_try_adding_channels(ses);
 	return mount_setup_tlink(cifs_sb, ses, tcon);
 
 error:
@@ -5192,21 +5204,23 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
 	int rc = -ENOSYS;
 	struct TCP_Server_Info *server = cifs_ses_server(ses);
 
-	ses->capabilities = server->capabilities;
-	if (linuxExtEnabled == 0)
-		ses->capabilities &= (~server->vals->cap_unix);
+	if (!ses->binding) {
+		ses->capabilities = server->capabilities;
+		if (linuxExtEnabled == 0)
+			ses->capabilities &= (~server->vals->cap_unix);
+
+		if (ses->auth_key.response) {
+			cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
+				 ses->auth_key.response);
+			kfree(ses->auth_key.response);
+			ses->auth_key.response = NULL;
+			ses->auth_key.len = 0;
+		}
+	}
 
 	cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
 		 server->sec_mode, server->capabilities, server->timeAdj);
 
-	if (ses->auth_key.response) {
-		cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
-			 ses->auth_key.response);
-		kfree(ses->auth_key.response);
-		ses->auth_key.response = NULL;
-		ses->auth_key.len = 0;
-	}
-
 	if (server->ops->sess_setup)
 		rc = server->ops->sess_setup(xid, ses, nls_info);
 
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 3c1c3999a414..20c73a42755d 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -31,6 +31,217 @@
 #include <linux/utsname.h>
 #include <linux/slab.h>
 #include "cifs_spnego.h"
+#include "smb2proto.h"
+
+bool
+is_server_using_iface(struct TCP_Server_Info *server,
+		      struct cifs_server_iface *iface)
+{
+	struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr;
+	struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr;
+	struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr;
+	struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr;
+
+	if (server->dstaddr.ss_family != iface->sockaddr.ss_family)
+		return false;
+	if (server->dstaddr.ss_family == AF_INET) {
+		if (s4->sin_addr.s_addr != i4->sin_addr.s_addr)
+			return false;
+	} else if (server->dstaddr.ss_family == AF_INET6) {
+		if (memcmp(&s6->sin6_addr, &i6->sin6_addr,
+			   sizeof(i6->sin6_addr)) != 0)
+			return false;
+	} else {
+		/* unknown family.. */
+		return false;
+	}
+	return true;
+}
+
+bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
+{
+	int i;
+	for (i = 0; i < ses->chan_count; i++) {
+		if (is_server_using_iface(ses->chans[i].server, iface))
+			return true;
+	}
+	return false;
+}
+
+/* returns number of channels added */
+int cifs_try_adding_channels(struct cifs_ses *ses)
+{
+	int old_chan_count = ses->chan_count;
+	int left = ses->chan_max - ses->chan_count;
+	int i = 0;
+	int rc = 0;
+
+	if (left <= 0) {
+		cifs_dbg(FYI,
+			 "ses already at max_channels (%zu), nothing to open\n",
+			 ses->chan_max);
+		return 0;
+	}
+
+	if (ses->server->dialect != SMB311_PROT_ID) {
+		cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.1.1\n");
+		return 0;
+	}
+
+	/* ifaces are sorted by speed, try them in order */
+	for (i = 0; left > 0 && i < ses->iface_count; i++) {
+		struct cifs_server_iface *iface;
+
+		iface = &ses->iface_list[i];
+		if (is_ses_using_iface(ses, iface) && !iface->rss_capable)
+			continue;
+
+		rc = cifs_ses_add_channel(ses, iface);
+		if (rc) {
+			cifs_dbg(FYI, "failed to open extra channel\n");
+			continue;
+		}
+
+		cifs_dbg(FYI, "sucessfully opened new channel\n");
+		left--;
+	}
+
+	/*
+	 * TODO: if we still have channels left to open try to connect
+	 * to same RSS-capable iface multiple times
+	 */
+
+	return ses->chan_count - old_chan_count;
+}
+
+int
+cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface)
+{
+	struct cifs_chan *chan;
+	struct smb_vol vol = {0};
+	const char unc_fmt[] = "\\%s\\foo";
+	char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0};
+	struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
+	struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
+	int rc;
+	unsigned int xid = get_xid();
+
+	cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ",
+		 ses, iface->speed, iface->rdma_capable ? "yes" : "no");
+	if (iface->sockaddr.ss_family == AF_INET)
+		cifs_dbg(FYI, "ip:%pI4)\n", &ipv4->sin_addr);
+	else
+		cifs_dbg(FYI, "ip:%pI6)\n", &ipv6->sin6_addr);
+
+	/*
+	 * Setup a smb_vol with mostly the same info as the existing
+	 * session and overwrite it with the requested iface data.
+	 *
+	 * We need to setup at least the fields used for negprot and
+	 * sesssetup.
+	 *
+	 * We only need the volume here, so we can reuse memory from
+	 * the session and server without caring about memory
+	 * management.
+	 */
+
+	/* Always make new connection for now (TODO?) */
+	vol.nosharesock = true;
+
+	/* Auth */
+	vol.domainauto = ses->domainAuto;
+	vol.domainname = ses->domainName;
+	vol.username = ses->user_name;
+	vol.password = ses->password;
+	vol.sectype = ses->sectype;
+	vol.sign = ses->sign;
+
+	/* UNC and paths */
+	/* XXX: Use ses->server->hostname? */
+	sprintf(unc, unc_fmt, ses->serverName);
+	vol.UNC = unc;
+	vol.prepath = "";
+
+	/* Require SMB3.1.1 */
+	vol.vals = &smb311_values;
+	vol.ops = &smb311_operations;
+
+	vol.noblocksnd = ses->server->noblocksnd;
+	vol.noautotune = ses->server->noautotune;
+	vol.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
+	vol.echo_interval = ses->server->echo_interval / HZ;
+
+	/*
+	 * This will be used for encoding/decoding user/domain/pw
+	 * during sess setup auth.
+	 *
+	 * XXX: We use the default for simplicity but the proper way
+	 * would be to use the one that ses used, which is not
+	 * stored. This might break when dealing with non-ascii
+	 * strings.
+	 */
+	vol.local_nls = load_nls_default();
+
+	/* Use RDMA if possible */
+	vol.rdma = iface->rdma_capable;
+	memcpy(&vol.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage));
+
+	/* reuse master con client guid */
+	memcpy(&vol.client_guid, ses->server->client_guid, SMB2_CLIENT_GUID_SIZE);
+	vol.use_client_guid = true;
+
+	mutex_lock(&ses->session_mutex);
+
+	chan = &ses->chans[ses->chan_count];
+	chan->server = cifs_get_tcp_session(&vol);
+	if (IS_ERR(chan->server)) {
+		rc = PTR_ERR(chan->server);
+		chan->server = NULL;
+		goto out;
+	}
+
+	/*
+	 * We need to allocate the server crypto now as we will need
+	 * to sign packets before we generate the channel signing key
+	 * (we sign with the session key)
+	 */
+	rc = smb311_crypto_shash_allocate(chan->server);
+	if (rc) {
+		cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
+		goto out;
+	}
+
+	ses->binding = true;
+	rc = cifs_negotiate_protocol(xid, ses);
+	if (rc)
+		goto out;
+
+	rc = cifs_setup_session(xid, ses, vol.local_nls);
+	if (rc)
+		goto out;
+
+	/* success, put it on the list
+	 * XXX: sharing ses between 2 tcp server is not possible, the
+	 * way "internal" linked lists works in linux makes element
+	 * only able to belong to one list
+	 *
+	 * the binding session is already established so the rest of
+	 * the code should be able to look it up, no need to add the
+	 * ses to the new server.
+	 */
+
+	ses->chan_count++;
+
+out:
+	ses->binding = false;
+	mutex_unlock(&ses->session_mutex);
+
+	if (rc && chan->server)
+		cifs_put_tcp_session(chan->server, 0);
+	unload_nls(vol.local_nls);
+
+	return rc;
+}
 
 static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
 {
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index e311f58dc1c8..a95ed951c67f 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -29,6 +29,7 @@
 #include "cifs_unicode.h"
 #include "smb2status.h"
 #include "smb2glob.h"
+#include "nterr.h"
 
 static int
 check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid)
@@ -788,23 +789,37 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
 	int i, rc;
 	struct sdesc *d;
 	struct smb2_sync_hdr *hdr;
+	struct TCP_Server_Info *server = cifs_ses_server(ses);
 
-	if (ses->server->tcpStatus == CifsGood) {
-		/* skip non smb311 connections */
-		if (ses->server->dialect != SMB311_PROT_ID)
-			return 0;
+	hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
+	/* neg prot are always taken */
+	if (hdr->Command == SMB2_NEGOTIATE)
+		goto ok;
 
-		/* skip last sess setup response */
-		hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
-		if (hdr->Flags & SMB2_FLAGS_SIGNED)
-			return 0;
-	}
+	/*
+	 * If we process a command which wasn't a negprot it means the
+	 * neg prot was already done, so the server dialect was set
+	 * and we can test it. Preauth requires 3.1.1 for now.
+	 */
+	if (server->dialect != SMB311_PROT_ID)
+		return 0;
+
+	if (hdr->Command != SMB2_SESSION_SETUP)
+		return 0;
+
+	/* skip last sess setup response */
+	if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
+	    && (hdr->Status == NT_STATUS_OK
+		|| (hdr->Status !=
+		    cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))))
+		return 0;
 
-	rc = smb311_crypto_shash_allocate(ses->server);
+ok:
+	rc = smb311_crypto_shash_allocate(server);
 	if (rc)
 		return rc;
 
-	d = ses->server->secmech.sdescsha512;
+	d = server->secmech.sdescsha512;
 	rc = crypto_shash_init(&d->shash);
 	if (rc) {
 		cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__);
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 9637f6e1d3ba..8bcb278fdb0a 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1177,13 +1177,18 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
 	if (rc)
 		return rc;
 
-	/* First session, not a reauthenticate */
-	req->sync_hdr.SessionId = 0;
-
-	/* if reconnect, we need to send previous sess id, otherwise it is 0 */
-	req->PreviousSessionId = sess_data->previous_session;
-
-	req->Flags = 0; /* MBZ */
+	if (sess_data->ses->binding) {
+		req->sync_hdr.SessionId = sess_data->ses->Suid;
+		req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
+		req->PreviousSessionId = 0;
+		req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
+	} else {
+		/* First session, not a reauthenticate */
+		req->sync_hdr.SessionId = 0;
+		/* if reconnect, we need to send previous sess id, otherwise it is 0 */
+		req->PreviousSessionId = sess_data->previous_session;
+		req->Flags = 0; /* MBZ */
+	}
 
 	/* enough to enable echos and oplocks and one max size write */
 	req->sync_hdr.CreditRequest = cpu_to_le16(130);
@@ -1275,10 +1280,14 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
 	mutex_unlock(&server->srv_mutex);
 
 	cifs_dbg(FYI, "SMB2/3 session established successfully\n");
-	spin_lock(&GlobalMid_Lock);
-	ses->status = CifsGood;
-	ses->need_reconnect = false;
-	spin_unlock(&GlobalMid_Lock);
+	/* keep exising ses state if binding */
+	if (!ses->binding) {
+		spin_lock(&GlobalMid_Lock);
+		ses->status = CifsGood;
+		ses->need_reconnect = false;
+		spin_unlock(&GlobalMid_Lock);
+	}
+
 	return rc;
 }
 
@@ -1316,16 +1325,19 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
 		goto out_put_spnego_key;
 	}
 
-	ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
-					 GFP_KERNEL);
-	if (!ses->auth_key.response) {
-		cifs_dbg(VFS,
-			"Kerberos can't allocate (%u bytes) memory",
-			msg->sesskey_len);
-		rc = -ENOMEM;
-		goto out_put_spnego_key;
+	/* keep session key if binding */
+	if (!ses->binding) {
+		ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
+						 GFP_KERNEL);
+		if (!ses->auth_key.response) {
+			cifs_dbg(VFS,
+				 "Kerberos can't allocate (%u bytes) memory",
+				 msg->sesskey_len);
+			rc = -ENOMEM;
+			goto out_put_spnego_key;
+		}
+		ses->auth_key.len = msg->sesskey_len;
 	}
-	ses->auth_key.len = msg->sesskey_len;
 
 	sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
 	sess_data->iov[1].iov_len = msg->secblob_len;
@@ -1335,9 +1347,11 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
 		goto out_put_spnego_key;
 
 	rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
-	ses->Suid = rsp->sync_hdr.SessionId;
-
-	ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+	/* keep session id and flags if binding */
+	if (!ses->binding) {
+		ses->Suid = rsp->sync_hdr.SessionId;
+		ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+	}
 
 	rc = SMB2_sess_establish_session(sess_data);
 out_put_spnego_key:
@@ -1431,9 +1445,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
 
 	cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
 
-
-	ses->Suid = rsp->sync_hdr.SessionId;
-	ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+	/* keep existing ses id and flags if binding */
+	if (!ses->binding) {
+		ses->Suid = rsp->sync_hdr.SessionId;
+		ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+	}
 
 out:
 	kfree(ntlmssp_blob);
@@ -1490,8 +1506,11 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
 
 	rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
 
-	ses->Suid = rsp->sync_hdr.SessionId;
-	ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+	/* keep existing ses id and flags if binding */
+	if (!ses->binding) {
+		ses->Suid = rsp->sync_hdr.SessionId;
+		ses->session_flags = le16_to_cpu(rsp->SessionFlags);
+	}
 
 	rc = SMB2_sess_establish_session(sess_data);
 out:
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index d7d89741f3f0..12988df6b24d 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -48,7 +48,7 @@ smb2_crypto_shash_allocate(struct TCP_Server_Info *server)
 			       &server->secmech.sdeschmacsha256);
 }
 
-static int
+int
 smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
 {
 	struct cifs_secmech *p = &server->secmech;
@@ -98,6 +98,44 @@ smb311_crypto_shash_allocate(struct TCP_Server_Info *server)
 	return rc;
 }
 
+u8 *smb2_find_chan_signkey(struct cifs_ses *ses, struct TCP_Server_Info *server)
+{
+	int i;
+	struct cifs_chan *chan;
+	int count;
+	spin_lock(&cifs_tcp_ses_lock);
+	count = ses->chan_count;
+	if (ses->binding)
+		count++;
+	for (i = 0; i < count; i++) {
+		chan = ses->chans + i;
+		if (chan->server == server) {
+			spin_unlock(&cifs_tcp_ses_lock);
+			return chan->signkey;
+		}
+	}
+	spin_unlock(&cifs_tcp_ses_lock);
+	return NULL;
+}
+
+struct cifs_ses *
+smb2_find_global_smb_ses(__u64 ses_id)
+{
+	struct TCP_Server_Info *server;
+	struct cifs_ses *ses;
+	spin_lock(&cifs_tcp_ses_lock);
+	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
+		list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+			if (ses->Suid == ses_id) {
+				spin_unlock(&cifs_tcp_ses_lock);
+				return ses;
+			}
+		}
+	}
+	spin_unlock(&cifs_tcp_ses_lock);
+	return NULL;
+}
+
 static struct cifs_ses *
 smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
 {
@@ -328,21 +366,33 @@ generate_smb3signingkey(struct cifs_ses *ses,
 {
 	int rc;
 
-	rc = generate_key(ses, ptriplet->signing.label,
-			  ptriplet->signing.context, ses->smb3signingkey,
-			  SMB3_SIGN_KEY_SIZE);
-	if (rc)
-		return rc;
-
-	rc = generate_key(ses, ptriplet->encryption.label,
-			  ptriplet->encryption.context, ses->smb3encryptionkey,
-			  SMB3_SIGN_KEY_SIZE);
-	if (rc)
-		return rc;
-
-	rc = generate_key(ses, ptriplet->decryption.label,
-			  ptriplet->decryption.context,
-			  ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
+	if (ses->binding) {
+		struct TCP_Server_Info *server;
+		server = cifs_ses_server(ses);
+		rc = generate_key(ses, ptriplet->signing.label,
+				  ptriplet->signing.context,
+				  smb2_find_chan_signkey(ses, server),
+				  SMB3_SIGN_KEY_SIZE);
+		if (rc)
+			return rc;
+	} else {
+		rc = generate_key(ses, ptriplet->signing.label,
+				  ptriplet->signing.context,
+				  ses->smb3signingkey,
+				  SMB3_SIGN_KEY_SIZE);
+		if (rc)
+			return rc;
+		rc = generate_key(ses, ptriplet->encryption.label,
+				  ptriplet->encryption.context,
+				  ses->smb3encryptionkey,
+				  SMB3_SIGN_KEY_SIZE);
+		rc = generate_key(ses, ptriplet->decryption.label,
+				  ptriplet->decryption.context,
+				  ses->smb3decryptionkey,
+				  SMB3_SIGN_KEY_SIZE);
+		if (rc)
+			return rc;
+	}
 
 	if (rc)
 		return rc;
@@ -434,18 +484,35 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 	struct cifs_ses *ses;
 	struct shash_desc *shash = &server->secmech.sdesccmacaes->shash;
 	struct smb_rqst drqst;
+	u8 *key;
 
-	ses = smb2_find_smb_ses(server, shdr->SessionId);
+	ses = smb2_find_global_smb_ses(shdr->SessionId);
 	if (!ses) {
 		cifs_server_dbg(VFS, "%s: Could not find session\n", __func__);
 		return 0;
 	}
 
+	/*
+	 * If we are binding an existing session use the session key,
+	 * otherwise use the channel key
+	 */
+	if (ses->binding)
+		key = ses->smb3signingkey;
+	else {
+		key = smb2_find_chan_signkey(ses, server);
+		if (!key) {
+			cifs_dbg(VFS,
+				 "%s: Could not find channel signing key\n",
+				 __func__);
+			return 0;
+		}
+        }
+
 	memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
 	memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
 
 	rc = crypto_shash_setkey(server->secmech.cmacaes,
-				 ses->smb3signingkey, SMB2_CMACAES_SIZE);
+				 key, SMB2_CMACAES_SIZE);
 	if (rc) {
 		cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
 		return rc;
@@ -494,16 +561,25 @@ static int
 smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 {
 	int rc = 0;
-	struct smb2_sync_hdr *shdr =
-			(struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
+	struct smb2_sync_hdr *shdr;
+	struct smb2_sess_setup_req *ssr;
+	bool is_binding;
+	bool is_signed;
 
-	if (!(shdr->Flags & SMB2_FLAGS_SIGNED) ||
-	    server->tcpStatus == CifsNeedNegotiate)
-		return rc;
+	shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
+	ssr = (struct smb2_sess_setup_req *)shdr;
 
-	if (!server->session_estab) {
+	is_binding = shdr->Command == SMB2_SESSION_SETUP &&
+		(ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING);
+	is_signed = shdr->Flags & SMB2_FLAGS_SIGNED;
+
+	if (!is_signed)
+		return 0;
+	if (server->tcpStatus == CifsNeedNegotiate)
+		return 0;
+	if (!is_binding && !server->session_estab) {
 		strncpy(shdr->Signature, "BSRSPYL", 8);
-		return rc;
+		return 0;
 	}
 
 	rc = server->ops->calc_signature(rqst, server);
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index afe66f9cb15e..39e6b47a77d9 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -995,7 +995,15 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
 		return -EIO;
 	}
 
-	server = ses->server;
+	if (!ses->binding) {
+		uint index = 0;
+		if (ses->chan_count > 1)
+			index = ((unsigned)get_random_int()) % ses->chan_count;
+		server = ses->chans[index].server;
+	} else {
+		server = cifs_ses_server(ses);
+	}
+
 	if (server->tcpStatus == CifsExiting)
 		return -ENOENT;
 
-- 
2.16.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v4 6/6] cifs: mention if an interface has a channel connected to it
  2019-09-20  5:01 [PATCH v4 0/6] multichannel Aurelien Aptel
                   ` (4 preceding siblings ...)
  2019-09-20  5:01 ` [PATCH v4 5/6] cifs: try opening channels after mounting Aurelien Aptel
@ 2019-09-20  5:01 ` Aurelien Aptel
  5 siblings, 0 replies; 8+ messages in thread
From: Aurelien Aptel @ 2019-09-20  5:01 UTC (permalink / raw)
  To: linux-cifs; +Cc: smfrench, Aurelien Aptel

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
---
 fs/cifs/cifs_debug.c | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 0b4eee3bed66..ef454170f37d 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -410,8 +410,12 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
 				seq_printf(m, "\n\tServer interfaces: %zu\n",
 					   ses->iface_count);
 			for (j = 0; j < ses->iface_count; j++) {
+				struct cifs_server_iface *iface;
+				iface = &ses->iface_list[j];
 				seq_printf(m, "\t%d)", j);
-				cifs_dump_iface(m, &ses->iface_list[j]);
+				cifs_dump_iface(m, iface);
+				if (is_ses_using_iface(ses, iface))
+					seq_puts(m, "\t\t[CONNECTED]\n");
 			}
 			spin_unlock(&ses->iface_lock);
 		}
-- 
2.16.4


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v4 5/6] cifs: try opening channels after mounting
  2019-09-20  5:01 ` [PATCH v4 5/6] cifs: try opening channels after mounting Aurelien Aptel
@ 2019-09-20  7:30   ` Steve French
  0 siblings, 0 replies; 8+ messages in thread
From: Steve French @ 2019-09-20  7:30 UTC (permalink / raw)
  To: Aurelien Aptel; +Cc: CIFS

Looks promising.

Some trivial checkpatch things to clean up in this (patch 5 and also patch 6)

0005-cifs-try-opening-channels-after-mounting.patch
---------------------------------------------------
WARNING: 'sucessfully' may be misspelled - perhaps 'successfully'?
#6:
After doing mount() sucessfully we call cifs_try_adding_channels()

WARNING: Missing a blank line after declarations
#186: FILE: fs/cifs/sess.c:64:
+    int i;
+    for (i = 0; i < ses->chan_count; i++) {

WARNING: 'sucessfully' may be misspelled - perhaps 'successfully'?
#227: FILE: fs/cifs/sess.c:105:
+        cifs_dbg(FYI, "sucessfully opened new channel\n");

WARNING: const array should probably be static const
#244: FILE: fs/cifs/sess.c:122:
+    const char unc_fmt[] = "\\%s\\foo";

WARNING: Possible unnecessary 'out of memory' message
#497: FILE: fs/cifs/smb2pdu.c:1333:
+        if (!ses->auth_key.response) {
+            cifs_dbg(VFS,

WARNING: Missing a blank line after declarations
#575: FILE: fs/cifs/smb2transport.c:106:
+    int count;
+    spin_lock(&cifs_tcp_ses_lock);

WARNING: Missing a blank line after declarations
#595: FILE: fs/cifs/smb2transport.c:126:
+    struct cifs_ses *ses;
+    spin_lock(&cifs_tcp_ses_lock);

WARNING: Missing a blank line after declarations
#632: FILE: fs/cifs/smb2transport.c:371:
+        struct TCP_Server_Info *server;
+        server = cifs_ses_server(ses);

ERROR: code indent should use tabs where possible
#687: FILE: fs/cifs/smb2transport.c:509:
+        }$

WARNING: please, no spaces at the start of a line
#687: FILE: fs/cifs/smb2transport.c:509:
+        }$

WARNING: Missing a blank line after declarations
#742: FILE: fs/cifs/transport.c:1000:
+        uint index = 0;
+        if (ses->chan_count > 1)

WARNING: Prefer 'unsigned int' to bare use of 'unsigned'
#743: FILE: fs/cifs/transport.c:1001:
+            index = ((unsigned)get_random_int()) % ses->chan_count;

total: 1 errors, 13 warnings, 671 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

NOTE: Whitespace errors detected.
      You may wish to use scripts/cleanpatch or scripts/cleanfile

0005-cifs-try-opening-channels-after-mounting.patch has style
problems, please review.
---------------------------------------------------------------
0006-cifs-mention-if-an-interface-has-a-channel-connected.patch
---------------------------------------------------------------
WARNING: Missing commit description - Add an appropriate one

WARNING: Missing a blank line after declarations
#22: FILE: fs/cifs/cifs_debug.c:414:
+                struct cifs_server_iface *iface;
+                iface = &ses->iface_list[j];

total: 0 errors, 2 warnings, 13 lines checked

On Fri, Sep 20, 2019 at 12:01 AM Aurelien Aptel <aaptel@suse.com> wrote:
>
> After doing mount() sucessfully we call cifs_try_adding_channels()
> which will open as many channels as it can.
>
> The master connection become the first channel.
>
> Each channel has its own signing key which must be used only after the
> channel has been opened.
>
> Signed-off-by: Aurelien Aptel <aaptel@suse.com>
> ---
>  fs/cifs/cifsglob.h      |   2 +
>  fs/cifs/cifsproto.h     |   7 ++
>  fs/cifs/connect.c       |  40 ++++++---
>  fs/cifs/sess.c          | 211 ++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/cifs/smb2misc.c      |  37 ++++++---
>  fs/cifs/smb2pdu.c       |  75 ++++++++++-------
>  fs/cifs/smb2transport.c | 126 +++++++++++++++++++++++------
>  fs/cifs/transport.c     |  10 ++-
>  8 files changed, 430 insertions(+), 78 deletions(-)
>
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 57c6d322a877..98b3fad66c55 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -999,6 +999,8 @@ struct cifs_ses {
>         __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
>         __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
>
> +       __u8 binding_preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
> +
>         /*
>          * Network interfaces available on the server this session is
>          * connected to.
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index ee3e6d6556a7..4cda8bba308b 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -242,6 +242,7 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
>                                          struct tcon_link *tlink,
>                                          struct cifs_pending_open *open);
>  extern void cifs_del_pending_open(struct cifs_pending_open *open);
> +extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb_vol *vol);
>  extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
>                                  int from_reconnect);
>  extern void cifs_put_tcon(struct cifs_tcon *tcon);
> @@ -583,6 +584,12 @@ 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);
> +int cifs_try_adding_channels(struct cifs_ses *ses);
> +int cifs_ses_add_channel(struct cifs_ses *ses,
> +                               struct cifs_server_iface *iface);
> +bool is_server_using_iface(struct TCP_Server_Info *server,
> +                          struct cifs_server_iface *iface);
> +bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
>
>  void extract_unc_hostname(const char *unc, const char **h, size_t *len);
>  int copy_path_name(char *dst, const char *src);
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 3e150cc8a5ae..9d6805062841 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -2728,7 +2728,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
>                 send_sig(SIGKILL, task, 1);
>  }
>
> -static struct TCP_Server_Info *
> +struct TCP_Server_Info *
>  cifs_get_tcp_session(struct smb_vol *volume_info)
>  {
>         struct TCP_Server_Info *tcp_ses = NULL;
> @@ -3302,14 +3302,25 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
>         ses->sectype = volume_info->sectype;
>         ses->sign = volume_info->sign;
>         mutex_lock(&ses->session_mutex);
> +
> +       /* add server as first channel */
> +       ses->chans[0].server = server;
> +       ses->chan_count = 1;
> +       ses->chan_max = volume_info->multichannel ? volume_info->max_channels:1;
> +
>         rc = cifs_negotiate_protocol(xid, ses);
>         if (!rc)
>                 rc = cifs_setup_session(xid, ses, volume_info->local_nls);
> +
> +       /* each channel uses a different signing key */
> +       memcpy(ses->chans[0].signkey, ses->smb3signingkey,
> +              sizeof(ses->smb3signingkey));
> +
>         mutex_unlock(&ses->session_mutex);
>         if (rc)
>                 goto get_ses_fail;
>
> -       /* success, put it on the list */
> +       /* success, put it on the list and add it as first channel */
>         spin_lock(&cifs_tcp_ses_lock);
>         list_add(&ses->smb_ses_list, &server->smb_ses_list);
>         spin_unlock(&cifs_tcp_ses_lock);
> @@ -4918,6 +4929,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
>         cifs_autodisable_serverino(cifs_sb);
>  out:
>         free_xid(xid);
> +       cifs_try_adding_channels(ses);
>         return mount_setup_tlink(cifs_sb, ses, tcon);
>
>  error:
> @@ -5192,21 +5204,23 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
>         int rc = -ENOSYS;
>         struct TCP_Server_Info *server = cifs_ses_server(ses);
>
> -       ses->capabilities = server->capabilities;
> -       if (linuxExtEnabled == 0)
> -               ses->capabilities &= (~server->vals->cap_unix);
> +       if (!ses->binding) {
> +               ses->capabilities = server->capabilities;
> +               if (linuxExtEnabled == 0)
> +                       ses->capabilities &= (~server->vals->cap_unix);
> +
> +               if (ses->auth_key.response) {
> +                       cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
> +                                ses->auth_key.response);
> +                       kfree(ses->auth_key.response);
> +                       ses->auth_key.response = NULL;
> +                       ses->auth_key.len = 0;
> +               }
> +       }
>
>         cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
>                  server->sec_mode, server->capabilities, server->timeAdj);
>
> -       if (ses->auth_key.response) {
> -               cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
> -                        ses->auth_key.response);
> -               kfree(ses->auth_key.response);
> -               ses->auth_key.response = NULL;
> -               ses->auth_key.len = 0;
> -       }
> -
>         if (server->ops->sess_setup)
>                 rc = server->ops->sess_setup(xid, ses, nls_info);
>
> diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
> index 3c1c3999a414..20c73a42755d 100644
> --- a/fs/cifs/sess.c
> +++ b/fs/cifs/sess.c
> @@ -31,6 +31,217 @@
>  #include <linux/utsname.h>
>  #include <linux/slab.h>
>  #include "cifs_spnego.h"
> +#include "smb2proto.h"
> +
> +bool
> +is_server_using_iface(struct TCP_Server_Info *server,
> +                     struct cifs_server_iface *iface)
> +{
> +       struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr;
> +       struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr;
> +       struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr;
> +       struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr;
> +
> +       if (server->dstaddr.ss_family != iface->sockaddr.ss_family)
> +               return false;
> +       if (server->dstaddr.ss_family == AF_INET) {
> +               if (s4->sin_addr.s_addr != i4->sin_addr.s_addr)
> +                       return false;
> +       } else if (server->dstaddr.ss_family == AF_INET6) {
> +               if (memcmp(&s6->sin6_addr, &i6->sin6_addr,
> +                          sizeof(i6->sin6_addr)) != 0)
> +                       return false;
> +       } else {
> +               /* unknown family.. */
> +               return false;
> +       }
> +       return true;
> +}
> +
> +bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
> +{
> +       int i;
> +       for (i = 0; i < ses->chan_count; i++) {
> +               if (is_server_using_iface(ses->chans[i].server, iface))
> +                       return true;
> +       }
> +       return false;
> +}
> +
> +/* returns number of channels added */
> +int cifs_try_adding_channels(struct cifs_ses *ses)
> +{
> +       int old_chan_count = ses->chan_count;
> +       int left = ses->chan_max - ses->chan_count;
> +       int i = 0;
> +       int rc = 0;
> +
> +       if (left <= 0) {
> +               cifs_dbg(FYI,
> +                        "ses already at max_channels (%zu), nothing to open\n",
> +                        ses->chan_max);
> +               return 0;
> +       }
> +
> +       if (ses->server->dialect != SMB311_PROT_ID) {
> +               cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.1.1\n");
> +               return 0;
> +       }
> +
> +       /* ifaces are sorted by speed, try them in order */
> +       for (i = 0; left > 0 && i < ses->iface_count; i++) {
> +               struct cifs_server_iface *iface;
> +
> +               iface = &ses->iface_list[i];
> +               if (is_ses_using_iface(ses, iface) && !iface->rss_capable)
> +                       continue;
> +
> +               rc = cifs_ses_add_channel(ses, iface);
> +               if (rc) {
> +                       cifs_dbg(FYI, "failed to open extra channel\n");
> +                       continue;
> +               }
> +
> +               cifs_dbg(FYI, "sucessfully opened new channel\n");
> +               left--;
> +       }
> +
> +       /*
> +        * TODO: if we still have channels left to open try to connect
> +        * to same RSS-capable iface multiple times
> +        */
> +
> +       return ses->chan_count - old_chan_count;
> +}
> +
> +int
> +cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface)
> +{
> +       struct cifs_chan *chan;
> +       struct smb_vol vol = {0};
> +       const char unc_fmt[] = "\\%s\\foo";
> +       char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0};
> +       struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
> +       struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
> +       int rc;
> +       unsigned int xid = get_xid();
> +
> +       cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ",
> +                ses, iface->speed, iface->rdma_capable ? "yes" : "no");
> +       if (iface->sockaddr.ss_family == AF_INET)
> +               cifs_dbg(FYI, "ip:%pI4)\n", &ipv4->sin_addr);
> +       else
> +               cifs_dbg(FYI, "ip:%pI6)\n", &ipv6->sin6_addr);
> +
> +       /*
> +        * Setup a smb_vol with mostly the same info as the existing
> +        * session and overwrite it with the requested iface data.
> +        *
> +        * We need to setup at least the fields used for negprot and
> +        * sesssetup.
> +        *
> +        * We only need the volume here, so we can reuse memory from
> +        * the session and server without caring about memory
> +        * management.
> +        */
> +
> +       /* Always make new connection for now (TODO?) */
> +       vol.nosharesock = true;
> +
> +       /* Auth */
> +       vol.domainauto = ses->domainAuto;
> +       vol.domainname = ses->domainName;
> +       vol.username = ses->user_name;
> +       vol.password = ses->password;
> +       vol.sectype = ses->sectype;
> +       vol.sign = ses->sign;
> +
> +       /* UNC and paths */
> +       /* XXX: Use ses->server->hostname? */
> +       sprintf(unc, unc_fmt, ses->serverName);
> +       vol.UNC = unc;
> +       vol.prepath = "";
> +
> +       /* Require SMB3.1.1 */
> +       vol.vals = &smb311_values;
> +       vol.ops = &smb311_operations;
> +
> +       vol.noblocksnd = ses->server->noblocksnd;
> +       vol.noautotune = ses->server->noautotune;
> +       vol.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
> +       vol.echo_interval = ses->server->echo_interval / HZ;
> +
> +       /*
> +        * This will be used for encoding/decoding user/domain/pw
> +        * during sess setup auth.
> +        *
> +        * XXX: We use the default for simplicity but the proper way
> +        * would be to use the one that ses used, which is not
> +        * stored. This might break when dealing with non-ascii
> +        * strings.
> +        */
> +       vol.local_nls = load_nls_default();
> +
> +       /* Use RDMA if possible */
> +       vol.rdma = iface->rdma_capable;
> +       memcpy(&vol.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage));
> +
> +       /* reuse master con client guid */
> +       memcpy(&vol.client_guid, ses->server->client_guid, SMB2_CLIENT_GUID_SIZE);
> +       vol.use_client_guid = true;
> +
> +       mutex_lock(&ses->session_mutex);
> +
> +       chan = &ses->chans[ses->chan_count];
> +       chan->server = cifs_get_tcp_session(&vol);
> +       if (IS_ERR(chan->server)) {
> +               rc = PTR_ERR(chan->server);
> +               chan->server = NULL;
> +               goto out;
> +       }
> +
> +       /*
> +        * We need to allocate the server crypto now as we will need
> +        * to sign packets before we generate the channel signing key
> +        * (we sign with the session key)
> +        */
> +       rc = smb311_crypto_shash_allocate(chan->server);
> +       if (rc) {
> +               cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
> +               goto out;
> +       }
> +
> +       ses->binding = true;
> +       rc = cifs_negotiate_protocol(xid, ses);
> +       if (rc)
> +               goto out;
> +
> +       rc = cifs_setup_session(xid, ses, vol.local_nls);
> +       if (rc)
> +               goto out;
> +
> +       /* success, put it on the list
> +        * XXX: sharing ses between 2 tcp server is not possible, the
> +        * way "internal" linked lists works in linux makes element
> +        * only able to belong to one list
> +        *
> +        * the binding session is already established so the rest of
> +        * the code should be able to look it up, no need to add the
> +        * ses to the new server.
> +        */
> +
> +       ses->chan_count++;
> +
> +out:
> +       ses->binding = false;
> +       mutex_unlock(&ses->session_mutex);
> +
> +       if (rc && chan->server)
> +               cifs_put_tcp_session(chan->server, 0);
> +       unload_nls(vol.local_nls);
> +
> +       return rc;
> +}
>
>  static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
>  {
> diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
> index e311f58dc1c8..a95ed951c67f 100644
> --- a/fs/cifs/smb2misc.c
> +++ b/fs/cifs/smb2misc.c
> @@ -29,6 +29,7 @@
>  #include "cifs_unicode.h"
>  #include "smb2status.h"
>  #include "smb2glob.h"
> +#include "nterr.h"
>
>  static int
>  check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid)
> @@ -788,23 +789,37 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
>         int i, rc;
>         struct sdesc *d;
>         struct smb2_sync_hdr *hdr;
> +       struct TCP_Server_Info *server = cifs_ses_server(ses);
>
> -       if (ses->server->tcpStatus == CifsGood) {
> -               /* skip non smb311 connections */
> -               if (ses->server->dialect != SMB311_PROT_ID)
> -                       return 0;
> +       hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
> +       /* neg prot are always taken */
> +       if (hdr->Command == SMB2_NEGOTIATE)
> +               goto ok;
>
> -               /* skip last sess setup response */
> -               hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
> -               if (hdr->Flags & SMB2_FLAGS_SIGNED)
> -                       return 0;
> -       }
> +       /*
> +        * If we process a command which wasn't a negprot it means the
> +        * neg prot was already done, so the server dialect was set
> +        * and we can test it. Preauth requires 3.1.1 for now.
> +        */
> +       if (server->dialect != SMB311_PROT_ID)
> +               return 0;
> +
> +       if (hdr->Command != SMB2_SESSION_SETUP)
> +               return 0;
> +
> +       /* skip last sess setup response */
> +       if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
> +           && (hdr->Status == NT_STATUS_OK
> +               || (hdr->Status !=
> +                   cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))))
> +               return 0;
>
> -       rc = smb311_crypto_shash_allocate(ses->server);
> +ok:
> +       rc = smb311_crypto_shash_allocate(server);
>         if (rc)
>                 return rc;
>
> -       d = ses->server->secmech.sdescsha512;
> +       d = server->secmech.sdescsha512;
>         rc = crypto_shash_init(&d->shash);
>         if (rc) {
>                 cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__);
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 9637f6e1d3ba..8bcb278fdb0a 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -1177,13 +1177,18 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
>         if (rc)
>                 return rc;
>
> -       /* First session, not a reauthenticate */
> -       req->sync_hdr.SessionId = 0;
> -
> -       /* if reconnect, we need to send previous sess id, otherwise it is 0 */
> -       req->PreviousSessionId = sess_data->previous_session;
> -
> -       req->Flags = 0; /* MBZ */
> +       if (sess_data->ses->binding) {
> +               req->sync_hdr.SessionId = sess_data->ses->Suid;
> +               req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
> +               req->PreviousSessionId = 0;
> +               req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
> +       } else {
> +               /* First session, not a reauthenticate */
> +               req->sync_hdr.SessionId = 0;
> +               /* if reconnect, we need to send previous sess id, otherwise it is 0 */
> +               req->PreviousSessionId = sess_data->previous_session;
> +               req->Flags = 0; /* MBZ */
> +       }
>
>         /* enough to enable echos and oplocks and one max size write */
>         req->sync_hdr.CreditRequest = cpu_to_le16(130);
> @@ -1275,10 +1280,14 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
>         mutex_unlock(&server->srv_mutex);
>
>         cifs_dbg(FYI, "SMB2/3 session established successfully\n");
> -       spin_lock(&GlobalMid_Lock);
> -       ses->status = CifsGood;
> -       ses->need_reconnect = false;
> -       spin_unlock(&GlobalMid_Lock);
> +       /* keep exising ses state if binding */
> +       if (!ses->binding) {
> +               spin_lock(&GlobalMid_Lock);
> +               ses->status = CifsGood;
> +               ses->need_reconnect = false;
> +               spin_unlock(&GlobalMid_Lock);
> +       }
> +
>         return rc;
>  }
>
> @@ -1316,16 +1325,19 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
>                 goto out_put_spnego_key;
>         }
>
> -       ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
> -                                        GFP_KERNEL);
> -       if (!ses->auth_key.response) {
> -               cifs_dbg(VFS,
> -                       "Kerberos can't allocate (%u bytes) memory",
> -                       msg->sesskey_len);
> -               rc = -ENOMEM;
> -               goto out_put_spnego_key;
> +       /* keep session key if binding */
> +       if (!ses->binding) {
> +               ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
> +                                                GFP_KERNEL);
> +               if (!ses->auth_key.response) {
> +                       cifs_dbg(VFS,
> +                                "Kerberos can't allocate (%u bytes) memory",
> +                                msg->sesskey_len);
> +                       rc = -ENOMEM;
> +                       goto out_put_spnego_key;
> +               }
> +               ses->auth_key.len = msg->sesskey_len;
>         }
> -       ses->auth_key.len = msg->sesskey_len;
>
>         sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
>         sess_data->iov[1].iov_len = msg->secblob_len;
> @@ -1335,9 +1347,11 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
>                 goto out_put_spnego_key;
>
>         rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
> -       ses->Suid = rsp->sync_hdr.SessionId;
> -
> -       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       /* keep session id and flags if binding */
> +       if (!ses->binding) {
> +               ses->Suid = rsp->sync_hdr.SessionId;
> +               ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       }
>
>         rc = SMB2_sess_establish_session(sess_data);
>  out_put_spnego_key:
> @@ -1431,9 +1445,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
>
>         cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
>
> -
> -       ses->Suid = rsp->sync_hdr.SessionId;
> -       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       /* keep existing ses id and flags if binding */
> +       if (!ses->binding) {
> +               ses->Suid = rsp->sync_hdr.SessionId;
> +               ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       }
>
>  out:
>         kfree(ntlmssp_blob);
> @@ -1490,8 +1506,11 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
>
>         rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
>
> -       ses->Suid = rsp->sync_hdr.SessionId;
> -       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       /* keep existing ses id and flags if binding */
> +       if (!ses->binding) {
> +               ses->Suid = rsp->sync_hdr.SessionId;
> +               ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       }
>
>         rc = SMB2_sess_establish_session(sess_data);
>  out:
> diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
> index d7d89741f3f0..12988df6b24d 100644
> --- a/fs/cifs/smb2transport.c
> +++ b/fs/cifs/smb2transport.c
> @@ -48,7 +48,7 @@ smb2_crypto_shash_allocate(struct TCP_Server_Info *server)
>                                &server->secmech.sdeschmacsha256);
>  }
>
> -static int
> +int
>  smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
>  {
>         struct cifs_secmech *p = &server->secmech;
> @@ -98,6 +98,44 @@ smb311_crypto_shash_allocate(struct TCP_Server_Info *server)
>         return rc;
>  }
>
> +u8 *smb2_find_chan_signkey(struct cifs_ses *ses, struct TCP_Server_Info *server)
> +{
> +       int i;
> +       struct cifs_chan *chan;
> +       int count;
> +       spin_lock(&cifs_tcp_ses_lock);
> +       count = ses->chan_count;
> +       if (ses->binding)
> +               count++;
> +       for (i = 0; i < count; i++) {
> +               chan = ses->chans + i;
> +               if (chan->server == server) {
> +                       spin_unlock(&cifs_tcp_ses_lock);
> +                       return chan->signkey;
> +               }
> +       }
> +       spin_unlock(&cifs_tcp_ses_lock);
> +       return NULL;
> +}
> +
> +struct cifs_ses *
> +smb2_find_global_smb_ses(__u64 ses_id)
> +{
> +       struct TCP_Server_Info *server;
> +       struct cifs_ses *ses;
> +       spin_lock(&cifs_tcp_ses_lock);
> +       list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
> +               list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
> +                       if (ses->Suid == ses_id) {
> +                               spin_unlock(&cifs_tcp_ses_lock);
> +                               return ses;
> +                       }
> +               }
> +       }
> +       spin_unlock(&cifs_tcp_ses_lock);
> +       return NULL;
> +}
> +
>  static struct cifs_ses *
>  smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
>  {
> @@ -328,21 +366,33 @@ generate_smb3signingkey(struct cifs_ses *ses,
>  {
>         int rc;
>
> -       rc = generate_key(ses, ptriplet->signing.label,
> -                         ptriplet->signing.context, ses->smb3signingkey,
> -                         SMB3_SIGN_KEY_SIZE);
> -       if (rc)
> -               return rc;
> -
> -       rc = generate_key(ses, ptriplet->encryption.label,
> -                         ptriplet->encryption.context, ses->smb3encryptionkey,
> -                         SMB3_SIGN_KEY_SIZE);
> -       if (rc)
> -               return rc;
> -
> -       rc = generate_key(ses, ptriplet->decryption.label,
> -                         ptriplet->decryption.context,
> -                         ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
> +       if (ses->binding) {
> +               struct TCP_Server_Info *server;
> +               server = cifs_ses_server(ses);
> +               rc = generate_key(ses, ptriplet->signing.label,
> +                                 ptriplet->signing.context,
> +                                 smb2_find_chan_signkey(ses, server),
> +                                 SMB3_SIGN_KEY_SIZE);
> +               if (rc)
> +                       return rc;
> +       } else {
> +               rc = generate_key(ses, ptriplet->signing.label,
> +                                 ptriplet->signing.context,
> +                                 ses->smb3signingkey,
> +                                 SMB3_SIGN_KEY_SIZE);
> +               if (rc)
> +                       return rc;
> +               rc = generate_key(ses, ptriplet->encryption.label,
> +                                 ptriplet->encryption.context,
> +                                 ses->smb3encryptionkey,
> +                                 SMB3_SIGN_KEY_SIZE);
> +               rc = generate_key(ses, ptriplet->decryption.label,
> +                                 ptriplet->decryption.context,
> +                                 ses->smb3decryptionkey,
> +                                 SMB3_SIGN_KEY_SIZE);
> +               if (rc)
> +                       return rc;
> +       }
>
>         if (rc)
>                 return rc;
> @@ -434,18 +484,35 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
>         struct cifs_ses *ses;
>         struct shash_desc *shash = &server->secmech.sdesccmacaes->shash;
>         struct smb_rqst drqst;
> +       u8 *key;
>
> -       ses = smb2_find_smb_ses(server, shdr->SessionId);
> +       ses = smb2_find_global_smb_ses(shdr->SessionId);
>         if (!ses) {
>                 cifs_server_dbg(VFS, "%s: Could not find session\n", __func__);
>                 return 0;
>         }
>
> +       /*
> +        * If we are binding an existing session use the session key,
> +        * otherwise use the channel key
> +        */
> +       if (ses->binding)
> +               key = ses->smb3signingkey;
> +       else {
> +               key = smb2_find_chan_signkey(ses, server);
> +               if (!key) {
> +                       cifs_dbg(VFS,
> +                                "%s: Could not find channel signing key\n",
> +                                __func__);
> +                       return 0;
> +               }
> +        }
> +
>         memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
>         memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
>
>         rc = crypto_shash_setkey(server->secmech.cmacaes,
> -                                ses->smb3signingkey, SMB2_CMACAES_SIZE);
> +                                key, SMB2_CMACAES_SIZE);
>         if (rc) {
>                 cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
>                 return rc;
> @@ -494,16 +561,25 @@ static int
>  smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
>  {
>         int rc = 0;
> -       struct smb2_sync_hdr *shdr =
> -                       (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
> +       struct smb2_sync_hdr *shdr;
> +       struct smb2_sess_setup_req *ssr;
> +       bool is_binding;
> +       bool is_signed;
>
> -       if (!(shdr->Flags & SMB2_FLAGS_SIGNED) ||
> -           server->tcpStatus == CifsNeedNegotiate)
> -               return rc;
> +       shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
> +       ssr = (struct smb2_sess_setup_req *)shdr;
>
> -       if (!server->session_estab) {
> +       is_binding = shdr->Command == SMB2_SESSION_SETUP &&
> +               (ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING);
> +       is_signed = shdr->Flags & SMB2_FLAGS_SIGNED;
> +
> +       if (!is_signed)
> +               return 0;
> +       if (server->tcpStatus == CifsNeedNegotiate)
> +               return 0;
> +       if (!is_binding && !server->session_estab) {
>                 strncpy(shdr->Signature, "BSRSPYL", 8);
> -               return rc;
> +               return 0;
>         }
>
>         rc = server->ops->calc_signature(rqst, server);
> diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
> index afe66f9cb15e..39e6b47a77d9 100644
> --- a/fs/cifs/transport.c
> +++ b/fs/cifs/transport.c
> @@ -995,7 +995,15 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
>                 return -EIO;
>         }
>
> -       server = ses->server;
> +       if (!ses->binding) {
> +               uint index = 0;
> +               if (ses->chan_count > 1)
> +                       index = ((unsigned)get_random_int()) % ses->chan_count;
> +               server = ses->chans[index].server;
> +       } else {
> +               server = cifs_ses_server(ses);
> +       }
> +
>         if (server->tcpStatus == CifsExiting)
>                 return -ENOENT;
>
> --
> 2.16.4
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, back to index

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-20  5:01 [PATCH v4 0/6] multichannel Aurelien Aptel
2019-09-20  5:01 ` [PATCH v4 1/6] cifs: add multichannel mount options and data structs Aurelien Aptel
2019-09-20  5:01 ` [PATCH v4 2/6] cifs: add server param Aurelien Aptel
2019-09-20  5:01 ` [PATCH v4 3/6] cifs: switch servers depending on binding state Aurelien Aptel
2019-09-20  5:01 ` [PATCH v4 4/6] cifs: sort interface list by speed Aurelien Aptel
2019-09-20  5:01 ` [PATCH v4 5/6] cifs: try opening channels after mounting Aurelien Aptel
2019-09-20  7:30   ` Steve French
2019-09-20  5:01 ` [PATCH v4 6/6] cifs: mention if an interface has a channel connected to it Aurelien Aptel

Linux-CIFS Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-cifs/0 linux-cifs/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-cifs linux-cifs/ https://lore.kernel.org/linux-cifs \
		linux-cifs@vger.kernel.org linux-cifs@archiver.kernel.org
	public-inbox-index linux-cifs

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-cifs


AGPL code for this site: git clone https://public-inbox.org/ public-inbox