lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250806203705.2560493-19-dhowells@redhat.com>
Date: Wed,  6 Aug 2025 21:36:39 +0100
From: David Howells <dhowells@...hat.com>
To: Steve French <sfrench@...ba.org>
Cc: David Howells <dhowells@...hat.com>,
	Paulo Alcantara <pc@...guebit.org>,
	Shyam Prasad N <sprasad@...rosoft.com>,
	Tom Talpey <tom@...pey.com>,
	Wang Zhaolong <wangzhaolong@...weicloud.com>,
	Stefan Metzmacher <metze@...ba.org>,
	Mina Almasry <almasrymina@...gle.com>,
	linux-cifs@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	netfs@...ts.linux.dev,
	linux-fsdevel@...r.kernel.org
Subject: [RFC PATCH 18/31] cifs: Pass smb_message structs down into the transport layer

Institute the creation of smb_message structs at the layer above the
transport layer and pass them down into the transport layer.  This replaces
the handling of mid_q_structs entirely within the transport layer.

This includes the following changes:

 (1) The smb_rqst struct is partially absorbed into the smb_message struct.
     Because the latter is on a linked list, the fixed-size arrays of
     requests can be got rid of - along with the COMPOUND_MAX-1 limit that
     gets imposed by the encryption code because it needs to steal a slot
     for the transform header.

 (2) Hand smb_message structs into the message sending code in the
     transport rather than allocating them (as it did for mid_q_structs) in
     the ->setup_request/->setup_async_request functions.

     For the moment compound_send_rcv() does the generation of smb_message
     structs, stringing them together before passing them down.

Signed-off-by: David Howells <dhowells@...hat.com>
cc: Steve French <sfrench@...ba.org>
cc: Paulo Alcantara <pc@...guebit.org>
cc: Shyam Prasad N <sprasad@...rosoft.com>
cc: Tom Talpey <tom@...pey.com>
cc: linux-cifs@...r.kernel.org
cc: netfs@...ts.linux.dev
cc: linux-fsdevel@...r.kernel.org
---
 fs/smb/client/cifsglob.h      |  38 ++--
 fs/smb/client/cifsproto.h     |  39 ++--
 fs/smb/client/cifssmb.c       |   1 -
 fs/smb/client/cifstransport.c |   8 +-
 fs/smb/client/compress.h      |   6 +-
 fs/smb/client/connect.c       |  24 +--
 fs/smb/client/netmisc.c       |   5 +-
 fs/smb/client/smb1ops.c       |  19 +-
 fs/smb/client/smb2misc.c      |  45 ++---
 fs/smb/client/smb2ops.c       |  40 +++--
 fs/smb/client/smb2pdu.c       | 212 +++++++++++++---------
 fs/smb/client/smb2proto.h     |  31 ++--
 fs/smb/client/smb2transport.c | 183 +++++++------------
 fs/smb/client/transport.c     | 325 +++++++++++++++++++---------------
 14 files changed, 501 insertions(+), 475 deletions(-)

diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 1eed8a463b58..4173b87bdf0f 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -313,26 +313,24 @@ struct cifs_credits;
 
 struct smb_version_operations {
 	int (*send_cancel)(struct cifs_ses *ses, struct TCP_Server_Info *server,
-			   struct smb_rqst *rqst, struct smb_message *smb,
-			   unsigned int xid);
+			   struct smb_message *smb, unsigned int xid);
 	bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
-	/* setup request: allocate mid, sign message */
-	struct smb_message *(*setup_request)(struct cifs_ses *,
-					     struct TCP_Server_Info *,
-					     struct smb_rqst *);
+	/* setup request: set up mid, sign message */
+	int (*setup_request)(struct cifs_ses *ses,
+			     struct TCP_Server_Info *server,
+			     struct smb_message *smb);
 	/* setup async request: allocate mid, sign message */
-	struct smb_message *(*setup_async_request)(struct TCP_Server_Info *,
-						   struct smb_rqst *);
+	int (*setup_async_request)(struct TCP_Server_Info *server, struct smb_message *smb);
 	/* check response: verify signature, map error */
-	int (*check_receive)(struct smb_message *, struct TCP_Server_Info *,
-			     bool);
+	int (*check_receive)(struct smb_message *smb, struct TCP_Server_Info *server,
+			     bool log_error);
 	void (*add_credits)(struct TCP_Server_Info *server,
 			    struct cifs_credits *credits,
 			    const int optype);
 	void (*set_credits)(struct TCP_Server_Info *, const int);
 	int * (*get_credits_field)(struct TCP_Server_Info *, const int);
 	unsigned int (*get_credits)(struct smb_message *smb);
-	__u64 (*get_next_mid)(struct TCP_Server_Info *);
+	__u64 (*get_next_mid)(struct TCP_Server_Info *server, unsigned int count);
 	void (*revert_current_mid)(struct TCP_Server_Info *server,
 				   const unsigned int val);
 	/* data offset from read response message */
@@ -537,8 +535,8 @@ struct smb_version_operations {
 	void (*new_lease_key)(struct cifs_fid *);
 	int (*generate_signingkey)(struct cifs_ses *ses,
 				   struct TCP_Server_Info *server);
-	int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *,
-				bool allocate_crypto);
+	int (*calc_signature)(struct smb_message *smb, struct TCP_Server_Info *server,
+			      bool allocate_crypto);
 	int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
 			     struct cifsFileInfo *src_file);
 	int (*enum_snapshots)(const unsigned int xid, struct cifs_tcon *tcon,
@@ -594,10 +592,8 @@ struct smb_version_operations {
 	long (*fallocate)(struct file *, struct cifs_tcon *, int, loff_t,
 			  loff_t);
 	/* init transform (compress/encrypt) request */
-	int (*init_transform_rq)(struct TCP_Server_Info *server,
-				 int num_rqst, const struct smb_rqst *rqst,
-				 struct smb2_transform_hdr *tr_hdr,
-				 struct iov_iter *iter);
+	int (*init_transform_rq)(struct TCP_Server_Info *server, struct smb_message *head_smb,
+				 struct smb2_transform_hdr *tr_hdr, struct iov_iter *iter);
 	int (*is_transform_hdr)(void *buf);
 	int (*receive_transform)(struct TCP_Server_Info *,
 				 struct smb_message **smb, char **, int *);
@@ -945,16 +941,16 @@ adjust_credits(struct TCP_Server_Info *server, struct cifs_io_subrequest *subreq
 		server->ops->adjust_credits(server, subreq, trace) : 0;
 }
 
-static inline __le64
-get_next_mid64(struct TCP_Server_Info *server)
+static inline __u64
+get_next_mid64(struct TCP_Server_Info *server, unsigned int count)
 {
-	return cpu_to_le64(server->ops->get_next_mid(server));
+	return server->ops->get_next_mid(server, count);
 }
 
 static inline __le16
 get_next_mid(struct TCP_Server_Info *server)
 {
-	__u16 mid = server->ops->get_next_mid(server);
+	__u16 mid = server->ops->get_next_mid(server, 1);
 	/*
 	 * The value in the SMB header should be little endian for easy
 	 * on-the-wire decoding.
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index 76cb047b2715..fd5fe2723b4a 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -83,8 +83,6 @@ extern char *cifs_build_path_to_root(struct smb3_fs_context *ctx,
 				     int add_treename);
 extern char *build_wildcard_path_from_dentry(struct dentry *direntry);
 char *cifs_build_devname(char *nodename, const char *prepath);
-extern void delete_mid(struct smb_message *smb);
-void __release_mid(struct smb_message *smb);
 extern void cifs_wake_up_task(struct smb_message *smb);
 extern int cifs_handle_standard(struct TCP_Server_Info *server,
 				struct smb_message *smb);
@@ -96,15 +94,16 @@ extern int cifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs);
 extern bool cifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs);
 extern int cifs_discard_remaining_data(struct TCP_Server_Info *server);
 extern int cifs_call_async(struct TCP_Server_Info *server,
-			struct smb_rqst *rqst,
-			mid_receive_t receive, mid_callback_t callback,
-			mid_handle_t handle, void *cbdata, const int flags,
-			const struct cifs_credits *exist_credits);
+			   struct smb_message *msg, const int flags,
+			   const struct cifs_credits *exist_credits);
 extern struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses);
 extern int cifs_send_recv(const unsigned int xid, struct cifs_ses *ses,
 			  struct TCP_Server_Info *server,
 			  struct smb_rqst *rqst, int *resp_buf_type,
 			  const int flags, struct kvec *resp_iov);
+int smb_send_recv_messages(const unsigned int xid, struct cifs_ses *ses,
+			   struct TCP_Server_Info *server,
+			   struct smb_message *smb, const int flags);
 extern int compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
 			      struct TCP_Server_Info *server,
 			      const int flags, const int num_rqst,
@@ -117,14 +116,14 @@ 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);
 int cifs_sync_mid_result(struct smb_message *smb, struct TCP_Server_Info *server);
-extern struct smb_message *cifs_setup_request(struct cifs_ses *,
-				struct TCP_Server_Info *,
-				struct smb_rqst *);
-extern struct smb_message *cifs_setup_async_request(struct TCP_Server_Info *,
-						struct smb_rqst *);
+int cifs_setup_request(struct cifs_ses *ses,
+		       struct TCP_Server_Info *server,
+		       struct smb_message *smb);
+int cifs_setup_async_request(struct TCP_Server_Info *server,
+			     struct smb_message *smb);
 int __smb_send_rqst(struct TCP_Server_Info *server, struct iov_iter *iter);
-extern int cifs_check_receive(struct smb_message *msg,
-			struct TCP_Server_Info *server, bool log_error);
+int cifs_check_receive(struct smb_message *smb,
+		       struct TCP_Server_Info *server, bool log_error);
 int wait_for_free_request(struct TCP_Server_Info *server, const int flags,
 			  unsigned int *instance);
 extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
@@ -133,11 +132,10 @@ extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
 
 static inline int
 send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
-	    struct smb_rqst *rqst, struct smb_message *smb,
-	    unsigned int xid)
+	    struct smb_message *smb, unsigned int xid)
 {
 	return server->ops->send_cancel ?
-		server->ops->send_cancel(ses, server, rqst, smb, xid) : 0;
+		server->ops->send_cancel(ses, server, smb, xid) : 0;
 }
 
 int wait_for_response(struct TCP_Server_Info *server, struct smb_message *smb);
@@ -181,7 +179,8 @@ extern int decode_negTokenInit(unsigned char *security_blob, int length,
 extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
 extern void cifs_set_port(struct sockaddr *addr, const unsigned short int port);
 extern int map_smb_to_linux_error(char *buf, bool logErr);
-extern int map_and_check_smb_error(struct smb_message *smb, bool logErr);
+extern int map_and_check_smb_error(struct TCP_Server_Info *server,
+				   struct smb_message *smb, bool logErr);
 extern void header_assemble(struct smb_hdr *, char /* command */ ,
 			    const struct cifs_tcon *, int /* length of
 			    fixed section (word count) in two byte units */);
@@ -754,12 +753,6 @@ static inline bool dfs_src_pathname_equal(const char *s1, const char *s2)
 	return true;
 }
 
-static inline void release_mid(struct smb_message *smb)
-{
-	if (refcount_dec_and_test(&smb->ref))
-		__release_mid(smb);
-}
-
 static inline void cifs_free_open_info(struct cifs_open_info_data *data)
 {
 	kfree(data->symlink_target);
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index a7a9f63f8c21..789ce76e3154 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -582,7 +582,6 @@ cifs_echo_callback(struct smb_message *smb)
 	struct TCP_Server_Info *server = smb->callback_data;
 	struct cifs_credits credits = { .value = 1, .instance = 0 };
 
-	release_mid(smb);
 	add_credits(server, &credits, CIFS_ECHO_OP);
 }
 
diff --git a/fs/smb/client/cifstransport.c b/fs/smb/client/cifstransport.c
index a2db95faeb17..8cdb9252a37e 100644
--- a/fs/smb/client/cifstransport.c
+++ b/fs/smb/client/cifstransport.c
@@ -101,8 +101,8 @@ static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
 	return 0;
 }
 
-struct smb_message *
-cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+int
+cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_message *smb)
 {
 	int rc;
 	struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
@@ -181,9 +181,9 @@ cifs_check_receive(struct smb_message *smb, struct TCP_Server_Info *server,
 	return map_and_check_smb_error(smb, log_error);
 }
 
-struct smb_message *
+int
 cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored,
-		   struct smb_rqst *rqst)
+		   struct smb_message *smb)
 {
 	int rc;
 	struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
diff --git a/fs/smb/client/compress.h b/fs/smb/client/compress.h
index f3ed1d3e52fb..472b0e8b2f45 100644
--- a/fs/smb/client/compress.h
+++ b/fs/smb/client/compress.h
@@ -35,7 +35,7 @@ int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_s
  * should_compress() - Determines if a request (write) or the response to a
  *		       request (read) should be compressed.
  * @tcon: tcon of the request is being sent to
- * @rqst: request to evaluate
+ * @smb: request to evaluate
  *
  * Return: true iff:
  * - compression was successfully negotiated with server
@@ -46,7 +46,7 @@ int smb_compress(struct TCP_Server_Info *server, struct smb_rqst *rq, compress_s
  *
  * Return false otherwise.
  */
-bool should_compress(const struct cifs_tcon *tcon, const struct smb_rqst *rq);
+bool should_compress(const struct cifs_tcon *tcon, const struct smb_message *smb);
 
 /**
  * smb_compress_alg_valid() - Validate a compression algorithm.
@@ -77,7 +77,7 @@ static inline int smb_compress(void *unused1, void *unused2, void *unused3)
 	return -EOPNOTSUPP;
 }
 
-static inline bool should_compress(void *unused1, void *unused2)
+static inline bool should_compress(const struct cifs_tcon *tcon, const struct smb_message *smb)
 {
 	return false;
 }
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index d26e2a6d7674..7a64b070f74b 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -323,7 +323,6 @@ cifs_abort_connection(struct TCP_Server_Info *server)
 	cifs_dbg(FYI, "%s: moving mids to private list\n", __func__);
 	spin_lock(&server->mid_lock);
 	list_for_each_entry_safe(smb, nsmb, &server->pending_mid_q, qhead) {
-		smb_get_message(smb);
 		if (smb->mid_state == MID_REQUEST_SUBMITTED)
 			smb->mid_state = MID_RETRY_NEEDED;
 		list_move(&smb->qhead, &retry_list);
@@ -336,7 +335,7 @@ cifs_abort_connection(struct TCP_Server_Info *server)
 	list_for_each_entry_safe(smb, nsmb, &retry_list, qhead) {
 		list_del_init(&smb->qhead);
 		smb->callback(smb);
-		release_mid(smb);
+		smb_put_message(smb);
 	}
 
 	if (cifs_rdma_enabled(server)) {
@@ -871,9 +870,9 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
 		    server->tcpStatus == CifsInNegotiate &&
 		    !server->with_rfc1001 &&
 		    server->rfc1001_sessinit != 0) {
-			int rc, mid_rc;
 			struct smb_message *smb, *nsmb;
 			LIST_HEAD(dispose_list);
+			int rc, mid_rc;
 
 			cifs_dbg(FYI, "RFC 1002 negative session response during SMB Negotiate, retrying with NetBIOS session\n");
 
@@ -886,7 +885,6 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
 			 */
 			spin_lock(&server->mid_lock);
 			list_for_each_entry_safe(smb, nsmb, &server->pending_mid_q, qhead) {
-				smb_get_message(smb);
 				list_move(&smb->qhead, &dispose_list);
 				smb->mid_flags |= MID_DELETED;
 			}
@@ -920,7 +918,7 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
 				smb->mid_rc = mid_rc;
 				smb->mid_state = MID_RC;
 				smb->callback(smb);
-				release_mid(smb);
+				smb_put_message(smb);
 			}
 
 			/*
@@ -1097,15 +1095,12 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
 	}
 
 	if (!list_empty(&server->pending_mid_q)) {
-		struct smb_message *smb;
-		struct list_head *tmp, *tmp2;
+		struct smb_message *smb, *smb2;
 		LIST_HEAD(dispose_list);
 
 		spin_lock(&server->mid_lock);
-		list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
-			smb = list_entry(tmp, struct smb_message, qhead);
+		list_for_each_entry_safe(smb, smb2, &server->pending_mid_q, qhead) {
 			cifs_dbg(FYI, "Clearing mid %llu\n", smb->mid);
-			smb_get_message(smb);
 			smb->mid_state = MID_SHUTDOWN;
 			list_move(&smb->qhead, &dispose_list);
 			smb->mid_flags |= MID_DELETED;
@@ -1113,12 +1108,11 @@ clean_demultiplex_info(struct TCP_Server_Info *server)
 		spin_unlock(&server->mid_lock);
 
 		/* now walk dispose list and issue callbacks */
-		list_for_each_safe(tmp, tmp2, &dispose_list) {
-			smb = list_entry(tmp, struct smb_message, qhead);
+		list_for_each_entry_safe(smb, smb2, &dispose_list, qhead) {
 			cifs_dbg(FYI, "Callback mid %llu\n", smb->mid);
 			list_del_init(&smb->qhead);
 			smb->callback(smb);
-			release_mid(smb);
+			smb_put_message(smb);
 		}
 		/* 1/8th of sec is more than enough time for them to exit */
 		msleep(125);
@@ -1361,7 +1355,7 @@ cifs_demultiplex_thread(void *p)
 		if (length < 0) {
 			for (i = 0; i < num_smbs; i++)
 				if (smbs[i])
-					release_mid(smbs[i]);
+					smb_put_message(smbs[i]);
 			continue;
 		}
 
@@ -1396,7 +1390,7 @@ cifs_demultiplex_thread(void *p)
 				if (!smbs[i]->multiRsp || smbs[i]->multiEnd)
 					smbs[i]->callback(smbs[i]);
 
-				release_mid(smbs[i]);
+				smb_put_message(smbs[i]);
 			} else if (server->ops->is_oplock_break &&
 				   server->ops->is_oplock_break(bufs[i],
 								server)) {
diff --git a/fs/smb/client/netmisc.c b/fs/smb/client/netmisc.c
index b34d2b91cf5a..89bd1ca9e3ce 100644
--- a/fs/smb/client/netmisc.c
+++ b/fs/smb/client/netmisc.c
@@ -889,7 +889,8 @@ map_smb_to_linux_error(char *buf, bool logErr)
 }
 
 int
-map_and_check_smb_error(struct smb_message *smb, bool logErr)
+map_and_check_smb_error(struct TCP_Server_Info *server,
+			struct smb_message *smb, bool logErr)
 {
 	int rc;
 	struct smb_hdr *rhdr = (struct smb_hdr *)smb->resp_buf;
@@ -904,7 +905,7 @@ map_and_check_smb_error(struct smb_message *smb, bool logErr)
 		if (class == ERRSRV && code == ERRbaduid) {
 			cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n",
 				code);
-			cifs_signal_cifsd_for_reconnect(smb->server, false);
+			cifs_signal_cifsd_for_reconnect(server, false);
 		}
 	}
 
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index 185210b7fd03..f179f1b4c0c1 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -29,11 +29,10 @@
  */
 static int
 send_nt_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
-	       struct smb_rqst *rqst, struct smb_message *smb,
-	       unsigned int xid)
+	       struct smb_message *smb, unsigned int xid)
 {
 	struct iov_iter iter;
-	struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+	struct smb_hdr *in_buf = (struct smb_hdr *)smb->rqst.rq_iov[0].iov_base;
 	struct kvec iov[1];
 	struct smb_rqst crqst = { .rq_iov = iov, .rq_nvec = 1 };
 	int rc = 0;
@@ -80,10 +79,9 @@ send_nt_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
  */
 static int
 send_lock_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
-		 struct smb_rqst *rqst, struct smb_message *smb,
-		 unsigned int xid)
+		 struct smb_message *smb, unsigned int xid)
 {
-	struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
+	struct smb_hdr *in_buf = (struct smb_hdr *)smb->rqst.rq_iov[0].iov_base;
 	LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
 	int rc;
 
@@ -104,12 +102,11 @@ send_lock_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
 }
 
 static int cifs_send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
-			    struct smb_rqst *rqst, struct smb_message *smb,
-			    unsigned int xid)
+			    struct smb_message *smb, unsigned int xid)
 {
 	if (smb->sr_flags & CIFS_WINDOWS_LOCK)
-		return send_lock_cancel(ses, server, rqst, smb, xid);
-	return send_nt_cancel(ses, server, rqst, smb, xid);
+		return send_lock_cancel(ses, server, smb, xid);
+	return send_nt_cancel(ses, server, smb, xid);
 }
 
 static bool
@@ -210,7 +207,7 @@ cifs_get_credits(struct smb_message *smb)
  * so many threads being in the vfs at one time.
  */
 static __u64
-cifs_get_next_mid(struct TCP_Server_Info *server)
+cifs_get_next_mid(struct TCP_Server_Info *server, unsigned int count)
 {
 	__u64 mid = 0;
 	__u16 last_mid, cur_mid;
diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c
index b8e3fe26d658..122e9fe78e23 100644
--- a/fs/smb/client/smb2misc.c
+++ b/fs/smb/client/smb2misc.c
@@ -866,27 +866,22 @@ smb2_handle_cancelled_mid(struct smb_message *smb, struct TCP_Server_Info *serve
 }
 
 /**
- * smb311_update_preauth_hash - update @ses hash with the packet data in @iov
- *
- * Assumes @iov does not contain the rfc1002 length and iov[0] has the
- * SMB2 header.
- *
+ * smb311_update_preauth_hash - update @ses hash from the message
  * @ses:	server session structure
  * @server:	pointer to server info
- * @iov:	array containing the SMB request we will send to the server
- * @nvec:	number of array entries for the iov
+ * @smb:	the SMB request we will send to the server
+ * @hash_resp:	T if we're hashing a response
  */
 int
 smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server,
-			   struct kvec *iov, int nvec)
+			   struct smb_message *smb, bool hash_resp)
 {
-	int i, rc;
-	struct smb2_hdr *hdr;
 	struct shash_desc *sha512 = NULL;
+	struct kvec *iov;
+	int i, rc, ioc;
 
-	hdr = (struct smb2_hdr *)iov[0].iov_base;
 	/* neg prot are always taken */
-	if (le16_to_cpu(hdr->Command) == SMB2_NEGOTIATE)
+	if (smb->command_id == SMB2_NEGOTIATE)
 		goto ok;
 
 	/*
@@ -897,15 +892,16 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server,
 	if (server->dialect != SMB311_PROT_ID)
 		return 0;
 
-	if (le16_to_cpu(hdr->Command) != SMB2_SESSION_SETUP)
+	if (smb->command_id != 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;
+	if (hash_resp) {
+		struct smb2_hdr *resp = smb->resp_iov->iov_base;
+		if (resp->Status == NT_STATUS_OK ||
+		    resp->Status != cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
+			return 0;
+	}
 
 ok:
 	rc = smb311_crypto_shash_allocate(server);
@@ -926,8 +922,17 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server,
 		return rc;
 	}
 
-	for (i = 0; i < nvec; i++) {
-		rc = crypto_shash_update(sha512, iov[i].iov_base, iov[i].iov_len);
+	if (hash_resp) {
+		ioc = 1;
+		iov = smb->resp_iov;
+	} else {
+		ioc = smb->rqst.rq_nvec;
+		iov = smb->rqst.rq_iov;
+	}
+
+	for (i = 0; i < ioc; i++) {
+		rc = crypto_shash_update(sha512, iov[i].iov_base,
+					 iov[i].iov_len);
 		if (rc) {
 			cifs_dbg(VFS, "%s: Could not update sha512 shash\n",
 				 __func__);
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 5933757d0170..835a76169d40 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -370,12 +370,13 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
 }
 
 static __u64
-smb2_get_next_mid(struct TCP_Server_Info *server)
+smb2_get_next_mid(struct TCP_Server_Info *server, unsigned int count)
 {
 	__u64 mid;
 	/* for SMB2 we need the current value */
 	spin_lock(&server->mid_lock);
-	mid = server->CurrentMid++;
+	mid = server->CurrentMid;
+	server->CurrentMid = mid + umax(count, 1);
 	spin_unlock(&server->mid_lock);
 	return mid;
 }
@@ -407,7 +408,6 @@ __smb2_find_mid(struct TCP_Server_Info *server, char *buf, bool dequeue)
 		if ((smb->mid == wire_mid) &&
 		    (smb->mid_state == MID_REQUEST_SUBMITTED) &&
 		    (smb->command_id == command)) {
-			smb_get_message(smb);
 			if (dequeue) {
 				list_del_init(&smb->qhead);
 				smb->mid_flags |= MID_DELETED;
@@ -4168,10 +4168,12 @@ smb2_dir_needs_close(struct cifsFileInfo *cfile)
 }
 
 static void
-fill_transform_hdr(struct smb2_transform_hdr *tr_hdr, unsigned int orig_len,
-		   const struct smb_rqst *old_rq, __le16 cipher_type)
+fill_transform_hdr(struct smb2_transform_hdr *tr_hdr,
+		   struct smb_message *head_smb,
+		   unsigned int orig_len,
+		   __le16 cipher_type)
 {
-	struct smb2_hdr *shdr = (struct smb2_hdr *)old_rq->rq_iov[0].iov_base;
+	struct smb2_hdr *shdr = head_smb->request;
 
 	*tr_hdr = (struct smb2_transform_hdr){
 		.ProtocolId		= SMB2_TRANSFORM_PROTO_NUM,
@@ -4422,7 +4424,6 @@ decrypt_message(struct TCP_Server_Info *server,
 	aead_request_set_ad(req, assoc_data_len);
 
 	rc = crypto_aead_decrypt(req);
-
 	kvfree_sensitive(creq, sensitive_size);
 	return rc;
 }
@@ -4434,13 +4435,14 @@ decrypt_message(struct TCP_Server_Info *server,
  */
 static int
 smb3_init_transform_rq(struct TCP_Server_Info *server,
-		       int num_rqst, const struct smb_rqst *rqst,
+		       struct smb_message *head_smb,
 		       struct smb2_transform_hdr *tr_hdr,
 		       struct iov_iter *iter)
+
 {
 	int rc;
 
-	fill_transform_hdr(tr_hdr, iov_iter_count(iter), rqst,
+	fill_transform_hdr(tr_hdr, head_smb, iov_iter_count(iter),
 			   server->cipher_type);
 
 	rc = encrypt_message(server, tr_hdr, iter, server->secmech.enc);
@@ -4689,9 +4691,9 @@ static void smb2_decrypt_offload(struct work_struct *work)
 
 	dw->server->lstrp = jiffies;
 	smb = smb2_find_dequeue_mid(dw->server, dw->buf);
-	if (smb == NULL)
+	if (smb == NULL) {
 		cifs_dbg(FYI, "mid not found\n");
-	else {
+	} else {
 		smb->decrypted = true;
 		rc = handle_read_data(dw->server, smb, dw->buf,
 				      dw->server->vals->read_rsp_size,
@@ -4724,7 +4726,7 @@ static void smb2_decrypt_offload(struct work_struct *work)
 				spin_unlock(&dw->server->srv_lock);
 			}
 		}
-		release_mid(smb);
+		smb_put_message(smb);
 	}
 
 free_pages:
@@ -4735,9 +4737,12 @@ static void smb2_decrypt_offload(struct work_struct *work)
 
 
 static int
-receive_encrypted_read(struct TCP_Server_Info *server, struct smb_message **smb,
+receive_encrypted_read(struct TCP_Server_Info *server, struct smb_message **mid,
 		       int *num_mids)
 {
+	WARN_ON_ONCE(1);
+	return -ENOANO; // TODO
+#if 0
 	char *buf = server->smallbuf;
 	struct smb2_transform_hdr *tr_hdr = (struct smb2_transform_hdr *)buf;
 	struct iov_iter iter;
@@ -4753,8 +4758,8 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct smb_message **smb,
 	dw->server = server;
 
 	*num_mids = 1;
-	len = min_t(unsigned int, buflen, server->vals->read_rsp_size +
-		sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1;
+	len = umin(buflen, server->vals->read_rsp_size +
+		   sizeof(struct smb2_transform_hdr)) - HEADER_SIZE(server) + 1;
 
 	rc = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1, len);
 	if (rc < 0)
@@ -4836,6 +4841,7 @@ receive_encrypted_read(struct TCP_Server_Info *server, struct smb_message **smb,
 discard_data:
 	cifs_discard_remaining_data(server);
 	goto free_pages;
+#endif
 }
 
 static int
@@ -4893,9 +4899,9 @@ receive_encrypted_standard(struct TCP_Server_Info *server,
 	}
 
 	smb = smb2_find_mid(server, buf);
-	if (smb == NULL)
+	if (smb == NULL) {
 		cifs_dbg(FYI, "mid not found\n");
-	else {
+	} else {
 		cifs_dbg(FYI, "mid found\n");
 		smb->decrypted = true;
 		smb->resp_buf_size = server->pdu_size;
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 8554a4aa001d..6b94da4c1149 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -4106,7 +4106,6 @@ smb2_echo_callback(struct smb_message *smb)
 		credits.instance = server->reconnect_instance;
 	}
 
-	release_mid(smb);
 	add_credits(server, &credits, CIFS_ECHO_OP);
 }
 
@@ -4262,10 +4261,9 @@ int
 SMB2_echo(struct TCP_Server_Info *server)
 {
 	struct smb2_echo_req *req;
+	struct smb_message *smb;
 	int rc = 0;
 	struct kvec iov[1];
-	struct smb_rqst rqst = { .rq_iov = iov,
-				 .rq_nvec = 1 };
 	unsigned int total_len;
 
 	cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id);
@@ -4280,22 +4278,36 @@ SMB2_echo(struct TCP_Server_Info *server)
 	}
 	spin_unlock(&server->srv_lock);
 
+	smb = smb_message_alloc(SMB2_ECHO, GFP_NOFS);
+	if (!smb)
+		return -ENOMEM;
+
 	rc = smb2_plain_req_init(SMB2_ECHO, NULL, server,
 				 (void **)&req, &total_len);
-	if (rc)
+	if (rc) {
+		mempool_free(smb, &smb_message_pool);
 		return rc;
-
-	req->hdr.CreditRequest = cpu_to_le16(1);
+	}
 
 	iov[0].iov_len = total_len;
 	iov[0].iov_base = (char *)req;
 
-	rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL,
-			     server, CIFS_ECHO_OP, NULL);
+	smb->rqst.rq_iov	= iov;
+	smb->rqst.rq_nvec	= 1;
+	smb->command_id		= SMB2_ECHO;
+	smb->request		= req;
+	smb->total_len		= total_len;
+	smb->callback		= smb2_echo_callback;
+	smb->callback_data	= server;
+
+	req->hdr.CreditRequest = cpu_to_le16(1);
+
+	rc = cifs_call_async(server, smb, CIFS_ECHO_OP, NULL);
 	if (rc)
 		cifs_dbg(FYI, "Echo request failed: %d\n", rc);
 
 	cifs_small_buf_release(req);
+	smb_put_message(smb);
 	return rc;
 }
 
@@ -4559,7 +4571,7 @@ smb2_readv_callback(struct smb_message *smb)
 			int rc;
 
 			iov_iter_truncate(&rqst.rq_iter, rdata->got_bytes);
-			rc = smb2_verify_signature(&rqst, server);
+			rc = smb2_verify_signature(smb, server);
 			if (rc)
 				cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n",
 					 rc);
@@ -4645,7 +4657,6 @@ smb2_readv_callback(struct smb_message *smb)
 	rdata->subreq.transferred += rdata->got_bytes;
 	trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_progress);
 	netfs_read_subreq_terminated(&rdata->subreq);
-	release_mid(smb);
 	trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0,
 			      server->credits, server->in_flight,
 			      credits.value, cifs_trace_rw_credits_read_response_add);
@@ -4656,46 +4667,62 @@ smb2_readv_callback(struct smb_message *smb)
 int
 smb2_async_readv(struct cifs_io_subrequest *rdata)
 {
-	int rc, flags = 0;
-	char *buf;
 	struct netfs_io_subrequest *subreq = &rdata->subreq;
-	struct smb2_hdr *shdr;
-	struct cifs_io_parms io_parms;
-	struct smb_rqst rqst = { .rq_iov = rdata->iov,
-				 .rq_nvec = 1 };
 	struct TCP_Server_Info *server;
+	struct smb_message *smb;
+	struct smb2_hdr *shdr;
 	struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink);
 	unsigned int total_len;
+	void *buf;
 	int credit_request;
+	int rc, flags = 0;
 
 	cifs_dbg(FYI, "%s: offset=%llu bytes=%zu\n",
 		 __func__, subreq->start, subreq->len);
 
-	if (!rdata->server)
-		rdata->server = cifs_pick_channel(tcon->ses);
+	server = rdata->server;
+	if (!server)
+		server = rdata->server = cifs_pick_channel(tcon->ses);
+
+	struct cifs_io_parms io_parms = {
+		.tcon		= tlink_tcon(rdata->req->cfile->tlink),
+		.server		= server,
+		.offset		= subreq->start + subreq->transferred,
+		.length		= subreq->len   - subreq->transferred,
+		.persistent_fid	= rdata->req->cfile->fid.persistent_fid,
+		.volatile_fid	= rdata->req->cfile->fid.volatile_fid,
+		.pid		= rdata->req->pid,
+	};
 
-	io_parms.tcon = tlink_tcon(rdata->req->cfile->tlink);
-	io_parms.server = server = rdata->server;
-	io_parms.offset = subreq->start + subreq->transferred;
-	io_parms.length = subreq->len   - subreq->transferred;
-	io_parms.persistent_fid = rdata->req->cfile->fid.persistent_fid;
-	io_parms.volatile_fid = rdata->req->cfile->fid.volatile_fid;
-	io_parms.pid = rdata->req->pid;
+	smb = smb_message_alloc(SMB2_READ, GFP_NOFS);
+	if (!smb)
+		return -ENOMEM;
 
-	rc = smb2_new_read_req(
-		(void **) &buf, &total_len, &io_parms, rdata, 0, 0);
-	if (rc)
+	rc = smb2_new_read_req(&buf, &total_len, &io_parms, rdata, 0, 0);
+	if (rc) {
+		mempool_free(smb, &smb_message_pool);
 		return rc;
+	}
 
 	if (smb3_encryption_required(io_parms.tcon))
 		flags |= CIFS_TRANSFORM_REQ;
 
-	rdata->iov[0].iov_base = buf;
-	rdata->iov[0].iov_len = total_len;
-	rdata->got_bytes = 0;
-	rdata->result = 0;
+	rdata->iov[0].iov_base	= buf;
+	rdata->iov[0].iov_len	= total_len;
+	rdata->got_bytes	= 0;
+	rdata->result		= 0;
+
+	smb->rqst.rq_iov	= rdata->iov;
+	smb->rqst.rq_nvec	= 1;
+	smb->command_id		= SMB2_READ;
+	smb->request		= buf;
+	smb->total_len		= total_len;
+	smb->receive		= cifs_readv_receive;
+	smb->handle		= smb3_handle_read_data;
+	smb->callback		= smb2_readv_callback;
+	smb->callback_data	= rdata;
 
-	shdr = (struct smb2_hdr *)buf;
+	shdr = (struct smb2_hdr *)smb->request;
 
 	if (rdata->credits.value > 0) {
 		shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(io_parms.length,
@@ -4715,10 +4742,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
 		flags |= CIFS_HAS_CREDITS;
 	}
 
-	rc = cifs_call_async(server, &rqst,
-			     cifs_readv_receive, smb2_readv_callback,
-			     smb3_handle_read_data, rdata, flags,
-			     &rdata->credits);
+	rc = cifs_call_async(server, smb, flags, &rdata->credits);
 	if (rc) {
 		cifs_stats_fail_inc(io_parms.tcon, SMB2_READ);
 		trace_smb3_read_err(rdata->rreq->debug_id,
@@ -4732,6 +4756,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
 
 async_readv_out:
 	cifs_small_buf_release(buf);
+	smb_put_message(smb);
 	return rc;
 }
 
@@ -4931,7 +4956,6 @@ smb2_writev_callback(struct smb_message *smb)
 			      0, cifs_trace_rw_credits_write_response_clear);
 	wdata->credits.value = 0;
 	cifs_write_subrequest_terminated(wdata, result ?: written);
-	release_mid(smb);
 	trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0,
 			      server->credits, server->in_flight,
 			      credits.value, cifs_trace_rw_credits_write_response_add);
@@ -4942,77 +4966,90 @@ smb2_writev_callback(struct smb_message *smb)
 void
 smb2_async_writev(struct cifs_io_subrequest *wdata)
 {
-	int rc = -EACCES, flags = 0;
 	struct smb2_write_req *req = NULL;
+	struct smb_message *smb;
 	struct smb2_hdr *shdr;
 	struct cifs_tcon *tcon = tlink_tcon(wdata->req->cfile->tlink);
 	struct TCP_Server_Info *server = wdata->server;
 	struct kvec iov[1];
-	struct smb_rqst rqst = { };
 	unsigned int total_len, xid = wdata->xid;
-	struct cifs_io_parms _io_parms;
-	struct cifs_io_parms *io_parms = NULL;
 	int credit_request;
+	int rc = -EACCES, flags = 0;
 
 	/*
 	 * in future we may get cifs_io_parms passed in from the caller,
 	 * but for now we construct it here...
 	 */
-	_io_parms = (struct cifs_io_parms) {
-		.tcon = tcon,
-		.server = server,
-		.offset = wdata->subreq.start,
-		.length = wdata->subreq.len,
-		.persistent_fid = wdata->req->cfile->fid.persistent_fid,
-		.volatile_fid = wdata->req->cfile->fid.volatile_fid,
-		.pid = wdata->req->pid,
+	struct cifs_io_parms io_parms = {
+		.tcon		= tcon,
+		.server		= server,
+		.offset		= wdata->subreq.start,
+		.length		= wdata->subreq.len,
+		.persistent_fid	= wdata->req->cfile->fid.persistent_fid,
+		.volatile_fid	= wdata->req->cfile->fid.volatile_fid,
+		.pid		= wdata->req->pid,
 	};
-	io_parms = &_io_parms;
+
+	smb = smb_message_alloc(SMB2_WRITE, GFP_NOFS);
+	if (!smb) {
+		rc = -ENOMEM;
+		goto out;
+	}
 
 	rc = smb2_plain_req_init(SMB2_WRITE, tcon, server,
 				 (void **) &req, &total_len);
-	if (rc)
+	if (rc) {
+		mempool_free(smb, &smb_message_pool);
 		goto out;
+	}
 
-	rqst.rq_iov = iov;
-	rqst.rq_iter = wdata->subreq.io_iter;
+	smb->rqst.rq_iov = iov;
+	smb->rqst.rq_iter = wdata->subreq.io_iter;
+
+	smb->rqst.rq_iov[0].iov_len = total_len - 1;
+	smb->rqst.rq_iov[0].iov_base = (char *)req;
+	smb->rqst.rq_nvec += 1;
 
-	rqst.rq_iov[0].iov_len = total_len - 1;
-	rqst.rq_iov[0].iov_base = (char *)req;
-	rqst.rq_nvec += 1;
+	smb->rqst.rq_iov	= wdata->iov;
+	smb->rqst.rq_nvec	= 1;
+	smb->command_id		= SMB2_WRITE;
+	smb->request		= req;
+	smb->total_len		= total_len;
+	smb->callback		= smb2_writev_callback;
+	smb->callback_data	= wdata;
 
 	if (smb3_encryption_required(tcon))
 		flags |= CIFS_TRANSFORM_REQ;
 
 	shdr = (struct smb2_hdr *)req;
-	shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
-
-	req->PersistentFileId = io_parms->persistent_fid;
-	req->VolatileFileId = io_parms->volatile_fid;
-	req->WriteChannelInfoOffset = 0;
-	req->WriteChannelInfoLength = 0;
-	req->Channel = SMB2_CHANNEL_NONE;
-	req->Length = cpu_to_le32(io_parms->length);
-	req->Offset = cpu_to_le64(io_parms->offset);
-	req->DataOffset = cpu_to_le16(
-				offsetof(struct smb2_write_req, Buffer));
-	req->RemainingBytes = 0;
+	shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms.pid);
+
+	req->PersistentFileId		= io_parms.persistent_fid;
+	req->VolatileFileId		= io_parms.volatile_fid;
+	req->WriteChannelInfoOffset	= 0;
+	req->WriteChannelInfoLength	= 0;
+	req->Channel			= SMB2_CHANNEL_NONE;
+	req->Length			= cpu_to_le32(io_parms.length);
+	req->Offset			= cpu_to_le64(io_parms.offset);
+	req->DataOffset			=
+		cpu_to_le16(offsetof(struct smb2_write_req, Buffer));
+	req->RemainingBytes		= 0;
 
 	trace_smb3_write_enter(wdata->rreq->debug_id,
 			       wdata->subreq.debug_index,
 			       wdata->xid,
-			       io_parms->persistent_fid,
-			       io_parms->tcon->tid,
-			       io_parms->tcon->ses->Suid,
-			       io_parms->offset,
-			       io_parms->length);
+			       io_parms.persistent_fid,
+			       io_parms.tcon->tid,
+			       io_parms.tcon->ses->Suid,
+			       io_parms.offset,
+			       io_parms.length);
 
 #ifdef CONFIG_CIFS_SMB_DIRECT
 	/*
 	 * If we want to do a server RDMA read, fill in and append
 	 * smbdirect_buffer_descriptor_v1 to the end of write request
 	 */
-	if (smb3_use_rdma_offload(io_parms)) {
+	if (smb3_use_rdma_offload(&io_parms)) {
 		struct smbdirect_buffer_descriptor_v1 *v1;
 		bool need_invalidate = server->dialect == SMB30_PROT_ID;
 
@@ -5038,21 +5075,21 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
 		v1->token = cpu_to_le32(wdata->mr->mr->rkey);
 		v1->length = cpu_to_le32(wdata->mr->mr->length);
 
-		rqst.rq_iov[0].iov_len += sizeof(*v1);
+		smb->rqst.rq_iov[0].iov_len += sizeof(*v1);
 
 		/*
 		 * We keep wdata->subreq.io_iter,
 		 * but we have to truncate rqst.rq_iter
 		 */
-		iov_iter_truncate(&rqst.rq_iter, 0);
+		iov_iter_truncate(&smb->rqst.rq_iter, 0);
 	}
 #endif
 
 	if (wdata->subreq.retry_count > 0)
-		smb2_set_replay(server, &rqst);
+		smb2_set_replay(server, &smb->rqst);
 
 	cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n",
-		 io_parms->offset, io_parms->length, iov_iter_count(&wdata->subreq.io_iter));
+		 io_parms.offset, io_parms.length, iov_iter_count(&wdata->subreq.io_iter));
 
 	if (wdata->credits.value > 0) {
 		shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->subreq.len,
@@ -5073,27 +5110,28 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
 	}
 
 	/* XXX: compression + encryption is unsupported for now */
-	if (((flags & CIFS_TRANSFORM_REQ) != CIFS_TRANSFORM_REQ) && should_compress(tcon, &rqst))
+	if (((flags & CIFS_TRANSFORM_REQ) != CIFS_TRANSFORM_REQ) &&
+	    should_compress(tcon, smb))
 		flags |= CIFS_COMPRESS_REQ;
 
-	rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL,
-			     wdata, flags, &wdata->credits);
+	rc = cifs_call_async(server, smb, flags, &wdata->credits);
 	/* Can't touch wdata if rc == 0 */
 	if (rc) {
 		trace_smb3_write_err(wdata->rreq->debug_id,
 				     wdata->subreq.debug_index,
 				     xid,
-				     io_parms->persistent_fid,
-				     io_parms->tcon->tid,
-				     io_parms->tcon->ses->Suid,
-				     io_parms->offset,
-				     io_parms->length,
+				     io_parms.persistent_fid,
+				     io_parms.tcon->tid,
+				     io_parms.tcon->ses->Suid,
+				     io_parms.offset,
+				     io_parms.length,
 				     rc);
 		cifs_stats_fail_inc(tcon, SMB2_WRITE);
 	}
 
 async_writev_out:
 	cifs_small_buf_release(req);
+	smb_put_message(smb);
 out:
 	if (rc) {
 		trace_smb3_rw_credits(wdata->rreq->debug_id,
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 6f1ce0399334..3018b171c6de 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -29,22 +29,20 @@ extern char *smb2_get_data_area_len(int *off, int *len,
 extern __le16 *cifs_convert_path_to_utf16(const char *from,
 					  struct cifs_sb_info *cifs_sb);
 
-extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *);
-extern int smb2_check_receive(struct smb_message *smb,
-			      struct TCP_Server_Info *server, bool log_error);
-extern struct smb_message *smb2_setup_request(struct cifs_ses *ses,
-					      struct TCP_Server_Info *,
-					      struct smb_rqst *rqst);
-extern struct smb_message *smb2_setup_async_request(
-			struct TCP_Server_Info *server, struct smb_rqst *rqst);
+int smb2_verify_signature(struct smb_message *smb, struct TCP_Server_Info *server);
+int smb2_check_receive(struct smb_message *smb, struct TCP_Server_Info *server,
+		       bool log_error);
+int smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server,
+		       struct smb_message *smb);
+int smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_message *smb);
 extern struct cifs_tcon *smb2_find_smb_tcon(struct TCP_Server_Info *server,
 						__u64 ses_id, __u32  tid);
-extern int smb2_calc_signature(struct smb_rqst *rqst,
-				struct TCP_Server_Info *server,
-				bool allocate_crypto);
-extern int smb3_calc_signature(struct smb_rqst *rqst,
-				struct TCP_Server_Info *server,
-				bool allocate_crypto);
+int smb2_calc_signature(struct smb_message *smb,
+			struct TCP_Server_Info *server,
+			bool allocate_crypto);
+int smb3_calc_signature(struct smb_message *smb,
+			struct TCP_Server_Info *server,
+			bool allocate_crypto);
 extern void smb2_echo_request(struct work_struct *work);
 extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
 extern bool smb2_is_valid_oplock_break(char *buffer,
@@ -127,8 +125,7 @@ extern int smb2_unlock_range(struct cifsFileInfo *cfile,
 extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
 extern void smb2_reconnect_server(struct work_struct *work);
 extern int smb3_crypto_aead_allocate(struct TCP_Server_Info *server);
-extern unsigned long smb_rqst_len(struct TCP_Server_Info *server,
-				  struct smb_rqst *rqst);
+extern unsigned long smb_rqst_len(struct TCP_Server_Info *server, struct smb_rqst *rqst);
 extern void smb2_set_next_command(struct cifs_tcon *tcon,
 				  struct smb_rqst *rqst);
 extern void smb2_set_related(struct smb_rqst *rqst);
@@ -298,7 +295,7 @@ extern void smb2_copy_fs_info_to_kstatfs(
 extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server);
 extern int smb311_update_preauth_hash(struct cifs_ses *ses,
 				      struct TCP_Server_Info *server,
-				      struct kvec *iov, int nvec);
+				      struct smb_message *smb, bool hash_resp);
 extern int smb2_query_info_compound(const unsigned int xid,
 				    struct cifs_tcon *tcon,
 				    const char *path, u32 desired_access,
diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
index b217bc0e8e5b..b02f458e408a 100644
--- a/fs/smb/client/smb2transport.c
+++ b/fs/smb/client/smb2transport.c
@@ -252,14 +252,13 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32  tid)
 	return tcon;
 }
 
-int
-smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
+int smb2_calc_signature(struct smb_message *smb, struct TCP_Server_Info *server,
 			bool allocate_crypto)
 {
 	int rc;
 	unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
 	unsigned char *sigptr = smb2_signature;
-	struct kvec *iov = rqst->rq_iov;
+	struct kvec *iov = smb->rqst.rq_iov;
 	struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
 	struct shash_desc *shash = NULL;
 	struct smb_rqst drqst;
@@ -308,7 +307,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
 	 * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to
 	 * __cifs_calc_signature().
 	 */
-	drqst = *rqst;
+	drqst = smb->rqst;
 	if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
 		rc = crypto_shash_update(shash, iov[0].iov_base,
 					 iov[0].iov_len);
@@ -581,15 +580,14 @@ generate_smb311signingkey(struct cifs_ses *ses,
 	return generate_smb3signingkey(ses, server, &triplet);
 }
 
-int
-smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
+int smb3_calc_signature(struct smb_message *smb, struct TCP_Server_Info *server,
 			bool allocate_crypto)
 {
 	int rc;
 	unsigned char smb3_signature[SMB2_CMACAES_SIZE];
 	unsigned char *sigptr = smb3_signature;
-	struct kvec *iov = rqst->rq_iov;
-	struct smb2_hdr *shdr = (struct smb2_hdr *)iov[0].iov_base;
+	struct kvec *iov = smb->rqst.rq_iov;
+	struct smb2_hdr *shdr = smb->request;
 	struct shash_desc *shash = NULL;
 	struct smb_rqst drqst;
 	u8 key[SMB3_SIGN_KEY_SIZE];
@@ -635,7 +633,7 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
 	 * Sign the rfc1002 length prior to passing the data (iov[1-N]) down to
 	 * __cifs_calc_signature().
 	 */
-	drqst = *rqst;
+	drqst = smb->rqst;
 	if (drqst.rq_nvec >= 2 && iov[0].iov_len == 4) {
 		rc = crypto_shash_update(shash, iov[0].iov_base,
 					 iov[0].iov_len);
@@ -660,23 +658,22 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server,
 
 /* must be called with server->srv_mutex held */
 static int
-smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+smb2_sign_rqst(struct smb_message *smb, struct TCP_Server_Info *server)
 {
 	int rc = 0;
-	struct smb2_hdr *shdr;
+	struct smb2_hdr *shdr = smb->request;
 	struct smb2_sess_setup_req *ssr;
 	bool is_binding;
 	bool is_signed;
 
-	shdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
 	ssr = (struct smb2_sess_setup_req *)shdr;
 
-	is_binding = le16_to_cpu(shdr->Command) == SMB2_SESSION_SETUP &&
+	is_binding = smb->command_id == SMB2_SESSION_SETUP &&
 		(ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING);
 	is_signed = shdr->Flags & SMB2_FLAGS_SIGNED;
-
 	if (!is_signed)
 		return 0;
+
 	spin_lock(&server->srv_lock);
 	if (server->ops->need_neg &&
 	    server->ops->need_neg(server)) {
@@ -689,24 +686,21 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 		return 0;
 	}
 
-	rc = server->ops->calc_signature(rqst, server, false);
-
+	rc = server->ops->calc_signature(smb, server, false);
 	return rc;
 }
 
-int
-smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+int smb2_verify_signature(struct smb_message *smb, struct TCP_Server_Info *server)
 {
-	unsigned int rc;
+	struct smb2_hdr *shdr = smb->request;
 	char server_response_sig[SMB2_SIGNATURE_SIZE];
-	struct smb2_hdr *shdr =
-			(struct smb2_hdr *)rqst->rq_iov[0].iov_base;
+	int rc;
 
-	if (le16_to_cpu(shdr->Command) == SMB2_NEGOTIATE ||
-	    le16_to_cpu(shdr->Command) == SMB2_SESSION_SETUP ||
-	    le16_to_cpu(shdr->Command) == SMB2_OPLOCK_BREAK ||
+	if (smb->command_id == SMB2_NEGOTIATE ||
+	    smb->command_id == SMB2_SESSION_SETUP ||
+	    smb->command_id == SMB2_OPLOCK_BREAK ||
 	    server->ignore_signature ||
-	    (!server->session_estab))
+	    !server->session_estab)
 		return 0;
 
 	/*
@@ -727,7 +721,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 
 	memset(shdr->Signature, 0, SMB2_SIGNATURE_SIZE);
 
-	rc = server->ops->calc_signature(rqst, server, true);
+	rc = server->ops->calc_signature(smb, server, true);
 
 	if (rc)
 		return rc;
@@ -736,8 +730,8 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 		cifs_dbg(VFS, "sign fail cmd 0x%x message id 0x%llx\n",
 			shdr->Command, shdr->MessageId);
 		return -EACCES;
-	} else
-		return 0;
+	}
+	return 0;
 }
 
 /*
@@ -746,58 +740,40 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
  */
 static inline void
 smb2_seq_num_into_buf(struct TCP_Server_Info *server,
-		      struct smb2_hdr *shdr)
+		      struct smb_message *smb)
 {
-	unsigned int i, num = le16_to_cpu(shdr->CreditCharge);
+	struct smb2_hdr *shdr = smb->request;
+	unsigned int num = le16_to_cpu(shdr->CreditCharge);
 
-	shdr->MessageId = get_next_mid64(server);
 	/* skip message numbers according to CreditCharge field */
-	for (i = 1; i < num; i++)
-		get_next_mid(server);
+	smb->mid = get_next_mid64(server, num);
+	shdr->MessageId = cpu_to_le64(smb->mid);
 }
 
-static struct smb_message *
-smb2_mid_entry_alloc(const struct smb2_hdr *shdr,
-		     struct TCP_Server_Info *server)
+static void smb2_init_mid(struct smb_message *smb,
+			  struct TCP_Server_Info *server)
 {
-	struct smb_message *smb;
+	const struct smb2_hdr *shdr = smb->request;
 	unsigned int credits = le16_to_cpu(shdr->CreditCharge);
 
-	if (server == NULL) {
-		cifs_dbg(VFS, "Null TCP session in smb2_mid_entry_alloc\n");
-		return NULL;
-	}
-
-	smb = mempool_alloc(&smb_message_pool, GFP_NOFS);
-	memset(smb, 0, sizeof(*smb));
-	refcount_set(&smb->ref, 1);
-	smb->mid = le64_to_cpu(shdr->MessageId);
-	smb->credits_consumed = credits > 0 ? credits : 1;
-	smb->pid = current->pid;
-	smb->command_id = le16_to_cpu(shdr->Command);
-	smb->when_alloc = jiffies;
-	smb->server = server;
+	smb->credits_consumed	= credits > 0 ? credits : 1;
+	smb->server		= server;
 
 	/*
 	 * The default is for the mid to be synchronous, so the
 	 * default callback just wakes up the current task.
 	 */
-	get_task_struct(current);
-	smb->creator = current;
-	smb->callback = cifs_wake_up_task;
-	smb->callback_data = current;
+	smb->creator		= get_task_struct(current);
 
 	atomic_inc(&mid_count);
-	smb->mid_state = MID_REQUEST_ALLOCATED;
 	trace_smb3_cmd_enter(le32_to_cpu(shdr->Id.SyncId.TreeId),
 			     le64_to_cpu(shdr->SessionId),
 			     le16_to_cpu(shdr->Command), smb->mid);
-	return smb;
 }
 
 static int
 smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
-		   struct smb2_hdr *shdr, struct smb_message **smb)
+		   struct smb_message *smb)
 {
 	spin_lock(&server->srv_lock);
 	if (server->tcpStatus == CifsExiting) {
@@ -812,7 +788,7 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
 	}
 
 	if (server->tcpStatus == CifsNeedNegotiate &&
-	    le16_to_cpu(shdr->Command) != SMB2_NEGOTIATE) {
+	    smb->command_id != SMB2_NEGOTIATE) {
 		spin_unlock(&server->srv_lock);
 		return -EAGAIN;
 	}
@@ -820,8 +796,8 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
 
 	spin_lock(&ses->ses_lock);
 	if (ses->ses_status == SES_NEW) {
-		if (le16_to_cpu(shdr->Command) != SMB2_SESSION_SETUP &&
-		    le16_to_cpu(shdr->Command) != SMB2_NEGOTIATE) {
+		if (smb->command_id != SMB2_SESSION_SETUP &&
+		    smb->command_id != SMB2_NEGOTIATE) {
 			spin_unlock(&ses->ses_lock);
 			return -EAGAIN;
 		}
@@ -829,7 +805,7 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
 	}
 
 	if (ses->ses_status == SES_EXITING) {
-		if (le16_to_cpu(shdr->Command) != SMB2_LOGOFF) {
+		if (smb->command_id != SMB2_LOGOFF) {
 			spin_unlock(&ses->ses_lock);
 			return -EAGAIN;
 		}
@@ -837,101 +813,78 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
 	}
 	spin_unlock(&ses->ses_lock);
 
-	*smb = smb2_mid_entry_alloc(shdr, server);
-	if (*smb == NULL)
-		return -ENOMEM;
+	smb2_init_mid(smb, server);
+
+	smb_get_message(smb);
 	spin_lock(&server->mid_lock);
-	list_add_tail(&(*smb)->qhead, &server->pending_mid_q);
+	list_add_tail(&smb->qhead, &server->pending_mid_q);
 	spin_unlock(&server->mid_lock);
-
 	return 0;
 }
 
-int
-smb2_check_receive(struct smb_message *mid, struct TCP_Server_Info *server,
-		   bool log_error)
+int smb2_check_receive(struct smb_message *smb, struct TCP_Server_Info *server,
+		       bool log_error)
 {
-	unsigned int len = mid->resp_buf_size;
-	struct kvec iov[1];
-	struct smb_rqst rqst = { .rq_iov = iov,
-				 .rq_nvec = 1 };
+	unsigned int len = smb->resp_buf_size;
 
-	iov[0].iov_base = (char *)mid->resp_buf;
-	iov[0].iov_len = len;
-
-	dump_smb(mid->resp_buf, min_t(u32, 80, len));
+	dump_smb(smb->resp_buf, min_t(u32, 80, len));
 	/* convert the length into a more usable form */
-	if (len > 24 && server->sign && !mid->decrypted) {
+	if (len > 24 && server->sign && !smb->decrypted) {
 		int rc;
 
-		rc = smb2_verify_signature(&rqst, server);
+		rc = smb2_verify_signature(smb, server);
 		if (rc)
 			cifs_server_dbg(VFS, "SMB signature verification returned error = %d\n",
 				 rc);
 	}
 
-	return map_smb2_to_linux_error(mid->resp_buf, log_error);
+	return map_smb2_to_linux_error(smb->resp_buf, log_error);
 }
 
-struct smb_message *
-smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server,
-		   struct smb_rqst *rqst)
+int smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server,
+		       struct smb_message *smb)
 {
+	struct smb2_hdr *shdr = smb->request;
 	int rc;
-	struct smb2_hdr *shdr =
-			(struct smb2_hdr *)rqst->rq_iov[0].iov_base;
-	struct smb_message *mid;
 
-	smb2_seq_num_into_buf(server, shdr);
+	smb2_seq_num_into_buf(server, smb);
 
-	rc = smb2_get_mid_entry(ses, server, shdr, &mid);
+	rc = smb2_get_mid_entry(ses, server, smb);
 	if (rc) {
 		revert_current_mid_from_hdr(server, shdr);
-		return ERR_PTR(rc);
+		return rc;
 	}
 
-	rc = smb2_sign_rqst(rqst, server);
-	if (rc) {
+	rc = smb2_sign_rqst(smb, server);
+	if (rc)
 		revert_current_mid_from_hdr(server, shdr);
-		delete_mid(mid);
-		return ERR_PTR(rc);
-	}
-
-	return mid;
+	return rc;
 }
 
-struct smb_message *
-smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+int
+smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_message *smb)
 {
+	struct smb2_hdr *shdr = smb->request;
 	int rc;
-	struct smb2_hdr *shdr =
-			(struct smb2_hdr *)rqst->rq_iov[0].iov_base;
-	struct smb_message *smb;
 
 	spin_lock(&server->srv_lock);
 	if (server->tcpStatus == CifsNeedNegotiate &&
-	    le16_to_cpu(shdr->Command) != SMB2_NEGOTIATE) {
+	   smb->command_id != SMB2_NEGOTIATE) {
 		spin_unlock(&server->srv_lock);
-		return ERR_PTR(-EAGAIN);
+		return -EAGAIN;
 	}
 	spin_unlock(&server->srv_lock);
 
-	smb2_seq_num_into_buf(server, shdr);
-
-	smb = smb2_mid_entry_alloc(shdr, server);
-	if (smb == NULL) {
-		revert_current_mid_from_hdr(server, shdr);
-		return ERR_PTR(-ENOMEM);
-	}
+	smb2_seq_num_into_buf(server, smb);
+	smb2_init_mid(smb, server);
 
-	rc = smb2_sign_rqst(rqst, server);
+	rc = smb2_sign_rqst(smb, server);
 	if (rc) {
 		revert_current_mid_from_hdr(server, shdr);
-		release_mid(smb);
-		return ERR_PTR(rc);
+		return rc;
 	}
 
-	return smb;
+	return 0;
 }
 
 int
diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c
index 6459acf959f3..042f689bbf52 100644
--- a/fs/smb/client/transport.c
+++ b/fs/smb/client/transport.c
@@ -40,7 +40,17 @@ struct smb_message *smb_message_alloc(enum smb2_command cmd, gfp_t gfp)
 	if (smb) {
 		memset(smb, 0, sizeof(*smb));
 		refcount_set(&smb->ref, 1);
-		smb->command_id = cmd;
+		smb->command_id	= cmd;
+		smb->when_alloc	= jiffies;
+		smb->pid	= current->pid;
+
+		/*
+		 * The default is for the mid to be synchronous, so the default
+		 * callback just wakes up the current task.
+		 */
+		smb->callback		= cifs_wake_up_task;
+		smb->callback_data	= current;
+		smb->mid_state		= MID_REQUEST_ALLOCATED;
 	}
 	return smb;
 }
@@ -60,7 +70,8 @@ void smb_put_message(struct smb_message *smb)
 }
 
 /*
- * Dispose of a chain of compound messages.
+ * Dispose of a chain of compound messages.  This should only be called by the
+ * caller of smb_send_recv_messages().
  */
 void smb_put_messages(struct smb_message *smb)
 {
@@ -80,7 +91,7 @@ cifs_wake_up_task(struct smb_message *smb)
 	wake_up_process(smb->callback_data);
 }
 
-void __release_mid(struct smb_message *smb)
+static void smb_clear_mid(struct smb_message *smb, struct TCP_Server_Info *server)
 {
 #ifdef CONFIG_CIFS_STATS2
 	enum smb2_command command = smb->server->vals->lock_cmd;
@@ -88,7 +99,6 @@ void __release_mid(struct smb_message *smb)
 	unsigned long now;
 	unsigned long roundtrip_time;
 #endif
-	struct TCP_Server_Info *server = smb->server;
 
 	if (smb->resp_buf && (smb->mid_flags & MID_WAIT_CANCELLED) &&
 	    (smb->mid_state == MID_RESPONSE_RECEIVED ||
@@ -154,22 +164,33 @@ void __release_mid(struct smb_message *smb)
 	}
 #endif
 	put_task_struct(smb->creator);
-
-	mempool_free(smb, &smb_message_pool);
 }
 
-void
-delete_mid(struct smb_message *smb)
+static bool discard_message(struct TCP_Server_Info *server, struct smb_message *smb)
 {
-	spin_lock(&smb->server->mid_lock);
+	bool got_ref = false;
+
+	spin_lock(&server->mid_lock);
 
 	if (!(smb->mid_flags & MID_DELETED)) {
 		list_del_init(&smb->qhead);
 		smb->mid_flags |= MID_DELETED;
+		got_ref = true;
 	}
-	spin_unlock(&smb->server->mid_lock);
 
-	release_mid(smb);
+	spin_unlock(&server->mid_lock);
+	return got_ref;
+}
+
+static void smb_discard_messages(struct TCP_Server_Info *server, struct smb_message *head_smb)
+{
+	struct smb_message *smb, *next;
+
+	for (smb = head_smb; smb; smb = next) {
+		next = smb->next;
+		if (discard_message(server, smb))
+			smb_put_message(smb);
+	}
 }
 
 /*
@@ -456,20 +477,16 @@ static size_t smb3_copy_data_iter(void *iter_from, size_t progress, size_t len,
  * at the front for the header(s).
  */
 static int smb_copy_data_into_buffer(struct TCP_Server_Info *server,
-				     int num_rqst, struct smb_rqst *rqst,
+				     struct smb_message *head_smb,
 				     struct iov_iter *iter, struct bvecq **_bq)
 {
+	struct smb_message *smb;
 	struct bvecq *bq;
 	size_t total_len = 0, offset = 0;
 
-	for (int i = 0; i < num_rqst; i++) {
-		struct smb_rqst *req = &rqst[i];
-		size_t size = iov_iter_count(&req->rq_iter);
-
-		for (int j = 0; j < req->rq_nvec; j++)
-			size += req->rq_iov[j].iov_len;
+	for (smb = head_smb; smb; smb = smb->next) {
 		total_len = ALIGN8(total_len);
-		total_len += size;
+		total_len += smb->total_len;
 	}
 
 	bq = netfs_alloc_bvecq_buffer(total_len, 1, GFP_NOFS);
@@ -478,9 +495,8 @@ static int smb_copy_data_into_buffer(struct TCP_Server_Info *server,
 
 	iov_iter_bvec_queue(iter, ITER_DEST, bq, 1, 0, total_len);
 
-	for (int i = 0; i < num_rqst; i++) {
-		struct smb_rqst *req = &rqst[i];
-		size_t size = iov_iter_count(&req->rq_iter);
+	for (smb = head_smb; smb; smb = smb->next) {
+		size_t size = iov_iter_count(&smb->rqst.rq_iter);
 
 		if (offset & 7) {
 			unsigned int tmp = offset;
@@ -488,14 +504,15 @@ static int smb_copy_data_into_buffer(struct TCP_Server_Info *server,
 			iov_iter_zero(offset - tmp, iter);
 		}
 
-		for (int j = 0; j < req->rq_nvec; j++) {
-			size_t len = req->rq_iov[j].iov_len;
-			if (copy_to_iter(req->rq_iov[j].iov_base, len, iter) != len)
+		for (int i = 0; i < smb->rqst.rq_nvec; i++) {
+			size_t len = smb->rqst.rq_iov[i].iov_len;
+			if (copy_to_iter(smb->rqst.rq_iov[i].iov_base,
+					 len, iter) != len)
 				goto error;
 			offset += len;
 		}
 
-		if (iterate_and_advance_kernel(&req->rq_iter,
+		if (iterate_and_advance_kernel(&smb->rqst.rq_iter,
 					       size, iter, NULL,
 					       smb3_copy_data_iter) != size)
 			goto error;
@@ -517,8 +534,7 @@ static int smb_copy_data_into_buffer(struct TCP_Server_Info *server,
 }
 
 static int
-smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
-	      struct smb_rqst *rqst, int flags)
+smb_send_rqst(struct TCP_Server_Info *server, struct smb_message *head_smb, int flags)
 {
 	struct smb2_transform_hdr *tr_hdr;
 	struct iov_iter iter;
@@ -532,7 +548,7 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
 		return -EIO;
 	}
 
-	rc = smb_copy_data_into_buffer(server, num_rqst, rqst, &iter, &bq);
+	rc = smb_copy_data_into_buffer(server, head_smb, &iter, &bq);
 	if (rc)
 		return rc;
 	content_len = iov_iter_count(&iter);
@@ -562,7 +578,7 @@ smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
 			hdr_len += sizeof(*tr_hdr);
 			iov_iter_bvec_queue(&iter, ITER_SOURCE, bq, 1, 0, content_len);
 
-			rc = server->ops->init_transform_rq(server, num_rqst, rqst, tr_hdr, &iter);
+			rc = server->ops->init_transform_rq(server, head_smb, tr_hdr, &iter);
 			if (rc)
 				goto error;
 		}
@@ -816,16 +832,16 @@ int wait_for_response(struct TCP_Server_Info *server, struct smb_message *smb)
  * the result. Caller is responsible for dealing with timeouts.
  */
 int
-cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
-		mid_receive_t receive, mid_callback_t callback,
-		mid_handle_t handle, void *cbdata, const int flags,
-		const struct cifs_credits *exist_credits)
+cifs_call_async(struct TCP_Server_Info *server, struct smb_message *smb,
+		const int flags, const struct cifs_credits *exist_credits)
 {
-	int rc;
-	struct smb_message *smb;
 	struct cifs_credits credits = { .value = 0, .instance = 0 };
 	unsigned int instance;
 	int optype;
+	int rc;
+
+	if (WARN_ON_ONCE(smb->next))
+		return -EIO;
 
 	optype = flags & CIFS_OP_MASK;
 
@@ -851,21 +867,17 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
 		return -EAGAIN;
 	}
 
-	smb = server->ops->setup_async_request(server, rqst);
-	if (IS_ERR(smb)) {
+	rc = server->ops->setup_async_request(server, smb);
+	if (rc) {
 		cifs_server_unlock(server);
 		add_credits_and_wake_if(server, &credits, optype);
-		return PTR_ERR(smb);
+		return rc;
 	}
 
-	smb->sr_flags = flags;
-	smb->receive = receive;
-	smb->callback = callback;
-	smb->callback_data = cbdata;
-	smb->handle = handle;
 	smb->mid_state = MID_REQUEST_SUBMITTED;
 
 	/* put it on the pending_mid_q */
+	smb_get_message(smb);
 	spin_lock(&server->mid_lock);
 	list_add_tail(&smb->qhead, &server->pending_mid_q);
 	spin_unlock(&server->mid_lock);
@@ -875,12 +887,13 @@ cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
 	 * I/O response may come back and free the mid entry on another thread.
 	 */
 	cifs_save_when_sent(smb);
-	rc = smb_send_rqst(server, 1, rqst, flags);
+	rc = smb_send_rqst(server, smb, flags);
 
 	if (rc < 0) {
 		revert_current_mid(server, smb->credits_consumed);
 		server->sequence_number -= 2;
-		delete_mid(smb);
+		if (discard_message(server, smb))
+			smb_put_message(smb);
 	}
 
 	cifs_server_unlock(server);
@@ -930,7 +943,7 @@ int cifs_sync_mid_result(struct smb_message *smb, struct TCP_Server_Info *server
 	spin_unlock(&server->mid_lock);
 
 sync_mid_done:
-	release_mid(smb);
+	smb_clear_mid(smb, server);
 	return rc;
 }
 
@@ -960,7 +973,6 @@ static void
 cifs_cancelled_callback(struct smb_message *smb)
 {
 	cifs_compound_callback(smb);
-	release_mid(smb);
 }
 
 /*
@@ -1017,31 +1029,29 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
 	return server;
 }
 
-int
-compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
-		   struct TCP_Server_Info *server,
-		   const int flags, const int num_rqst, struct smb_rqst *rqst,
-		   int *resp_buf_type, struct kvec *resp_iov)
+/*
+ * Send a single message or a string of messages as a compound.
+ */
+int smb_send_recv_messages(const unsigned int xid, struct cifs_ses *ses,
+			   struct TCP_Server_Info *server,
+			   struct smb_message *head_smb, const int flags)
 {
-	int i, j, optype, rc = 0;
-	struct smb_message *smb[MAX_COMPOUND];
-	bool cancelled_mid[MAX_COMPOUND] = {false};
-	struct cifs_credits credits[MAX_COMPOUND] = {
-		{ .value = 0, .instance = 0 }
-	};
 	unsigned int instance;
+	int nr_reqs, i, optype, rc = 0;
 	char *buf;
 
-	optype = flags & CIFS_OP_MASK;
-
-	for (i = 0; i < num_rqst; i++)
-		resp_buf_type[i] = CIFS_NO_BUFFER;  /* no response buf yet */
-
 	if (!ses || !ses->server || !server) {
 		cifs_dbg(VFS, "Null session\n");
 		return -EIO;
 	}
 
+	optype = flags & CIFS_OP_MASK;
+
+	/* TODO: Stitch together the messages in a compound. */
+	nr_reqs = 0;
+	for (struct smb_message *smb = head_smb; smb; smb = smb->next)
+		nr_reqs++;
+
 	spin_lock(&server->srv_lock);
 	if (server->tcpStatus == CifsExiting) {
 		spin_unlock(&server->srv_lock);
@@ -1057,14 +1067,13 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
 	 * other requests.
 	 * This can be handled by the eventual session reconnect.
 	 */
-	rc = wait_for_compound_request(server, num_rqst, flags,
-				       &instance);
+	rc = wait_for_compound_request(server, nr_reqs, flags, &instance);
 	if (rc)
 		return rc;
 
-	for (i = 0; i < num_rqst; i++) {
-		credits[i].value = 1;
-		credits[i].instance = instance;
+	for (struct smb_message *smb = head_smb; smb; smb = smb->next) {
+		smb->credits.value	= 1;
+		smb->credits.instance	= instance;
 	}
 
 	/*
@@ -1084,45 +1093,46 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
 	 */
 	if (instance != server->reconnect_instance) {
 		cifs_server_unlock(server);
-		for (j = 0; j < num_rqst; j++)
-			add_credits(server, &credits[j], optype);
+		for (struct smb_message *smb = head_smb; smb; smb = smb->next)
+			add_credits(server, &smb->credits, optype);
 		return -EAGAIN;
 	}
 
-	for (i = 0; i < num_rqst; i++) {
-		smb[i] = server->ops->setup_request(ses, server, &rqst[i]);
-		if (IS_ERR(smb[i])) {
-			revert_current_mid(server, i);
-			for (j = 0; j < i; j++)
-				delete_mid(smb[j]);
-			cifs_server_unlock(server);
-
-			/* Update # of requests on wire to server */
-			for (j = 0; j < num_rqst; j++)
-				add_credits(server, &credits[j], optype);
-			return PTR_ERR(smb[i]);
-		}
-
-		smb[i]->sr_flags = flags;
-		smb[i]->mid_state = MID_REQUEST_SUBMITTED;
-		smb[i]->optype = optype;
+	i = 0;
+	for (struct smb_message *smb = head_smb; smb; smb = smb->next) {
+		smb->optype = optype;
 		/*
 		 * Invoke callback for every part of the compound chain
 		 * to calculate credits properly. Wake up this thread only when
 		 * the last element is received.
 		 */
-		if (i < num_rqst - 1)
-			smb[i]->callback = cifs_compound_callback;
+		if (smb->next)
+			smb->callback = cifs_compound_callback;
 		else
-			smb[i]->callback = cifs_compound_last_callback;
+			smb->callback = cifs_compound_last_callback;
+
+		rc = server->ops->setup_request(ses, server, smb);
+		if (rc) {
+			revert_current_mid(server, i);
+			smb_discard_messages(server, head_smb);
+			cifs_server_unlock(server);
+
+			/* Update # of requests on wire to server */
+			for (struct smb_message *smb = head_smb; smb; smb = smb->next)
+				add_credits(server, &smb->credits, optype);
+			return rc;
+		}
+
+		smb->mid_state = MID_REQUEST_SUBMITTED;
 	}
-	rc = smb_send_rqst(server, num_rqst, rqst, flags);
 
-	for (i = 0; i < num_rqst; i++)
-		cifs_save_when_sent(smb[i]);
+	rc = smb_send_rqst(server, head_smb, flags);
+
+	for (struct smb_message *smb = head_smb; smb; smb = smb->next)
+		cifs_save_when_sent(smb);
 
 	if (rc < 0) {
-		revert_current_mid(server, num_rqst);
+		revert_current_mid(server, nr_reqs);
 		server->sequence_number -= 2;
 	}
 
@@ -1133,8 +1143,8 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
 	 * will not receive a response to - return credits back
 	 */
 	if (rc < 0 || (flags & CIFS_NO_SRV_RSP)) {
-		for (i = 0; i < num_rqst; i++)
-			add_credits(server, &credits[i], optype);
+		for (struct smb_message *smb = head_smb; smb; smb = smb->next)
+			add_credits(server, &smb->credits, optype);
 		goto out;
 	}
 
@@ -1154,70 +1164,71 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
 		spin_unlock(&ses->ses_lock);
 
 		cifs_server_lock(server);
-		smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec);
+		smb311_update_preauth_hash(ses, server, head_smb, false);
 		cifs_server_unlock(server);
-
-		spin_lock(&ses->ses_lock);
+	} else {
+		spin_unlock(&ses->ses_lock);
 	}
-	spin_unlock(&ses->ses_lock);
 
-	for (i = 0; i < num_rqst; i++) {
-		rc = wait_for_response(server, smb[i]);
-		if (rc != 0)
-			break;
+	for (struct smb_message *smb = head_smb; smb; smb = smb->next) {
+		if (!smb->next) {
+			rc = wait_for_response(server, smb);
+			if (rc != 0)
+				break;
+		}
 	}
 	if (rc != 0) {
-		for (; i < num_rqst; i++) {
+		for (struct smb_message *smb = head_smb; smb; smb = smb->next) {
 			cifs_server_dbg(FYI, "Cancelling wait for mid %llu cmd: %d\n",
-				 smb[i]->mid, smb[i]->command_id);
-			send_cancel(ses, server, &rqst[i], smb[i], xid);
+					smb->mid, smb->command_id);
+			send_cancel(ses, server, smb, xid);
 			spin_lock(&server->mid_lock);
-			smb[i]->mid_flags |= MID_WAIT_CANCELLED;
-			if (smb[i]->mid_state == MID_REQUEST_SUBMITTED ||
-			    smb[i]->mid_state == MID_RESPONSE_RECEIVED) {
-				smb[i]->callback = cifs_cancelled_callback;
-				cancelled_mid[i] = true;
-				credits[i].value = 0;
+			smb->mid_flags |= MID_WAIT_CANCELLED;
+			if (smb->mid_state == MID_REQUEST_SUBMITTED ||
+			    smb->mid_state == MID_RESPONSE_RECEIVED) {
+				smb->callback = cifs_cancelled_callback;
+				smb->cancelled = true;
+				smb->credits.value = 0;
 			}
 			spin_unlock(&server->mid_lock);
 		}
 	}
 
-	for (i = 0; i < num_rqst; i++) {
+	for (struct smb_message *smb = head_smb; smb; smb = smb->next) {
 		if (rc < 0)
 			goto out;
 
-		rc = cifs_sync_mid_result(smb[i], server);
+		rc = cifs_sync_mid_result(smb, server);
 		if (rc != 0) {
 			/* mark this mid as cancelled to not free it below */
-			cancelled_mid[i] = true;
+			smb->cancelled = true;
 			goto out;
 		}
 
-		if (!smb[i]->resp_buf ||
-		    smb[i]->mid_state != MID_RESPONSE_READY) {
+		if (!smb->resp_buf ||
+		    smb->mid_state != MID_RESPONSE_READY) {
 			rc = -EIO;
 			cifs_dbg(FYI, "Bad MID state?\n");
 			goto out;
 		}
 
-		rc = server->ops->check_receive(smb[i], server,
-						     flags & CIFS_LOG_ERROR);
+		rc = server->ops->check_receive(smb, server,
+						flags & CIFS_LOG_ERROR);
 
-		if (resp_iov) {
-			buf = (char *)smb[i]->resp_buf;
-			resp_iov[i].iov_base = buf;
-			resp_iov[i].iov_len = smb[i]->resp_buf_size +
+		if (smb->resp_iov) {
+			buf = (char *)smb->resp_buf;
+			smb->resp_iov->iov_base = buf;
+			smb->resp_iov->iov_len = smb->resp_buf_size +
 				HEADER_PREAMBLE_SIZE(server);
 
-			if (smb[i]->large_buf)
-				resp_buf_type[i] = CIFS_LARGE_BUFFER;
+			if (smb->large_buf)
+				*smb->resp_buf_type = CIFS_LARGE_BUFFER;
 			else
-				resp_buf_type[i] = CIFS_SMALL_BUFFER;
+				*smb->resp_buf_type = CIFS_SMALL_BUFFER;
 
 			/* mark it so buf will not be freed by delete_mid */
 			if ((flags & CIFS_NO_RSP_BUF) == 0)
-				smb[i]->resp_buf = NULL;
+				smb->resp_buf = NULL;
 		}
 	}
 
@@ -1226,17 +1237,13 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
 	 */
 	spin_lock(&ses->ses_lock);
 	if ((ses->ses_status == SES_NEW) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
-		struct kvec iov = {
-			.iov_base = resp_iov[0].iov_base,
-			.iov_len = resp_iov[0].iov_len
-		};
 		spin_unlock(&ses->ses_lock);
 		cifs_server_lock(server);
-		smb311_update_preauth_hash(ses, server, &iov, 1);
+		smb311_update_preauth_hash(ses, server, head_smb, true);
 		cifs_server_unlock(server);
-		spin_lock(&ses->ses_lock);
+	} else {
+		spin_unlock(&ses->ses_lock);
 	}
-	spin_unlock(&ses->ses_lock);
 
 out:
 	/*
@@ -1245,11 +1252,51 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
 	 * This is prevented above by using a noop callback that will not
 	 * wake this thread except for the very last PDU.
 	 */
-	for (i = 0; i < num_rqst; i++) {
-		if (!cancelled_mid[i])
-			delete_mid(smb[i]);
+	for (struct smb_message *smb = head_smb; smb; smb = smb->next)
+		if (!smb->cancelled)
+			discard_message(server, smb);
+
+	return rc;
+}
+
+int
+compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
+		   struct TCP_Server_Info *server,
+		   const int flags, const int num_rqst, struct smb_rqst *rqst,
+		   int *resp_buf_type, struct kvec *resp_iov)
+{
+	struct smb_message *head_smb = NULL, **ppsmb = &head_smb, *smb;
+	int rc = -ENOMEM;
+
+	if (!ses || !ses->server || !server) {
+		cifs_dbg(VFS, "Null session\n");
+		return -EIO;
+	}
+
+	for (int i = 0; i < num_rqst; i++) {
+		void *request = rqst[i].rq_iov[0].iov_base;
+		struct smb2_hdr *hdr = request;
+		enum smb2_command cmd = le16_to_cpu(hdr->Command);
+
+		smb = smb_message_alloc(cmd, GFP_NOFS);
+		if (!smb)
+			goto error;
+
+		*ppsmb = smb;
+		ppsmb = &smb->next;
+		smb->request		= request;
+		smb->rqst		= rqst[i];
+		smb->sr_flags		= flags;
+		smb->total_len		= smb_rqst_len(server, &smb->rqst);
+		smb->resp_buf_type	= &resp_buf_type[i];
+		smb->resp_iov		= &resp_iov[i];
+		resp_buf_type[i] = CIFS_NO_BUFFER;  /* no response buf yet */
 	}
 
+	rc = smb_send_recv_messages(xid, ses, server, head_smb, flags);
+
+error:
+	smb_put_messages(smb);
 	return rc;
 }
 


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ