linux-cifs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] cifs: when out of credits on one channel, retry on another.
@ 2021-04-26 16:18 Shyam Prasad N
  2021-04-27 23:13 ` Steve French
  2021-04-28 15:23 ` Aurélien Aptel
  0 siblings, 2 replies; 6+ messages in thread
From: Shyam Prasad N @ 2021-04-26 16:18 UTC (permalink / raw)
  To: Aurélien Aptel, Steve French, CIFS

[-- Attachment #1: Type: text/plain, Size: 305 bytes --]

Hi Aurelien/Steve,

Here is the multichannel out-of-credits related patch which we
discussed about a few weeks ago.
The idea is to retry on -EDEADLK (which we return when we run out of
credits and we have no requests in flight), so that another channel is
chosen for the next request.

-- 
Regards,
Shyam

[-- Attachment #2: 0001-cifs-when-out-of-credits-on-one-channel-retry-on-ano.patch --]
[-- Type: application/octet-stream, Size: 38181 bytes --]

From d8e0ecb1aa196a2fd86b6a78b52b4a7a91ac22c5 Mon Sep 17 00:00:00 2001
From: Shyam Prasad N <sprasad@microsoft.com>
Date: Mon, 26 Apr 2021 12:28:50 +0000
Subject: [PATCH] cifs: when out of credits on one channel, retry on another.

Following the fix:
smb3: fix crediting for compounding when only one request in flight

... we're returning -EDEADLK when we run out of credits on a
connection to the server, indicating a wrong accounting.

However, for a multichannel scenario, we should not return this error
straightaway, as there could be other channels that do have credits.
We must try all channels before we give up.

Today, we don't have a good way to know when to give up here.
So we keep retrying indefinitely. Also, this fix assumes that
cifs_pick_channel() selects a different channel on retry.
When a new channel allocation strategy is implemented, we should
make sure that we don't select a channel that ran out of credits,
unless it received credits from the server.

This fix mostly introduces a wrapper for all functions which call
cifs_pick_channel. In the wrapper function, the function is retried
when the error is -EDEADLK, and uses multichannel.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
---
 fs/cifs/file.c      |  96 +++++++++++-
 fs/cifs/smb2inode.c |  93 ++++++++----
 fs/cifs/smb2ops.c   | 113 +++++++++++++-
 fs/cifs/smb2pdu.c   | 359 ++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 593 insertions(+), 68 deletions(-)

diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 7e97aeabd616..7609b9739ce4 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -2378,7 +2378,7 @@ wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
 	return rc;
 }
 
-static int cifs_writepages(struct address_space *mapping,
+static int cifs_writepages_final(struct address_space *mapping,
 			   struct writeback_control *wbc)
 {
 	struct inode *inode = mapping->host;
@@ -2543,6 +2543,21 @@ static int cifs_writepages(struct address_space *mapping,
 	return rc;
 }
 
+static int cifs_writepages(struct address_space *mapping,
+			   struct writeback_control *wbc)
+{
+	int rc;
+	struct inode *inode = mapping->host;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+
+	do {
+		rc = cifs_writepages_final(mapping, wbc); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static int
 cifs_writepage_locked(struct page *page, struct writeback_control *wbc)
 {
@@ -2937,7 +2952,7 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
 }
 
 static int
-cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
+cifs_write_from_iter_final(loff_t offset, size_t len, struct iov_iter *from,
 		     struct cifsFileInfo *open_file,
 		     struct cifs_sb_info *cifs_sb, struct list_head *wdata_list,
 		     struct cifs_aio_ctx *ctx)
@@ -3102,6 +3117,24 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
 	return rc;
 }
 
+static int
+cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
+		     struct cifsFileInfo *open_file,
+		     struct cifs_sb_info *cifs_sb, struct list_head *wdata_list,
+		     struct cifs_aio_ctx *ctx)
+{
+	int rc;
+	struct cifs_tcon *tcon = tlink_tcon(open_file->tlink);
+
+	do {
+		rc = cifs_write_from_iter_final(offset, len, from,
+				open_file, cifs_sb,
+				wdata_list, ctx); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static void collect_uncached_write_data(struct cifs_aio_ctx *ctx)
 {
 	struct cifs_writedata *wdata, *tmp;
@@ -3671,7 +3704,7 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
 }
 
 static int
-cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
+cifs_send_async_read_final(loff_t offset, size_t len, struct cifsFileInfo *open_file,
 		     struct cifs_sb_info *cifs_sb, struct list_head *rdata_list,
 		     struct cifs_aio_ctx *ctx)
 {
@@ -3812,6 +3845,23 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
 	return rc;
 }
 
+static int
+cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
+		     struct cifs_sb_info *cifs_sb, struct list_head *rdata_list,
+		     struct cifs_aio_ctx *ctx)
+{
+	int rc;
+	struct cifs_tcon *tcon = tlink_tcon(open_file->tlink);
+
+	do {
+		rc = cifs_send_async_read_final(offset, len,
+				open_file, cifs_sb,
+				rdata_list, ctx); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static void
 collect_uncached_read_data(struct cifs_aio_ctx *ctx)
 {
@@ -4071,7 +4121,7 @@ cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to)
 }
 
 static ssize_t
-cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
+cifs_read_final(struct file *file, char *read_data, size_t read_size, loff_t *offset)
 {
 	int rc = -EACCES;
 	unsigned int bytes_read = 0;
@@ -4162,6 +4212,24 @@ cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
 	return total_read;
 }
 
+static ssize_t
+cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
+{
+	int rc;
+	struct cifsFileInfo *open_file;
+	struct cifs_tcon *tcon;
+
+	open_file = file->private_data;
+	tcon = tlink_tcon(open_file->tlink);
+
+	do {
+		rc = cifs_read_final(file, read_data,
+				read_size, offset); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  * If the page is mmap'ed into a process' page tables, then we need to make
  * sure that it doesn't change while being written back.
@@ -4412,7 +4480,7 @@ readpages_get_pages(struct address_space *mapping, struct list_head *page_list,
 	return rc;
 }
 
-static int cifs_readpages(struct file *file, struct address_space *mapping,
+static int cifs_readpages_final(struct file *file, struct address_space *mapping,
 	struct list_head *page_list, unsigned num_pages)
 {
 	int rc;
@@ -4567,6 +4635,24 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
 	return rc;
 }
 
+static int cifs_readpages(struct file *file, struct address_space *mapping,
+	struct list_head *page_list, unsigned num_pages)
+{
+	int rc;
+	struct cifsFileInfo *open_file;
+	struct cifs_tcon *tcon;
+
+	open_file = file->private_data;
+	tcon = tlink_tcon(open_file->tlink);
+
+	do {
+		rc = cifs_readpages_final(file, mapping,
+				page_list, num_pages); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  * cifs_readpage_worker must be called with the page pinned
  */
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 9a61209a283e..24c02a989d1d 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -60,7 +60,7 @@ struct cop_vars {
 };
 
 static int
-smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_compound_op_final(const unsigned int xid, struct cifs_tcon *tcon,
 		 struct cifs_sb_info *cifs_sb, const char *full_path,
 		 __u32 desired_access, __u32 create_disposition,
 		 __u32 create_options, umode_t mode, void *ptr, int command,
@@ -371,8 +371,6 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
 	num_rqst++;
 
 	if (cfile) {
-		cifsFileInfo_put(cfile);
-		cfile = NULL;
 		rc = compound_send_recv(xid, ses, server,
 					flags, num_rqst - 2,
 					&rqst[1], &resp_buftype[1],
@@ -384,9 +382,6 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
 					rsp_iov);
 
  finished:
-	if (cfile)
-		cifsFileInfo_put(cfile);
-
 	SMB2_open_free(&rqst[0]);
 	if (rc == -EREMCHG) {
 		pr_warn_once("server share %s deleted\n", tcon->treeName);
@@ -494,6 +489,43 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+		 struct cifs_sb_info *cifs_sb, const char *full_path,
+		 __u32 desired_access, __u32 create_disposition,
+		 __u32 create_options, umode_t mode, void *ptr, int command,
+		 bool check_open, bool writable_path)
+{
+	int flags;
+	int rc;
+	struct cifsFileInfo *cfile;
+
+	/* Check if we have the handle open */
+	if (check_open) {
+		if (writable_path) {
+			if (command == SMB2_OP_RENAME)
+				flags = FIND_WR_WITH_DELETE;
+			else
+				flags = FIND_WR_ANY;
+
+			cifs_get_writable_path(tcon, full_path, flags, &cfile);
+		} else
+			cifs_get_readable_path(tcon, full_path, &cfile);
+	}
+	
+	/* If we ran out of credits, and ses uses multichannel, try again on another chan */
+	do {
+		rc = smb2_compound_op_final(xid, tcon, cifs_sb, full_path,
+				desired_access, create_disposition, create_options,
+				mode, ptr, command, cfile);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	if (check_open && cfile)
+		cifsFileInfo_put(cfile);
+	
+	return rc;
+}
+
 void
 move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
 {
@@ -512,7 +544,6 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 	int rc;
 	struct smb2_file_all_info *smb2_data;
 	__u32 create_options = 0;
-	struct cifsFileInfo *cfile;
 	struct cached_fid *cfid = NULL;
 
 	*adjust_tz = false;
@@ -540,10 +571,11 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		goto out;
 	}
 
-	cifs_get_readable_path(tcon, full_path, &cfile);
 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
 			      FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
-			      ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO, cfile);
+			      ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO, 
+				  true, false);
+
 	if (rc == -EOPNOTSUPP) {
 		*reparse = true;
 		create_options |= OPEN_REPARSE_POINT;
@@ -552,7 +584,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
 				      FILE_READ_ATTRIBUTES, FILE_OPEN,
 				      create_options, ACL_NO_MODE,
-				      smb2_data, SMB2_OP_QUERY_INFO, NULL);
+				      smb2_data, SMB2_OP_QUERY_INFO, 
+					  false, false);
 	}
 	if (rc)
 		goto out;
@@ -571,7 +604,6 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 {
 	int rc;
 	__u32 create_options = 0;
-	struct cifsFileInfo *cfile;
 	struct smb311_posix_qinfo *smb2_data;
 
 	*adjust_tz = false;
@@ -590,10 +622,10 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 	 * (always using the compounded version).
 	 */
 
-	cifs_get_readable_path(tcon, full_path, &cfile);
 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
 			      FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
-			      ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
+			      ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO,
+				  true, false);
 	if (rc == -EOPNOTSUPP) {
 		/* BB TODO: When support for special files added to Samba re-verify this path */
 		*reparse = true;
@@ -603,7 +635,8 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
 				      FILE_READ_ATTRIBUTES, FILE_OPEN,
 				      create_options, ACL_NO_MODE,
-				      smb2_data, SMB2_OP_POSIX_QUERY_INFO, NULL);
+				      smb2_data, SMB2_OP_POSIX_QUERY_INFO,
+					  false, false);
 	}
 	if (rc)
 		goto out;
@@ -624,7 +657,7 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
 	return smb2_compound_op(xid, tcon, cifs_sb, name,
 				FILE_WRITE_ATTRIBUTES, FILE_CREATE,
 				CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR,
-				NULL);
+				false, false);
 }
 
 void
@@ -634,7 +667,6 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
 {
 	FILE_BASIC_INFO data;
 	struct cifsInodeInfo *cifs_i;
-	struct cifsFileInfo *cfile;
 	u32 dosattrs;
 	int tmprc;
 
@@ -642,11 +674,11 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
 	cifs_i = CIFS_I(inode);
 	dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
 	data.Attributes = cpu_to_le32(dosattrs);
-	cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
 	tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
 				 FILE_WRITE_ATTRIBUTES, FILE_CREATE,
 				 CREATE_NOT_FILE, ACL_NO_MODE,
-				 &data, SMB2_OP_SET_INFO, cfile);
+				 &data, SMB2_OP_SET_INFO,
+				 true, true);
 	if (tmprc == 0)
 		cifs_i->cifsAttrs = dosattrs;
 }
@@ -657,7 +689,8 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
 {
 	return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
 				CREATE_NOT_FILE, ACL_NO_MODE,
-				NULL, SMB2_OP_RMDIR, NULL);
+				NULL, SMB2_OP_RMDIR,
+				false, false);
 }
 
 int
@@ -666,14 +699,15 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
 {
 	return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
 				CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
-				ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL);
+				ACL_NO_MODE, NULL, SMB2_OP_DELETE,
+				false, false);
 }
 
 static int
 smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
 		   const char *from_name, const char *to_name,
 		   struct cifs_sb_info *cifs_sb, __u32 access, int command,
-		   struct cifsFileInfo *cfile)
+		   bool check_open, bool writable_path)
 {
 	__le16 *smb2_to_name = NULL;
 	int rc;
@@ -685,7 +719,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
 	}
 	rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
 			      FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name,
-			      command, cfile);
+			      command, check_open, writable_path);
 smb2_rename_path:
 	kfree(smb2_to_name);
 	return rc;
@@ -696,12 +730,9 @@ smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
 		 const char *from_name, const char *to_name,
 		 struct cifs_sb_info *cifs_sb)
 {
-	struct cifsFileInfo *cfile;
-
-	cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
-
 	return smb2_set_path_attr(xid, tcon, from_name, to_name,
-				  cifs_sb, DELETE, SMB2_OP_RENAME, cfile);
+				  cifs_sb, DELETE, SMB2_OP_RENAME,
+				  true, true);
 }
 
 int
@@ -711,7 +742,7 @@ smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
 {
 	return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
 				  FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK,
-				  NULL);
+				  false, false);
 }
 
 int
@@ -723,7 +754,8 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
 
 	return smb2_compound_op(xid, tcon, cifs_sb, full_path,
 				FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE,
-				&eof, SMB2_OP_SET_EOF, NULL);
+				&eof, SMB2_OP_SET_EOF,
+				false, false);
 }
 
 int
@@ -745,7 +777,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
 
 	rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path,
 			      FILE_WRITE_ATTRIBUTES, FILE_OPEN,
-			      0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, NULL);
+			      0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO,
+				  false, false);
 	cifs_put_tlink(tlink);
 	return rc;
 }
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 66b20762c2c9..9e3854e75df5 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -1221,7 +1221,7 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
 
 
 static int
-smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_set_ea_final(const unsigned int xid, struct cifs_tcon *tcon,
 	    const char *path, const char *ea_name, const void *ea_value,
 	    const __u16 ea_value_len, const struct nls_table *nls_codepage,
 	    struct cifs_sb_info *cifs_sb)
@@ -1376,6 +1376,23 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 	free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
 	return rc;
 }
+
+static int
+smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+	    const char *path, const char *ea_name, const void *ea_value,
+	    const __u16 ea_value_len, const struct nls_table *nls_codepage,
+	    struct cifs_sb_info *cifs_sb)
+{
+	int rc;
+
+	do {
+		rc = smb2_set_ea_final(xid, tcon, path,
+				ea_name, ea_value, ea_value_len,
+				nls_codepage, cifs_sb);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
 #endif
 
 static bool
@@ -1593,7 +1610,7 @@ struct iqi_vars {
 };
 
 static int
-smb2_ioctl_query_info(const unsigned int xid,
+smb2_ioctl_query_info_final(const unsigned int xid,
 		      struct cifs_tcon *tcon,
 		      struct cifs_sb_info *cifs_sb,
 		      __le16 *path, int is_dir,
@@ -1813,6 +1830,23 @@ smb2_ioctl_query_info(const unsigned int xid,
 	goto iqinf_exit;
 }
 
+static int
+smb2_ioctl_query_info(const unsigned int xid,
+		      struct cifs_tcon *tcon,
+		      struct cifs_sb_info *cifs_sb,
+		      __le16 *path, int is_dir,
+		      unsigned long p)
+{
+	int rc;
+
+	do {
+		rc = smb2_ioctl_query_info_final(xid, tcon, cifs_sb,
+				path, is_dir, p); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static ssize_t
 smb2_copychunk_range(const unsigned int xid,
 			struct cifsFileInfo *srcfile,
@@ -2302,7 +2336,7 @@ smb3_notify(const unsigned int xid, struct file *pfile,
 }
 
 static int
-smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_query_dir_first_final(const unsigned int xid, struct cifs_tcon *tcon,
 		     const char *path, struct cifs_sb_info *cifs_sb,
 		     struct cifs_fid *fid, __u16 search_flags,
 		     struct cifs_search_info *srch_inf)
@@ -2420,6 +2454,23 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+		     const char *path, struct cifs_sb_info *cifs_sb,
+		     struct cifs_fid *fid, __u16 search_flags,
+		     struct cifs_search_info *srch_inf)
+{
+	int rc;
+
+	do {
+		rc = smb2_query_dir_first_final(xid, tcon,
+				path, cifs_sb, fid, 
+				search_flags, srch_inf); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static int
 smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
 		    struct cifs_fid *fid, __u16 search_flags,
@@ -2613,7 +2664,7 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst)
  * Caller need to free this with free_rsp_buf().
  */
 int
-smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_query_info_compound_final(const unsigned int xid, struct cifs_tcon *tcon,
 			 __le16 *utf16_path, u32 desired_access,
 			 u32 class, u32 type, u32 output_len,
 			 struct kvec *rsp, int *buftype,
@@ -2705,6 +2756,25 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
+			 __le16 *utf16_path, u32 desired_access,
+			 u32 class, u32 type, u32 output_len,
+			 struct kvec *rsp, int *buftype,
+			 struct cifs_sb_info *cifs_sb)
+{
+	int rc;
+
+	do {
+		rc = smb2_query_info_compound_final(xid, tcon,
+				utf16_path, desired_access,
+				class, type, output_len,
+				rsp, buftype, cifs_sb);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static int
 smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
 	     struct cifs_sb_info *cifs_sb, struct kstatfs *buf)
@@ -3002,7 +3072,7 @@ parse_reparse_point(struct reparse_data_buffer *buf,
 	(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
 
 static int
-smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_query_symlink_final(const unsigned int xid, struct cifs_tcon *tcon,
 		   struct cifs_sb_info *cifs_sb, const char *full_path,
 		   char **target_path, bool is_reparse_point)
 {
@@ -3190,8 +3260,24 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
+		   struct cifs_sb_info *cifs_sb, const char *full_path,
+		   char **target_path, bool is_reparse_point)
+{
+	int rc;
+
+	do {
+		rc = smb2_query_symlink_final(xid, tcon,
+				cifs_sb, full_path, target_path,
+				is_reparse_point); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
-smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_query_reparse_tag_final(const unsigned int xid, struct cifs_tcon *tcon,
 		   struct cifs_sb_info *cifs_sb, const char *full_path,
 		   __u32 *tag)
 {
@@ -3317,6 +3403,21 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
+		   struct cifs_sb_info *cifs_sb, const char *full_path,
+		   __u32 *tag)
+{
+	int rc;
+
+	do {
+		rc = smb2_query_reparse_tag_final(xid, tcon,
+				cifs_sb, full_path, tag);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static struct cifs_ntsd *
 get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
 		    const struct cifs_fid *cifsfid, u32 *pacllen, u32 info)
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index e36c2a867783..9b38be5f5c9b 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -2475,7 +2475,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
 	return 0;
 }
 
-int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+int smb311_posix_mkdir_final(const unsigned int xid, struct inode *inode,
 			       umode_t mode, struct cifs_tcon *tcon,
 			       const char *full_path,
 			       struct cifs_sb_info *cifs_sb)
@@ -2628,6 +2628,22 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
 	return rc;
 }
 
+int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+			       umode_t mode, struct cifs_tcon *tcon,
+			       const char *full_path,
+			       struct cifs_sb_info *cifs_sb)
+{
+	int rc;
+
+	do {
+		rc = smb311_posix_mkdir_final(xid, inode,
+				mode, tcon, full_path,
+				cifs_sb); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
 	       struct smb_rqst *rqst, __u8 *oplock,
@@ -2823,7 +2839,7 @@ SMB2_open_free(struct smb_rqst *rqst)
 }
 
 int
-SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
+SMB2_open_final(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 	  __u8 *oplock, struct smb2_file_all_info *buf,
 	  struct create_posix_rsp *posix,
 	  struct kvec *err_iov, int *buftype)
@@ -2911,6 +2927,24 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 	return rc;
 }
 
+int
+SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
+	  __u8 *oplock, struct smb2_file_all_info *buf,
+	  struct create_posix_rsp *posix,
+	  struct kvec *err_iov, int *buftype)
+{
+	int rc;
+	struct cifs_tcon *tcon = oparms->tcon;
+
+	do {
+		rc = SMB2_open_final(xid, oparms, path,
+				oplock, buf,
+				posix, err_iov, buftype);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
 		struct smb_rqst *rqst,
@@ -3019,7 +3053,7 @@ SMB2_ioctl_free(struct smb_rqst *rqst)
  *	SMB2 IOCTL is used for both IOCTLs and FSCTLs
  */
 int
-SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+SMB2_ioctl_final(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	   u64 volatile_fid, u32 opcode, bool is_fsctl,
 	   char *in_data, u32 indatalen, u32 max_out_data_len,
 	   char **out_data, u32 *plen /* returned data len */)
@@ -3130,6 +3164,25 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	return rc;
 }
 
+int
+SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+	   u64 volatile_fid, u32 opcode, bool is_fsctl,
+	   char *in_data, u32 indatalen, u32 max_out_data_len,
+	   char **out_data, u32 *plen /* returned data len */)
+{
+	int rc;
+
+	do {
+		rc = SMB2_ioctl_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				opcode, is_fsctl,
+				in_data, indatalen,
+				max_out_data_len, out_data, plen);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  *   Individual callers to ioctl worker function follow
  */
@@ -3191,7 +3244,7 @@ SMB2_close_free(struct smb_rqst *rqst)
 }
 
 int
-__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
+__SMB2_close_final(const unsigned int xid, struct cifs_tcon *tcon,
 	     u64 persistent_fid, u64 volatile_fid,
 	     struct smb2_file_network_open_info *pbuf)
 {
@@ -3268,6 +3321,22 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
+	     u64 persistent_fid, u64 volatile_fid,
+	     struct smb2_file_network_open_info *pbuf)
+{
+	int rc;
+
+	do {
+		rc = __SMB2_close_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				pbuf);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
 		u64 persistent_fid, u64 volatile_fid)
@@ -3375,7 +3444,7 @@ SMB2_query_info_free(struct smb_rqst *rqst)
 }
 
 static int
-query_info(const unsigned int xid, struct cifs_tcon *tcon,
+query_info_final(const unsigned int xid, struct cifs_tcon *tcon,
 	   u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
 	   u32 additional_info, size_t output_len, size_t min_len, void **data,
 		u32 *dlen)
@@ -3462,6 +3531,26 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+query_info(const unsigned int xid, struct cifs_tcon *tcon,
+	   u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
+	   u32 additional_info, size_t output_len, size_t min_len, void **data,
+		u32 *dlen)
+{
+	int rc;
+
+	do {
+		rc = query_info_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				info_class, info_type,
+				additional_info,
+				output_len, min_len,
+				data, dlen);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
 	u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
 {
@@ -3549,7 +3638,7 @@ SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
 }
 
 int
-SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_change_notify_final(const unsigned int xid, struct cifs_tcon *tcon,
 		u64 persistent_fid, u64 volatile_fid, bool watch_tree,
 		u32 completion_filter)
 {
@@ -3600,7 +3689,21 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
+		u64 persistent_fid, u64 volatile_fid, bool watch_tree,
+		u32 completion_filter)
+{
+	int rc;
 
+	do {
+		rc = SMB2_change_notify_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				watch_tree, completion_filter);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
 
 /*
  * This is a no-op for now. We're not really interested in the reply, but
@@ -3764,7 +3867,7 @@ SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst,
 }
 
 int
-SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+SMB2_flush_final(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	   u64 volatile_fid)
 {
 	struct cifs_ses *ses = tcon->ses;
@@ -3811,6 +3914,20 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	return rc;
 }
 
+int
+SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+	   u64 volatile_fid)
+{
+	int rc;
+
+	do {
+		rc = SMB2_flush_final(xid, tcon,
+				persistent_fid, volatile_fid);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  * To form a chain of read requests, any read requests after the first should
  * have the end_of_chain boolean set to true.
@@ -3997,9 +4114,8 @@ smb2_readv_callback(struct mid_q_entry *mid)
 	add_credits(server, &credits, 0);
 }
 
-/* smb2_async_readv - send an async read, and set up mid to handle result */
 int
-smb2_async_readv(struct cifs_readdata *rdata)
+smb2_async_readv_final(struct cifs_readdata *rdata)
 {
 	int rc, flags = 0;
 	char *buf;
@@ -4069,8 +4185,24 @@ smb2_async_readv(struct cifs_readdata *rdata)
 	return rc;
 }
 
+/* smb2_async_readv - send an async read, and set up mid to handle result */
 int
-SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
+smb2_async_readv(struct cifs_readdata *rdata)
+{
+	int rc;
+	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+
+	do {
+		rc = smb2_async_readv_final(rdata);
+	} while (!rdata->server &&
+			tcon->ses->chan_count > 1 &&
+			rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+SMB2_read_final(const unsigned int xid, struct cifs_io_parms *io_parms,
 	  unsigned int *nbytes, char **buf, int *buf_type)
 {
 	struct smb_rqst rqst;
@@ -4149,6 +4281,23 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
 	return rc;
 }
 
+int
+SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
+	  unsigned int *nbytes, char **buf, int *buf_type)
+{
+	int rc;
+	struct cifs_tcon *tcon = io_parms->tcon;
+
+	do {
+		rc = SMB2_read_final(xid, io_parms,
+				nbytes, buf, buf_type);
+	} while (!io_parms->server &&
+			tcon->ses->chan_count > 1 &&
+			rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  * Check the mid_state and signature on received buffer (if any), and queue the
  * workqueue completion task.
@@ -4235,9 +4384,8 @@ smb2_writev_callback(struct mid_q_entry *mid)
 	add_credits(server, &credits, 0);
 }
 
-/* smb2_async_writev - send an async write, and set up mid to handle result */
 int
-smb2_async_writev(struct cifs_writedata *wdata,
+smb2_async_writev_final(struct cifs_writedata *wdata,
 		  void (*release)(struct kref *kref))
 {
 	int rc = -EACCES, flags = 0;
@@ -4373,14 +4521,25 @@ smb2_async_writev(struct cifs_writedata *wdata,
 	return rc;
 }
 
-/*
- * SMB2_write function gets iov pointer to kvec array with n_vec as a length.
- * The length field from io_parms must be at least 1 and indicates a number of
- * elements with data to write that begins with position 1 in iov array. All
- * data length is specified by count.
- */
+/* smb2_async_writev - send an async write, and set up mid to handle result */
 int
-SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+smb2_async_writev(struct cifs_writedata *wdata,
+		  void (*release)(struct kref *kref))
+{
+	int rc;
+	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+
+	do {
+		rc = smb2_async_writev_final(wdata, release);
+	} while (!wdata->server &&
+			tcon->ses->chan_count > 1 &&
+			rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+SMB2_write_final(const unsigned int xid, struct cifs_io_parms *io_parms,
 	   unsigned int *nbytes, struct kvec *iov, int n_vec)
 {
 	struct smb_rqst rqst;
@@ -4462,6 +4621,29 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
 	return rc;
 }
 
+/*
+ * SMB2_write function gets iov pointer to kvec array with n_vec as a length.
+ * The length field from io_parms must be at least 1 and indicates a number of
+ * elements with data to write that begins with position 1 in iov array. All
+ * data length is specified by count.
+ */
+int
+SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+	   unsigned int *nbytes, struct kvec *iov, int n_vec)
+{
+	int rc;
+	struct cifs_tcon *tcon = io_parms->tcon;
+
+	do {
+		rc = SMB2_write_final(xid, io_parms,
+				nbytes, iov, n_vec);
+	} while (!io_parms->server &&
+			tcon->ses->chan_count > 1 &&
+			rc == -EDEADLK);
+
+	return rc;
+}
+
 int posix_info_sid_size(const void *beg, const void *end)
 {
 	size_t subauth;
@@ -4761,7 +4943,7 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
 }
 
 int
-SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_query_directory_final(const unsigned int xid, struct cifs_tcon *tcon,
 		     u64 persistent_fid, u64 volatile_fid, int index,
 		     struct cifs_search_info *srch_inf)
 {
@@ -4830,6 +5012,22 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+		     u64 persistent_fid, u64 volatile_fid, int index,
+		     struct cifs_search_info *srch_inf)
+{
+	int rc;
+
+	do {
+		rc = SMB2_query_directory_final(xid, tcon,
+				persistent_fid, volatile_fid, index,
+				srch_inf);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
 		   struct smb_rqst *rqst,
@@ -4882,7 +5080,7 @@ SMB2_set_info_free(struct smb_rqst *rqst)
 }
 
 static int
-send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+send_set_info_final(const unsigned int xid, struct cifs_tcon *tcon,
 	       u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class,
 	       u8 info_type, u32 additional_info, unsigned int num,
 		void **data, unsigned int *size)
@@ -4941,6 +5139,25 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+	       u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class,
+	       u8 info_type, u32 additional_info, unsigned int num,
+		void **data, unsigned int *size)
+{
+	int rc;
+
+	do {
+		rc = send_set_info_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				pid, info_class, info_type,
+				additional_info,
+				num, data, size);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	     u64 volatile_fid, u32 pid, __le64 *eof)
@@ -4980,7 +5197,7 @@ SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_oplock_break_final(const unsigned int xid, struct cifs_tcon *tcon,
 		  const u64 persistent_fid, const u64 volatile_fid,
 		  __u8 oplock_level)
 {
@@ -5030,6 +5247,22 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+		  const u64 persistent_fid, const u64 volatile_fid,
+		  __u8 oplock_level)
+{
+	int rc;
+
+	do {
+		rc = SMB2_oplock_break_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				oplock_level);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 void
 smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
 			     struct kstatfs *kst)
@@ -5097,7 +5330,7 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon,
 }
 
 int
-SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
+SMB311_posix_qfs_info_final(const unsigned int xid, struct cifs_tcon *tcon,
 	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
 {
 	struct smb_rqst rqst;
@@ -5148,7 +5381,21 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
+	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
+{
+	int rc;
+
+	do {
+		rc = SMB311_posix_qfs_info_final(xid, tcon,
+				persistent_fid, volatile_fid, fsdata);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+SMB2_QFS_info_final(const unsigned int xid, struct cifs_tcon *tcon,
 	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
 {
 	struct smb_rqst rqst;
@@ -5199,7 +5446,21 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
+{
+	int rc;
+
+	do {
+		rc = SMB2_QFS_info_final(xid, tcon,
+				persistent_fid, volatile_fid, fsdata);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+SMB2_QFS_attr_final(const unsigned int xid, struct cifs_tcon *tcon,
 	      u64 persistent_fid, u64 volatile_fid, int level)
 {
 	struct smb_rqst rqst;
@@ -5284,7 +5545,21 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
+	      u64 persistent_fid, u64 volatile_fid, int level)
+{
+	int rc;
+
+	do {
+		rc = SMB2_QFS_attr_final(xid, tcon,
+				persistent_fid, volatile_fid, level);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+smb2_lockv_final(const unsigned int xid, struct cifs_tcon *tcon,
 	   const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
 	   const __u32 num_lock, struct smb2_lock_element *buf)
 {
@@ -5342,6 +5617,22 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+	   const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
+	   const __u32 num_lock, struct smb2_lock_element *buf)
+{
+	int rc;
+
+	do {
+		rc = smb2_lockv_final(xid, tcon,
+				persist_fid, volatile_fid, pid,
+				num_lock, buf); 
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
 	  const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
@@ -5360,7 +5651,7 @@ SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_lease_break_final(const unsigned int xid, struct cifs_tcon *tcon,
 		 __u8 *lease_key, const __le32 lease_state)
 {
 	struct smb_rqst rqst;
@@ -5418,3 +5709,17 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
 
 	return rc;
 }
+
+int
+SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
+		 __u8 *lease_key, const __le32 lease_state)
+{
+	int rc;
+
+	do {
+		rc = SMB2_lease_break_final(xid, tcon,
+				lease_key, lease_state);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
-- 
2.25.1


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

* Re: [PATCH] cifs: when out of credits on one channel, retry on another.
  2021-04-26 16:18 [PATCH] cifs: when out of credits on one channel, retry on another Shyam Prasad N
@ 2021-04-27 23:13 ` Steve French
  2021-04-28 15:23 ` Aurélien Aptel
  1 sibling, 0 replies; 6+ messages in thread
From: Steve French @ 2021-04-27 23:13 UTC (permalink / raw)
  To: Shyam Prasad N; +Cc: Aurélien Aptel, CIFS

[-- Attachment #1: Type: text/plain, Size: 632 bytes --]

updated your patch to fix various minor checkpatch warnings (see attached)

and now testing it with multichannel target
http://smb3-test-rhel-75.southcentralus.cloudapp.azure.com/#/builders/11/builds/36

On Mon, Apr 26, 2021 at 11:18 AM Shyam Prasad N <nspmangalore@gmail.com> wrote:
>
> Hi Aurelien/Steve,
>
> Here is the multichannel out-of-credits related patch which we
> discussed about a few weeks ago.
> The idea is to retry on -EDEADLK (which we return when we run out of
> credits and we have no requests in flight), so that another channel is
> chosen for the next request.
>
> --
> Regards,
> Shyam



-- 
Thanks,

Steve

[-- Attachment #2: 0001-cifs-when-out-of-credits-on-one-channel-retry-on-ano.patch --]
[-- Type: text/x-patch, Size: 38223 bytes --]

From 82722f5bb281fd1fc34f28b9fa3abf74c41b66fb Mon Sep 17 00:00:00 2001
From: Shyam Prasad N <sprasad@microsoft.com>
Date: Mon, 26 Apr 2021 12:28:50 +0000
Subject: [PATCH] cifs: when out of credits on one channel, retry on another.

Following the fix:
smb3: fix crediting for compounding when only one request in flight

... we're returning -EDEADLK when we run out of credits on a
connection to the server, indicating a wrong accounting.

However, for a multichannel scenario, we should not return this error
straightaway, as there could be other channels that do have credits.
We must try all channels before we give up.

Today, we don't have a good way to know when to give up here.
So we keep retrying indefinitely. Also, this fix assumes that
cifs_pick_channel() selects a different channel on retry.
When a new channel allocation strategy is implemented, we should
make sure that we don't select a channel that ran out of credits,
unless it received credits from the server.

This fix mostly introduces a wrapper for all functions which call
cifs_pick_channel. In the wrapper function, the function is retried
when the error is -EDEADLK, and uses multichannel.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/file.c      |  96 +++++++++++-
 fs/cifs/smb2inode.c |  93 ++++++++----
 fs/cifs/smb2ops.c   | 113 +++++++++++++-
 fs/cifs/smb2pdu.c   | 359 ++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 593 insertions(+), 68 deletions(-)

diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 3d4e6e7dac1d..5cf3ce25ffa2 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -2316,7 +2316,7 @@ wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
 	return rc;
 }
 
-static int cifs_writepages(struct address_space *mapping,
+static int cifs_writepages_final(struct address_space *mapping,
 			   struct writeback_control *wbc)
 {
 	struct inode *inode = mapping->host;
@@ -2479,6 +2479,21 @@ static int cifs_writepages(struct address_space *mapping,
 	return rc;
 }
 
+static int cifs_writepages(struct address_space *mapping,
+			   struct writeback_control *wbc)
+{
+	int rc;
+	struct inode *inode = mapping->host;
+	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+
+	do {
+		rc = cifs_writepages_final(mapping, wbc);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static int
 cifs_writepage_locked(struct page *page, struct writeback_control *wbc)
 {
@@ -2871,7 +2886,7 @@ cifs_resend_wdata(struct cifs_writedata *wdata, struct list_head *wdata_list,
 }
 
 static int
-cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
+cifs_write_from_iter_final(loff_t offset, size_t len, struct iov_iter *from,
 		     struct cifsFileInfo *open_file,
 		     struct cifs_sb_info *cifs_sb, struct list_head *wdata_list,
 		     struct cifs_aio_ctx *ctx)
@@ -3036,6 +3051,24 @@ cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
 	return rc;
 }
 
+static int
+cifs_write_from_iter(loff_t offset, size_t len, struct iov_iter *from,
+		     struct cifsFileInfo *open_file,
+		     struct cifs_sb_info *cifs_sb, struct list_head *wdata_list,
+		     struct cifs_aio_ctx *ctx)
+{
+	int rc;
+	struct cifs_tcon *tcon = tlink_tcon(open_file->tlink);
+
+	do {
+		rc = cifs_write_from_iter_final(offset, len, from,
+				open_file, cifs_sb,
+				wdata_list, ctx);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static void collect_uncached_write_data(struct cifs_aio_ctx *ctx)
 {
 	struct cifs_writedata *wdata, *tmp;
@@ -3605,7 +3638,7 @@ static int cifs_resend_rdata(struct cifs_readdata *rdata,
 }
 
 static int
-cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
+cifs_send_async_read_final(loff_t offset, size_t len, struct cifsFileInfo *open_file,
 		     struct cifs_sb_info *cifs_sb, struct list_head *rdata_list,
 		     struct cifs_aio_ctx *ctx)
 {
@@ -3746,6 +3779,23 @@ cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
 	return rc;
 }
 
+static int
+cifs_send_async_read(loff_t offset, size_t len, struct cifsFileInfo *open_file,
+		     struct cifs_sb_info *cifs_sb, struct list_head *rdata_list,
+		     struct cifs_aio_ctx *ctx)
+{
+	int rc;
+	struct cifs_tcon *tcon = tlink_tcon(open_file->tlink);
+
+	do {
+		rc = cifs_send_async_read_final(offset, len,
+				open_file, cifs_sb,
+				rdata_list, ctx);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static void
 collect_uncached_read_data(struct cifs_aio_ctx *ctx)
 {
@@ -4005,7 +4055,7 @@ cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to)
 }
 
 static ssize_t
-cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
+cifs_read_final(struct file *file, char *read_data, size_t read_size, loff_t *offset)
 {
 	int rc = -EACCES;
 	unsigned int bytes_read = 0;
@@ -4096,6 +4146,24 @@ cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
 	return total_read;
 }
 
+static ssize_t
+cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
+{
+	int rc;
+	struct cifsFileInfo *open_file;
+	struct cifs_tcon *tcon;
+
+	open_file = file->private_data;
+	tcon = tlink_tcon(open_file->tlink);
+
+	do {
+		rc = cifs_read_final(file, read_data,
+				read_size, offset);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  * If the page is mmap'ed into a process' page tables, then we need to make
  * sure that it doesn't change while being written back.
@@ -4346,7 +4414,7 @@ readpages_get_pages(struct address_space *mapping, struct list_head *page_list,
 	return rc;
 }
 
-static int cifs_readpages(struct file *file, struct address_space *mapping,
+static int cifs_readpages_final(struct file *file, struct address_space *mapping,
 	struct list_head *page_list, unsigned num_pages)
 {
 	int rc;
@@ -4501,6 +4569,24 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
 	return rc;
 }
 
+static int cifs_readpages(struct file *file, struct address_space *mapping,
+	struct list_head *page_list, unsigned int num_pages)
+{
+	int rc;
+	struct cifsFileInfo *open_file;
+	struct cifs_tcon *tcon;
+
+	open_file = file->private_data;
+	tcon = tlink_tcon(open_file->tlink);
+
+	do {
+		rc = cifs_readpages_final(file, mapping,
+				page_list, num_pages);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  * cifs_readpage_worker must be called with the page pinned
  */
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 9a61209a283e..24c02a989d1d 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -60,7 +60,7 @@ struct cop_vars {
 };
 
 static int
-smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_compound_op_final(const unsigned int xid, struct cifs_tcon *tcon,
 		 struct cifs_sb_info *cifs_sb, const char *full_path,
 		 __u32 desired_access, __u32 create_disposition,
 		 __u32 create_options, umode_t mode, void *ptr, int command,
@@ -371,8 +371,6 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
 	num_rqst++;
 
 	if (cfile) {
-		cifsFileInfo_put(cfile);
-		cfile = NULL;
 		rc = compound_send_recv(xid, ses, server,
 					flags, num_rqst - 2,
 					&rqst[1], &resp_buftype[1],
@@ -384,9 +382,6 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
 					rsp_iov);
 
  finished:
-	if (cfile)
-		cifsFileInfo_put(cfile);
-
 	SMB2_open_free(&rqst[0]);
 	if (rc == -EREMCHG) {
 		pr_warn_once("server share %s deleted\n", tcon->treeName);
@@ -494,6 +489,43 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
+		 struct cifs_sb_info *cifs_sb, const char *full_path,
+		 __u32 desired_access, __u32 create_disposition,
+		 __u32 create_options, umode_t mode, void *ptr, int command,
+		 bool check_open, bool writable_path)
+{
+	int flags;
+	int rc;
+	struct cifsFileInfo *cfile;
+
+	/* Check if we have the handle open */
+	if (check_open) {
+		if (writable_path) {
+			if (command == SMB2_OP_RENAME)
+				flags = FIND_WR_WITH_DELETE;
+			else
+				flags = FIND_WR_ANY;
+
+			cifs_get_writable_path(tcon, full_path, flags, &cfile);
+		} else
+			cifs_get_readable_path(tcon, full_path, &cfile);
+	}
+
+	/* If we ran out of credits, and ses uses multichannel, try again on another chan */
+	do {
+		rc = smb2_compound_op_final(xid, tcon, cifs_sb, full_path,
+				desired_access, create_disposition, create_options,
+				mode, ptr, command, cfile);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	if (check_open && cfile)
+		cifsFileInfo_put(cfile);
+
+	return rc;
+}
+
 void
 move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
 {
@@ -512,7 +544,6 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 	int rc;
 	struct smb2_file_all_info *smb2_data;
 	__u32 create_options = 0;
-	struct cifsFileInfo *cfile;
 	struct cached_fid *cfid = NULL;
 
 	*adjust_tz = false;
@@ -540,10 +571,11 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		goto out;
 	}
 
-	cifs_get_readable_path(tcon, full_path, &cfile);
 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
 			      FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
-			      ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO, cfile);
+			      ACL_NO_MODE, smb2_data, SMB2_OP_QUERY_INFO,
+				  true, false);
+
 	if (rc == -EOPNOTSUPP) {
 		*reparse = true;
 		create_options |= OPEN_REPARSE_POINT;
@@ -552,7 +584,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
 				      FILE_READ_ATTRIBUTES, FILE_OPEN,
 				      create_options, ACL_NO_MODE,
-				      smb2_data, SMB2_OP_QUERY_INFO, NULL);
+				      smb2_data, SMB2_OP_QUERY_INFO,
+					  false, false);
 	}
 	if (rc)
 		goto out;
@@ -571,7 +604,6 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 {
 	int rc;
 	__u32 create_options = 0;
-	struct cifsFileInfo *cfile;
 	struct smb311_posix_qinfo *smb2_data;
 
 	*adjust_tz = false;
@@ -590,10 +622,10 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 	 * (always using the compounded version).
 	 */
 
-	cifs_get_readable_path(tcon, full_path, &cfile);
 	rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
 			      FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
-			      ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
+			      ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO,
+				  true, false);
 	if (rc == -EOPNOTSUPP) {
 		/* BB TODO: When support for special files added to Samba re-verify this path */
 		*reparse = true;
@@ -603,7 +635,8 @@ smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
 				      FILE_READ_ATTRIBUTES, FILE_OPEN,
 				      create_options, ACL_NO_MODE,
-				      smb2_data, SMB2_OP_POSIX_QUERY_INFO, NULL);
+				      smb2_data, SMB2_OP_POSIX_QUERY_INFO,
+					  false, false);
 	}
 	if (rc)
 		goto out;
@@ -624,7 +657,7 @@ smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
 	return smb2_compound_op(xid, tcon, cifs_sb, name,
 				FILE_WRITE_ATTRIBUTES, FILE_CREATE,
 				CREATE_NOT_FILE, mode, NULL, SMB2_OP_MKDIR,
-				NULL);
+				false, false);
 }
 
 void
@@ -634,7 +667,6 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
 {
 	FILE_BASIC_INFO data;
 	struct cifsInodeInfo *cifs_i;
-	struct cifsFileInfo *cfile;
 	u32 dosattrs;
 	int tmprc;
 
@@ -642,11 +674,11 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
 	cifs_i = CIFS_I(inode);
 	dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
 	data.Attributes = cpu_to_le32(dosattrs);
-	cifs_get_writable_path(tcon, name, FIND_WR_ANY, &cfile);
 	tmprc = smb2_compound_op(xid, tcon, cifs_sb, name,
 				 FILE_WRITE_ATTRIBUTES, FILE_CREATE,
 				 CREATE_NOT_FILE, ACL_NO_MODE,
-				 &data, SMB2_OP_SET_INFO, cfile);
+				 &data, SMB2_OP_SET_INFO,
+				 true, true);
 	if (tmprc == 0)
 		cifs_i->cifsAttrs = dosattrs;
 }
@@ -657,7 +689,8 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
 {
 	return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
 				CREATE_NOT_FILE, ACL_NO_MODE,
-				NULL, SMB2_OP_RMDIR, NULL);
+				NULL, SMB2_OP_RMDIR,
+				false, false);
 }
 
 int
@@ -666,14 +699,15 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
 {
 	return smb2_compound_op(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
 				CREATE_DELETE_ON_CLOSE | OPEN_REPARSE_POINT,
-				ACL_NO_MODE, NULL, SMB2_OP_DELETE, NULL);
+				ACL_NO_MODE, NULL, SMB2_OP_DELETE,
+				false, false);
 }
 
 static int
 smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
 		   const char *from_name, const char *to_name,
 		   struct cifs_sb_info *cifs_sb, __u32 access, int command,
-		   struct cifsFileInfo *cfile)
+		   bool check_open, bool writable_path)
 {
 	__le16 *smb2_to_name = NULL;
 	int rc;
@@ -685,7 +719,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
 	}
 	rc = smb2_compound_op(xid, tcon, cifs_sb, from_name, access,
 			      FILE_OPEN, 0, ACL_NO_MODE, smb2_to_name,
-			      command, cfile);
+			      command, check_open, writable_path);
 smb2_rename_path:
 	kfree(smb2_to_name);
 	return rc;
@@ -696,12 +730,9 @@ smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
 		 const char *from_name, const char *to_name,
 		 struct cifs_sb_info *cifs_sb)
 {
-	struct cifsFileInfo *cfile;
-
-	cifs_get_writable_path(tcon, from_name, FIND_WR_WITH_DELETE, &cfile);
-
 	return smb2_set_path_attr(xid, tcon, from_name, to_name,
-				  cifs_sb, DELETE, SMB2_OP_RENAME, cfile);
+				  cifs_sb, DELETE, SMB2_OP_RENAME,
+				  true, true);
 }
 
 int
@@ -711,7 +742,7 @@ smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
 {
 	return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
 				  FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK,
-				  NULL);
+				  false, false);
 }
 
 int
@@ -723,7 +754,8 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
 
 	return smb2_compound_op(xid, tcon, cifs_sb, full_path,
 				FILE_WRITE_DATA, FILE_OPEN, 0, ACL_NO_MODE,
-				&eof, SMB2_OP_SET_EOF, NULL);
+				&eof, SMB2_OP_SET_EOF,
+				false, false);
 }
 
 int
@@ -745,7 +777,8 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
 
 	rc = smb2_compound_op(xid, tlink_tcon(tlink), cifs_sb, full_path,
 			      FILE_WRITE_ATTRIBUTES, FILE_OPEN,
-			      0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO, NULL);
+			      0, ACL_NO_MODE, buf, SMB2_OP_SET_INFO,
+				  false, false);
 	cifs_put_tlink(tlink);
 	return rc;
 }
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index dd0eb665b680..5bcfa7904fba 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -1222,7 +1222,7 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
 
 
 static int
-smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_set_ea_final(const unsigned int xid, struct cifs_tcon *tcon,
 	    const char *path, const char *ea_name, const void *ea_value,
 	    const __u16 ea_value_len, const struct nls_table *nls_codepage,
 	    struct cifs_sb_info *cifs_sb)
@@ -1377,6 +1377,23 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 	free_rsp_buf(resp_buftype[2], rsp_iov[2].iov_base);
 	return rc;
 }
+
+static int
+smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
+	    const char *path, const char *ea_name, const void *ea_value,
+	    const __u16 ea_value_len, const struct nls_table *nls_codepage,
+	    struct cifs_sb_info *cifs_sb)
+{
+	int rc;
+
+	do {
+		rc = smb2_set_ea_final(xid, tcon, path,
+				ea_name, ea_value, ea_value_len,
+				nls_codepage, cifs_sb);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
 #endif
 
 static bool
@@ -1597,7 +1614,7 @@ struct iqi_vars {
 };
 
 static int
-smb2_ioctl_query_info(const unsigned int xid,
+smb2_ioctl_query_info_final(const unsigned int xid,
 		      struct cifs_tcon *tcon,
 		      struct cifs_sb_info *cifs_sb,
 		      __le16 *path, int is_dir,
@@ -1817,6 +1834,23 @@ smb2_ioctl_query_info(const unsigned int xid,
 	goto iqinf_exit;
 }
 
+static int
+smb2_ioctl_query_info(const unsigned int xid,
+		      struct cifs_tcon *tcon,
+		      struct cifs_sb_info *cifs_sb,
+		      __le16 *path, int is_dir,
+		      unsigned long p)
+{
+	int rc;
+
+	do {
+		rc = smb2_ioctl_query_info_final(xid, tcon, cifs_sb,
+				path, is_dir, p);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static ssize_t
 smb2_copychunk_range(const unsigned int xid,
 			struct cifsFileInfo *srcfile,
@@ -2306,7 +2340,7 @@ smb3_notify(const unsigned int xid, struct file *pfile,
 }
 
 static int
-smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_query_dir_first_final(const unsigned int xid, struct cifs_tcon *tcon,
 		     const char *path, struct cifs_sb_info *cifs_sb,
 		     struct cifs_fid *fid, __u16 search_flags,
 		     struct cifs_search_info *srch_inf)
@@ -2424,6 +2458,23 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+		     const char *path, struct cifs_sb_info *cifs_sb,
+		     struct cifs_fid *fid, __u16 search_flags,
+		     struct cifs_search_info *srch_inf)
+{
+	int rc;
+
+	do {
+		rc = smb2_query_dir_first_final(xid, tcon,
+				path, cifs_sb, fid,
+				search_flags, srch_inf);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static int
 smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
 		    struct cifs_fid *fid, __u16 search_flags,
@@ -2617,7 +2668,7 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst)
  * Caller need to free this with free_rsp_buf().
  */
 int
-smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_query_info_compound_final(const unsigned int xid, struct cifs_tcon *tcon,
 			 __le16 *utf16_path, u32 desired_access,
 			 u32 class, u32 type, u32 output_len,
 			 struct kvec *rsp, int *buftype,
@@ -2709,6 +2760,25 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
+			 __le16 *utf16_path, u32 desired_access,
+			 u32 class, u32 type, u32 output_len,
+			 struct kvec *rsp, int *buftype,
+			 struct cifs_sb_info *cifs_sb)
+{
+	int rc;
+
+	do {
+		rc = smb2_query_info_compound_final(xid, tcon,
+				utf16_path, desired_access,
+				class, type, output_len,
+				rsp, buftype, cifs_sb);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static int
 smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
 	     struct cifs_sb_info *cifs_sb, struct kstatfs *buf)
@@ -3006,7 +3076,7 @@ parse_reparse_point(struct reparse_data_buffer *buf,
 	(sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
 
 static int
-smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_query_symlink_final(const unsigned int xid, struct cifs_tcon *tcon,
 		   struct cifs_sb_info *cifs_sb, const char *full_path,
 		   char **target_path, bool is_reparse_point)
 {
@@ -3194,8 +3264,24 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,
+		   struct cifs_sb_info *cifs_sb, const char *full_path,
+		   char **target_path, bool is_reparse_point)
+{
+	int rc;
+
+	do {
+		rc = smb2_query_symlink_final(xid, tcon,
+				cifs_sb, full_path, target_path,
+				is_reparse_point);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
-smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
+smb2_query_reparse_tag_final(const unsigned int xid, struct cifs_tcon *tcon,
 		   struct cifs_sb_info *cifs_sb, const char *full_path,
 		   __u32 *tag)
 {
@@ -3321,6 +3407,21 @@ smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
+		   struct cifs_sb_info *cifs_sb, const char *full_path,
+		   __u32 *tag)
+{
+	int rc;
+
+	do {
+		rc = smb2_query_reparse_tag_final(xid, tcon,
+				cifs_sb, full_path, tag);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 static struct cifs_ntsd *
 get_smb2_acl_by_fid(struct cifs_sb_info *cifs_sb,
 		    const struct cifs_fid *cifsfid, u32 *pacllen, u32 info)
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index e36c2a867783..9b38be5f5c9b 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -2475,7 +2475,7 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
 	return 0;
 }
 
-int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+int smb311_posix_mkdir_final(const unsigned int xid, struct inode *inode,
 			       umode_t mode, struct cifs_tcon *tcon,
 			       const char *full_path,
 			       struct cifs_sb_info *cifs_sb)
@@ -2628,6 +2628,22 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
 	return rc;
 }
 
+int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
+			       umode_t mode, struct cifs_tcon *tcon,
+			       const char *full_path,
+			       struct cifs_sb_info *cifs_sb)
+{
+	int rc;
+
+	do {
+		rc = smb311_posix_mkdir_final(xid, inode,
+				mode, tcon, full_path,
+				cifs_sb);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
 	       struct smb_rqst *rqst, __u8 *oplock,
@@ -2823,7 +2839,7 @@ SMB2_open_free(struct smb_rqst *rqst)
 }
 
 int
-SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
+SMB2_open_final(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 	  __u8 *oplock, struct smb2_file_all_info *buf,
 	  struct create_posix_rsp *posix,
 	  struct kvec *err_iov, int *buftype)
@@ -2911,6 +2927,24 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 	return rc;
 }
 
+int
+SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
+	  __u8 *oplock, struct smb2_file_all_info *buf,
+	  struct create_posix_rsp *posix,
+	  struct kvec *err_iov, int *buftype)
+{
+	int rc;
+	struct cifs_tcon *tcon = oparms->tcon;
+
+	do {
+		rc = SMB2_open_final(xid, oparms, path,
+				oplock, buf,
+				posix, err_iov, buftype);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
 		struct smb_rqst *rqst,
@@ -3019,7 +3053,7 @@ SMB2_ioctl_free(struct smb_rqst *rqst)
  *	SMB2 IOCTL is used for both IOCTLs and FSCTLs
  */
 int
-SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+SMB2_ioctl_final(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	   u64 volatile_fid, u32 opcode, bool is_fsctl,
 	   char *in_data, u32 indatalen, u32 max_out_data_len,
 	   char **out_data, u32 *plen /* returned data len */)
@@ -3130,6 +3164,25 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	return rc;
 }
 
+int
+SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+	   u64 volatile_fid, u32 opcode, bool is_fsctl,
+	   char *in_data, u32 indatalen, u32 max_out_data_len,
+	   char **out_data, u32 *plen /* returned data len */)
+{
+	int rc;
+
+	do {
+		rc = SMB2_ioctl_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				opcode, is_fsctl,
+				in_data, indatalen,
+				max_out_data_len, out_data, plen);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  *   Individual callers to ioctl worker function follow
  */
@@ -3191,7 +3244,7 @@ SMB2_close_free(struct smb_rqst *rqst)
 }
 
 int
-__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
+__SMB2_close_final(const unsigned int xid, struct cifs_tcon *tcon,
 	     u64 persistent_fid, u64 volatile_fid,
 	     struct smb2_file_network_open_info *pbuf)
 {
@@ -3268,6 +3321,22 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
+	     u64 persistent_fid, u64 volatile_fid,
+	     struct smb2_file_network_open_info *pbuf)
+{
+	int rc;
+
+	do {
+		rc = __SMB2_close_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				pbuf);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
 		u64 persistent_fid, u64 volatile_fid)
@@ -3375,7 +3444,7 @@ SMB2_query_info_free(struct smb_rqst *rqst)
 }
 
 static int
-query_info(const unsigned int xid, struct cifs_tcon *tcon,
+query_info_final(const unsigned int xid, struct cifs_tcon *tcon,
 	   u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
 	   u32 additional_info, size_t output_len, size_t min_len, void **data,
 		u32 *dlen)
@@ -3462,6 +3531,26 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+query_info(const unsigned int xid, struct cifs_tcon *tcon,
+	   u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
+	   u32 additional_info, size_t output_len, size_t min_len, void **data,
+		u32 *dlen)
+{
+	int rc;
+
+	do {
+		rc = query_info_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				info_class, info_type,
+				additional_info,
+				output_len, min_len,
+				data, dlen);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
 	u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
 {
@@ -3549,7 +3638,7 @@ SMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
 }
 
 int
-SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_change_notify_final(const unsigned int xid, struct cifs_tcon *tcon,
 		u64 persistent_fid, u64 volatile_fid, bool watch_tree,
 		u32 completion_filter)
 {
@@ -3600,7 +3689,21 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
+		u64 persistent_fid, u64 volatile_fid, bool watch_tree,
+		u32 completion_filter)
+{
+	int rc;
 
+	do {
+		rc = SMB2_change_notify_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				watch_tree, completion_filter);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
 
 /*
  * This is a no-op for now. We're not really interested in the reply, but
@@ -3764,7 +3867,7 @@ SMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst,
 }
 
 int
-SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+SMB2_flush_final(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	   u64 volatile_fid)
 {
 	struct cifs_ses *ses = tcon->ses;
@@ -3811,6 +3914,20 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	return rc;
 }
 
+int
+SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+	   u64 volatile_fid)
+{
+	int rc;
+
+	do {
+		rc = SMB2_flush_final(xid, tcon,
+				persistent_fid, volatile_fid);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  * To form a chain of read requests, any read requests after the first should
  * have the end_of_chain boolean set to true.
@@ -3997,9 +4114,8 @@ smb2_readv_callback(struct mid_q_entry *mid)
 	add_credits(server, &credits, 0);
 }
 
-/* smb2_async_readv - send an async read, and set up mid to handle result */
 int
-smb2_async_readv(struct cifs_readdata *rdata)
+smb2_async_readv_final(struct cifs_readdata *rdata)
 {
 	int rc, flags = 0;
 	char *buf;
@@ -4069,8 +4185,24 @@ smb2_async_readv(struct cifs_readdata *rdata)
 	return rc;
 }
 
+/* smb2_async_readv - send an async read, and set up mid to handle result */
 int
-SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
+smb2_async_readv(struct cifs_readdata *rdata)
+{
+	int rc;
+	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+
+	do {
+		rc = smb2_async_readv_final(rdata);
+	} while (!rdata->server &&
+			tcon->ses->chan_count > 1 &&
+			rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+SMB2_read_final(const unsigned int xid, struct cifs_io_parms *io_parms,
 	  unsigned int *nbytes, char **buf, int *buf_type)
 {
 	struct smb_rqst rqst;
@@ -4149,6 +4281,23 @@ SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
 	return rc;
 }
 
+int
+SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
+	  unsigned int *nbytes, char **buf, int *buf_type)
+{
+	int rc;
+	struct cifs_tcon *tcon = io_parms->tcon;
+
+	do {
+		rc = SMB2_read_final(xid, io_parms,
+				nbytes, buf, buf_type);
+	} while (!io_parms->server &&
+			tcon->ses->chan_count > 1 &&
+			rc == -EDEADLK);
+
+	return rc;
+}
+
 /*
  * Check the mid_state and signature on received buffer (if any), and queue the
  * workqueue completion task.
@@ -4235,9 +4384,8 @@ smb2_writev_callback(struct mid_q_entry *mid)
 	add_credits(server, &credits, 0);
 }
 
-/* smb2_async_writev - send an async write, and set up mid to handle result */
 int
-smb2_async_writev(struct cifs_writedata *wdata,
+smb2_async_writev_final(struct cifs_writedata *wdata,
 		  void (*release)(struct kref *kref))
 {
 	int rc = -EACCES, flags = 0;
@@ -4373,14 +4521,25 @@ smb2_async_writev(struct cifs_writedata *wdata,
 	return rc;
 }
 
-/*
- * SMB2_write function gets iov pointer to kvec array with n_vec as a length.
- * The length field from io_parms must be at least 1 and indicates a number of
- * elements with data to write that begins with position 1 in iov array. All
- * data length is specified by count.
- */
+/* smb2_async_writev - send an async write, and set up mid to handle result */
 int
-SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+smb2_async_writev(struct cifs_writedata *wdata,
+		  void (*release)(struct kref *kref))
+{
+	int rc;
+	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+
+	do {
+		rc = smb2_async_writev_final(wdata, release);
+	} while (!wdata->server &&
+			tcon->ses->chan_count > 1 &&
+			rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+SMB2_write_final(const unsigned int xid, struct cifs_io_parms *io_parms,
 	   unsigned int *nbytes, struct kvec *iov, int n_vec)
 {
 	struct smb_rqst rqst;
@@ -4462,6 +4621,29 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
 	return rc;
 }
 
+/*
+ * SMB2_write function gets iov pointer to kvec array with n_vec as a length.
+ * The length field from io_parms must be at least 1 and indicates a number of
+ * elements with data to write that begins with position 1 in iov array. All
+ * data length is specified by count.
+ */
+int
+SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+	   unsigned int *nbytes, struct kvec *iov, int n_vec)
+{
+	int rc;
+	struct cifs_tcon *tcon = io_parms->tcon;
+
+	do {
+		rc = SMB2_write_final(xid, io_parms,
+				nbytes, iov, n_vec);
+	} while (!io_parms->server &&
+			tcon->ses->chan_count > 1 &&
+			rc == -EDEADLK);
+
+	return rc;
+}
+
 int posix_info_sid_size(const void *beg, const void *end)
 {
 	size_t subauth;
@@ -4761,7 +4943,7 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
 }
 
 int
-SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_query_directory_final(const unsigned int xid, struct cifs_tcon *tcon,
 		     u64 persistent_fid, u64 volatile_fid, int index,
 		     struct cifs_search_info *srch_inf)
 {
@@ -4830,6 +5012,22 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+		     u64 persistent_fid, u64 volatile_fid, int index,
+		     struct cifs_search_info *srch_inf)
+{
+	int rc;
+
+	do {
+		rc = SMB2_query_directory_final(xid, tcon,
+				persistent_fid, volatile_fid, index,
+				srch_inf);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
 		   struct smb_rqst *rqst,
@@ -4882,7 +5080,7 @@ SMB2_set_info_free(struct smb_rqst *rqst)
 }
 
 static int
-send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+send_set_info_final(const unsigned int xid, struct cifs_tcon *tcon,
 	       u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class,
 	       u8 info_type, u32 additional_info, unsigned int num,
 		void **data, unsigned int *size)
@@ -4941,6 +5139,25 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+static int
+send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+	       u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class,
+	       u8 info_type, u32 additional_info, unsigned int num,
+		void **data, unsigned int *size)
+{
+	int rc;
+
+	do {
+		rc = send_set_info_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				pid, info_class, info_type,
+				additional_info,
+				num, data, size);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 	     u64 volatile_fid, u32 pid, __le64 *eof)
@@ -4980,7 +5197,7 @@ SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_oplock_break_final(const unsigned int xid, struct cifs_tcon *tcon,
 		  const u64 persistent_fid, const u64 volatile_fid,
 		  __u8 oplock_level)
 {
@@ -5030,6 +5247,22 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+		  const u64 persistent_fid, const u64 volatile_fid,
+		  __u8 oplock_level)
+{
+	int rc;
+
+	do {
+		rc = SMB2_oplock_break_final(xid, tcon,
+				persistent_fid, volatile_fid,
+				oplock_level);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 void
 smb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
 			     struct kstatfs *kst)
@@ -5097,7 +5330,7 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon,
 }
 
 int
-SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
+SMB311_posix_qfs_info_final(const unsigned int xid, struct cifs_tcon *tcon,
 	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
 {
 	struct smb_rqst rqst;
@@ -5148,7 +5381,21 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
+	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
+{
+	int rc;
+
+	do {
+		rc = SMB311_posix_qfs_info_final(xid, tcon,
+				persistent_fid, volatile_fid, fsdata);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+SMB2_QFS_info_final(const unsigned int xid, struct cifs_tcon *tcon,
 	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
 {
 	struct smb_rqst rqst;
@@ -5199,7 +5446,21 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
+{
+	int rc;
+
+	do {
+		rc = SMB2_QFS_info_final(xid, tcon,
+				persistent_fid, volatile_fid, fsdata);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+SMB2_QFS_attr_final(const unsigned int xid, struct cifs_tcon *tcon,
 	      u64 persistent_fid, u64 volatile_fid, int level)
 {
 	struct smb_rqst rqst;
@@ -5284,7 +5545,21 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
+	      u64 persistent_fid, u64 volatile_fid, int level)
+{
+	int rc;
+
+	do {
+		rc = SMB2_QFS_attr_final(xid, tcon,
+				persistent_fid, volatile_fid, level);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
+int
+smb2_lockv_final(const unsigned int xid, struct cifs_tcon *tcon,
 	   const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
 	   const __u32 num_lock, struct smb2_lock_element *buf)
 {
@@ -5342,6 +5617,22 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
 	return rc;
 }
 
+int
+smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+	   const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
+	   const __u32 num_lock, struct smb2_lock_element *buf)
+{
+	int rc;
+
+	do {
+		rc = smb2_lockv_final(xid, tcon,
+				persist_fid, volatile_fid, pid,
+				num_lock, buf);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
+
 int
 SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
 	  const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
@@ -5360,7 +5651,7 @@ SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
 }
 
 int
-SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
+SMB2_lease_break_final(const unsigned int xid, struct cifs_tcon *tcon,
 		 __u8 *lease_key, const __le32 lease_state)
 {
 	struct smb_rqst rqst;
@@ -5418,3 +5709,17 @@ SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
 
 	return rc;
 }
+
+int
+SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
+		 __u8 *lease_key, const __le32 lease_state)
+{
+	int rc;
+
+	do {
+		rc = SMB2_lease_break_final(xid, tcon,
+				lease_key, lease_state);
+	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
+
+	return rc;
+}
-- 
2.27.0


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

* Re: [PATCH] cifs: when out of credits on one channel, retry on another.
  2021-04-26 16:18 [PATCH] cifs: when out of credits on one channel, retry on another Shyam Prasad N
  2021-04-27 23:13 ` Steve French
@ 2021-04-28 15:23 ` Aurélien Aptel
  2021-04-28 15:33   ` Steve French
  1 sibling, 1 reply; 6+ messages in thread
From: Aurélien Aptel @ 2021-04-28 15:23 UTC (permalink / raw)
  To: Shyam Prasad N, Steve French, CIFS

Shyam Prasad N <nspmangalore@gmail.com> writes:
> The idea is to retry on -EDEADLK (which we return when we run out of
> credits and we have no requests in flight), so that another channel is
> chosen for the next request.

I agree with the idea.

Have you been able to test those DEADLK codepaths? If so how, because we
should add buildbot tests I think.

For the function renaming, there is a precedent in the kernel to use a _
prefix for "sub-functions" instead of the _final suffix.

> This fix mostly introduces a wrapper for all functions which call
> cifs_pick_channel. In the wrapper function, the function is retried
> when the error is -EDEADLK, and uses multichannel.

I think we should put a limit on the number of retries. If it goes above
some reasonable number print a warning and return EDEADLK.

We could also hide the retry logic loop in a macro to avoid code
duplication when possible. This could get rid of multiple simpler funcs
I think if we use the macro in the caller.

Something like (feel free to improve/modify):

#define MULTICHAN_RETRY(chan_count, call) \
({ \
	int __rc; \
	int __tries = 0;
	do { \
		__rc = call; \
	        __tries++; \
	} while (__tries < MULTICHAN_MAX_RETRY && __rc == -EDEADLK && chan_count > 1) \
	WARN_ON(__tries == MULTICHAN_MAX_RETRY); \
	__rc; \
})        


> Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
> ---
>  fs/cifs/file.c      |  96 +++++++++++-
>  fs/cifs/smb2inode.c |  93 ++++++++----
>  fs/cifs/smb2ops.c   | 113 +++++++++++++-
>  fs/cifs/smb2pdu.c   | 359 ++++++++++++++++++++++++++++++++++++++++----
>  4 files changed, 593 insertions(+), 68 deletions(-)
>
> diff --git a/fs/cifs/file.c b/fs/cifs/file.c
> index 7e97aeabd616..7609b9739ce4 100644
> --- a/fs/cifs/file.c
> +++ b/fs/cifs/file.c
> @@ -2378,7 +2378,7 @@ wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
>  	return rc;
>  }
>  
> -static int cifs_writepages(struct address_space *mapping,
> +static int cifs_writepages_final(struct address_space *mapping,
>  			   struct writeback_control *wbc)
>  {
>  	struct inode *inode = mapping->host;
> @@ -2543,6 +2543,21 @@ static int cifs_writepages(struct address_space *mapping,
>  	return rc;
>  }
>  
> +static int cifs_writepages(struct address_space *mapping,
> +			   struct writeback_control *wbc)
> +{
> +	int rc;
> +	struct inode *inode = mapping->host;
> +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
> +	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
> +
> +	do {
> +		rc = cifs_writepages_final(mapping, wbc); 
> +	} while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
> +
> +	return rc;
> +}

cifs_writepages can issue multiple writes. I suspect it can do some
successful writes before it runs out of credit, and I'm not sure if
individual pages are marked as flushed or not. Basically this func might
not be idempotent so that's one codepath that we should definitely try I
think (unless someone knows more and can explain me).

Same goes for /some/ of the the other funcs... I think this should be
documented/tried.
>  
> +static int
> +smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
> +		 struct cifs_sb_info *cifs_sb, const char *full_path,
> +		 __u32 desired_access, __u32 create_disposition,
> +		 __u32 create_options, umode_t mode, void *ptr, int command,
> +		 bool check_open, bool writable_path)
> +{

I would use an int for flags intead of passing those 2 booleans. This
way adding more flags in the future will be easier, and you can use
named enum or defines in the code instead of true/false which will be
more understandable.

Cheers,
-- 
Aurélien Aptel / SUSE Labs Samba Team
GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)


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

* Re: [PATCH] cifs: when out of credits on one channel, retry on another.
  2021-04-28 15:23 ` Aurélien Aptel
@ 2021-04-28 15:33   ` Steve French
  2021-04-28 19:31     ` Pavel Shilovsky
  0 siblings, 1 reply; 6+ messages in thread
From: Steve French @ 2021-04-28 15:33 UTC (permalink / raw)
  To: Aurélien Aptel; +Cc: Shyam Prasad N, CIFS

We have a simple repro we are looking at now with 4 channels

We start with reasonable number of credits but end up with channels 3
and 4 reconnecting and only having one credit on reconnect

On Wed, Apr 28, 2021 at 10:23 AM Aurélien Aptel <aaptel@suse.com> wrote:
>
> Shyam Prasad N <nspmangalore@gmail.com> writes:
> > The idea is to retry on -EDEADLK (which we return when we run out of
> > credits and we have no requests in flight), so that another channel is
> > chosen for the next request.
>
> I agree with the idea.
>
> Have you been able to test those DEADLK codepaths? If so how, because we
> should add buildbot tests I think.
>
> For the function renaming, there is a precedent in the kernel to use a _
> prefix for "sub-functions" instead of the _final suffix.
>
> > This fix mostly introduces a wrapper for all functions which call
> > cifs_pick_channel. In the wrapper function, the function is retried
> > when the error is -EDEADLK, and uses multichannel.
>
> I think we should put a limit on the number of retries. If it goes above
> some reasonable number print a warning and return EDEADLK.
>
> We could also hide the retry logic loop in a macro to avoid code
> duplication when possible. This could get rid of multiple simpler funcs
> I think if we use the macro in the caller.
>
> Something like (feel free to improve/modify):
>
> #define MULTICHAN_RETRY(chan_count, call) \
> ({ \
>         int __rc; \
>         int __tries = 0;
>         do { \
>                 __rc = call; \
>                 __tries++; \
>         } while (__tries < MULTICHAN_MAX_RETRY && __rc == -EDEADLK && chan_count > 1) \
>         WARN_ON(__tries == MULTICHAN_MAX_RETRY); \
>         __rc; \
> })
>
>
> > Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
> > ---
> >  fs/cifs/file.c      |  96 +++++++++++-
> >  fs/cifs/smb2inode.c |  93 ++++++++----
> >  fs/cifs/smb2ops.c   | 113 +++++++++++++-
> >  fs/cifs/smb2pdu.c   | 359 ++++++++++++++++++++++++++++++++++++++++----
> >  4 files changed, 593 insertions(+), 68 deletions(-)
> >
> > diff --git a/fs/cifs/file.c b/fs/cifs/file.c
> > index 7e97aeabd616..7609b9739ce4 100644
> > --- a/fs/cifs/file.c
> > +++ b/fs/cifs/file.c
> > @@ -2378,7 +2378,7 @@ wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
> >       return rc;
> >  }
> >
> > -static int cifs_writepages(struct address_space *mapping,
> > +static int cifs_writepages_final(struct address_space *mapping,
> >                          struct writeback_control *wbc)
> >  {
> >       struct inode *inode = mapping->host;
> > @@ -2543,6 +2543,21 @@ static int cifs_writepages(struct address_space *mapping,
> >       return rc;
> >  }
> >
> > +static int cifs_writepages(struct address_space *mapping,
> > +                        struct writeback_control *wbc)
> > +{
> > +     int rc;
> > +     struct inode *inode = mapping->host;
> > +     struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
> > +     struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
> > +
> > +     do {
> > +             rc = cifs_writepages_final(mapping, wbc);
> > +     } while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
> > +
> > +     return rc;
> > +}
>
> cifs_writepages can issue multiple writes. I suspect it can do some
> successful writes before it runs out of credit, and I'm not sure if
> individual pages are marked as flushed or not. Basically this func might
> not be idempotent so that's one codepath that we should definitely try I
> think (unless someone knows more and can explain me).
>
> Same goes for /some/ of the the other funcs... I think this should be
> documented/tried.
> >
> > +static int
> > +smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
> > +              struct cifs_sb_info *cifs_sb, const char *full_path,
> > +              __u32 desired_access, __u32 create_disposition,
> > +              __u32 create_options, umode_t mode, void *ptr, int command,
> > +              bool check_open, bool writable_path)
> > +{
>
> I would use an int for flags intead of passing those 2 booleans. This
> way adding more flags in the future will be easier, and you can use
> named enum or defines in the code instead of true/false which will be
> more understandable.
>
> Cheers,
> --
> Aurélien Aptel / SUSE Labs Samba Team
> GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
>


-- 
Thanks,

Steve

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

* Re: [PATCH] cifs: when out of credits on one channel, retry on another.
  2021-04-28 15:33   ` Steve French
@ 2021-04-28 19:31     ` Pavel Shilovsky
  2021-04-29  8:24       ` Shyam Prasad N
  0 siblings, 1 reply; 6+ messages in thread
From: Pavel Shilovsky @ 2021-04-28 19:31 UTC (permalink / raw)
  To: Steve French; +Cc: Aurélien Aptel, Shyam Prasad N, CIFS

I agree this is a good idea to retry if a channel is out of credits.

The proposed solution should work for all operations but writepages.
For writepages there is an internal logic that marks pages and mapping
with errors:

2412                 if (!wdata->cfile) {
2413                         cifs_dbg(VFS, "No writable handle in
writepages rc=%d\n",
2414                                  get_file_rc);
2415                         if (is_retryable_error(get_file_rc))
2416                                 rc = get_file_rc;
2417                         else
2418                                 rc = -EBADF;
2419                 } else
2420                         rc = wdata_send_pages(wdata, nr_pages,
mapping, wbc);
2421
2422                 for (i = 0; i < nr_pages; ++i)
2423                         unlock_page(wdata->pages[i]);
2424
2425                 /* send failure -- clean up the mess */
2426                 if (rc != 0) {
2427                         add_credits_and_wake_if(server,
&wdata->credits, 0);
2428                         for (i = 0; i < nr_pages; ++i) {
2429                                 if (is_retryable_error(rc))
2430                                         redirty_page_for_writepage(wbc,
2431
wdata->pages[i]);
2432                                 else
2433                                         SetPageError(wdata->pages[i]);
2434                                 end_page_writeback(wdata->pages[i]);
2435                                 put_page(wdata->pages[i]);
2436                         }
2437                         if (!is_retryable_error(rc))
2438                                 mapping_set_error(mapping, rc);
2439                 }


What I think we should do instead is to include -EDEADLK into the set
of retryable errors conditionally to multi-channel being enabled and
let cifs_writepages itself use its internal retry mechanism.

--
Best regards,
Pavel Shilovsky

ср, 28 апр. 2021 г. в 08:35, Steve French <smfrench@gmail.com>:
>
> We have a simple repro we are looking at now with 4 channels
>
> We start with reasonable number of credits but end up with channels 3
> and 4 reconnecting and only having one credit on reconnect
>
> On Wed, Apr 28, 2021 at 10:23 AM Aurélien Aptel <aaptel@suse.com> wrote:
> >
> > Shyam Prasad N <nspmangalore@gmail.com> writes:
> > > The idea is to retry on -EDEADLK (which we return when we run out of
> > > credits and we have no requests in flight), so that another channel is
> > > chosen for the next request.
> >
> > I agree with the idea.
> >
> > Have you been able to test those DEADLK codepaths? If so how, because we
> > should add buildbot tests I think.
> >
> > For the function renaming, there is a precedent in the kernel to use a _
> > prefix for "sub-functions" instead of the _final suffix.
> >
> > > This fix mostly introduces a wrapper for all functions which call
> > > cifs_pick_channel. In the wrapper function, the function is retried
> > > when the error is -EDEADLK, and uses multichannel.
> >
> > I think we should put a limit on the number of retries. If it goes above
> > some reasonable number print a warning and return EDEADLK.
> >
> > We could also hide the retry logic loop in a macro to avoid code
> > duplication when possible. This could get rid of multiple simpler funcs
> > I think if we use the macro in the caller.
> >
> > Something like (feel free to improve/modify):
> >
> > #define MULTICHAN_RETRY(chan_count, call) \
> > ({ \
> >         int __rc; \
> >         int __tries = 0;
> >         do { \
> >                 __rc = call; \
> >                 __tries++; \
> >         } while (__tries < MULTICHAN_MAX_RETRY && __rc == -EDEADLK && chan_count > 1) \
> >         WARN_ON(__tries == MULTICHAN_MAX_RETRY); \
> >         __rc; \
> > })
> >
> >
> > > Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
> > > ---
> > >  fs/cifs/file.c      |  96 +++++++++++-
> > >  fs/cifs/smb2inode.c |  93 ++++++++----
> > >  fs/cifs/smb2ops.c   | 113 +++++++++++++-
> > >  fs/cifs/smb2pdu.c   | 359 ++++++++++++++++++++++++++++++++++++++++----
> > >  4 files changed, 593 insertions(+), 68 deletions(-)
> > >
> > > diff --git a/fs/cifs/file.c b/fs/cifs/file.c
> > > index 7e97aeabd616..7609b9739ce4 100644
> > > --- a/fs/cifs/file.c
> > > +++ b/fs/cifs/file.c
> > > @@ -2378,7 +2378,7 @@ wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
> > >       return rc;
> > >  }
> > >
> > > -static int cifs_writepages(struct address_space *mapping,
> > > +static int cifs_writepages_final(struct address_space *mapping,
> > >                          struct writeback_control *wbc)
> > >  {
> > >       struct inode *inode = mapping->host;
> > > @@ -2543,6 +2543,21 @@ static int cifs_writepages(struct address_space *mapping,
> > >       return rc;
> > >  }
> > >
> > > +static int cifs_writepages(struct address_space *mapping,
> > > +                        struct writeback_control *wbc)
> > > +{
> > > +     int rc;
> > > +     struct inode *inode = mapping->host;
> > > +     struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
> > > +     struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
> > > +
> > > +     do {
> > > +             rc = cifs_writepages_final(mapping, wbc);
> > > +     } while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
> > > +
> > > +     return rc;
> > > +}
> >
> > cifs_writepages can issue multiple writes. I suspect it can do some
> > successful writes before it runs out of credit, and I'm not sure if
> > individual pages are marked as flushed or not. Basically this func might
> > not be idempotent so that's one codepath that we should definitely try I
> > think (unless someone knows more and can explain me).
> >
> > Same goes for /some/ of the the other funcs... I think this should be
> > documented/tried.
> > >
> > > +static int
> > > +smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
> > > +              struct cifs_sb_info *cifs_sb, const char *full_path,
> > > +              __u32 desired_access, __u32 create_disposition,
> > > +              __u32 create_options, umode_t mode, void *ptr, int command,
> > > +              bool check_open, bool writable_path)
> > > +{
> >
> > I would use an int for flags intead of passing those 2 booleans. This
> > way adding more flags in the future will be easier, and you can use
> > named enum or defines in the code instead of true/false which will be
> > more understandable.
> >
> > Cheers,
> > --
> > Aurélien Aptel / SUSE Labs Samba Team
> > GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> > SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> > GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
> >
>
>
> --
> Thanks,
>
> Steve

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

* Re: [PATCH] cifs: when out of credits on one channel, retry on another.
  2021-04-28 19:31     ` Pavel Shilovsky
@ 2021-04-29  8:24       ` Shyam Prasad N
  0 siblings, 0 replies; 6+ messages in thread
From: Shyam Prasad N @ 2021-04-29  8:24 UTC (permalink / raw)
  To: Pavel Shilovsky; +Cc: Steve French, Aurélien Aptel, CIFS

Hi all,

Thanks for the reviews.

@Aurélien Aptel The issue came up quite frequently during the tests
due to another bug. I've submitted a patch for the fix, so this should
not come up for a day one scenario again.
This could come up when one or more channels if the server stops
giving out credits because it's heavily loaded. In that case, we
should try on one of the other channels rather than simply giving up.

@Pavel Shilovsky I like your approach better, as there should be a lot
less code churn. Let me explore it further.

Regards,
Shyam

On Thu, Apr 29, 2021 at 1:01 AM Pavel Shilovsky <piastryyy@gmail.com> wrote:
>
> I agree this is a good idea to retry if a channel is out of credits.
>
> The proposed solution should work for all operations but writepages.
> For writepages there is an internal logic that marks pages and mapping
> with errors:
>
> 2412                 if (!wdata->cfile) {
> 2413                         cifs_dbg(VFS, "No writable handle in
> writepages rc=%d\n",
> 2414                                  get_file_rc);
> 2415                         if (is_retryable_error(get_file_rc))
> 2416                                 rc = get_file_rc;
> 2417                         else
> 2418                                 rc = -EBADF;
> 2419                 } else
> 2420                         rc = wdata_send_pages(wdata, nr_pages,
> mapping, wbc);
> 2421
> 2422                 for (i = 0; i < nr_pages; ++i)
> 2423                         unlock_page(wdata->pages[i]);
> 2424
> 2425                 /* send failure -- clean up the mess */
> 2426                 if (rc != 0) {
> 2427                         add_credits_and_wake_if(server,
> &wdata->credits, 0);
> 2428                         for (i = 0; i < nr_pages; ++i) {
> 2429                                 if (is_retryable_error(rc))
> 2430                                         redirty_page_for_writepage(wbc,
> 2431
> wdata->pages[i]);
> 2432                                 else
> 2433                                         SetPageError(wdata->pages[i]);
> 2434                                 end_page_writeback(wdata->pages[i]);
> 2435                                 put_page(wdata->pages[i]);
> 2436                         }
> 2437                         if (!is_retryable_error(rc))
> 2438                                 mapping_set_error(mapping, rc);
> 2439                 }
>
>
> What I think we should do instead is to include -EDEADLK into the set
> of retryable errors conditionally to multi-channel being enabled and
> let cifs_writepages itself use its internal retry mechanism.
>
> --
> Best regards,
> Pavel Shilovsky
>
> ср, 28 апр. 2021 г. в 08:35, Steve French <smfrench@gmail.com>:
> >
> > We have a simple repro we are looking at now with 4 channels
> >
> > We start with reasonable number of credits but end up with channels 3
> > and 4 reconnecting and only having one credit on reconnect
> >
> > On Wed, Apr 28, 2021 at 10:23 AM Aurélien Aptel <aaptel@suse.com> wrote:
> > >
> > > Shyam Prasad N <nspmangalore@gmail.com> writes:
> > > > The idea is to retry on -EDEADLK (which we return when we run out of
> > > > credits and we have no requests in flight), so that another channel is
> > > > chosen for the next request.
> > >
> > > I agree with the idea.
> > >
> > > Have you been able to test those DEADLK codepaths? If so how, because we
> > > should add buildbot tests I think.
> > >
> > > For the function renaming, there is a precedent in the kernel to use a _
> > > prefix for "sub-functions" instead of the _final suffix.
> > >
> > > > This fix mostly introduces a wrapper for all functions which call
> > > > cifs_pick_channel. In the wrapper function, the function is retried
> > > > when the error is -EDEADLK, and uses multichannel.
> > >
> > > I think we should put a limit on the number of retries. If it goes above
> > > some reasonable number print a warning and return EDEADLK.
> > >
> > > We could also hide the retry logic loop in a macro to avoid code
> > > duplication when possible. This could get rid of multiple simpler funcs
> > > I think if we use the macro in the caller.
> > >
> > > Something like (feel free to improve/modify):
> > >
> > > #define MULTICHAN_RETRY(chan_count, call) \
> > > ({ \
> > >         int __rc; \
> > >         int __tries = 0;
> > >         do { \
> > >                 __rc = call; \
> > >                 __tries++; \
> > >         } while (__tries < MULTICHAN_MAX_RETRY && __rc == -EDEADLK && chan_count > 1) \
> > >         WARN_ON(__tries == MULTICHAN_MAX_RETRY); \
> > >         __rc; \
> > > })
> > >
> > >
> > > > Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
> > > > ---
> > > >  fs/cifs/file.c      |  96 +++++++++++-
> > > >  fs/cifs/smb2inode.c |  93 ++++++++----
> > > >  fs/cifs/smb2ops.c   | 113 +++++++++++++-
> > > >  fs/cifs/smb2pdu.c   | 359 ++++++++++++++++++++++++++++++++++++++++----
> > > >  4 files changed, 593 insertions(+), 68 deletions(-)
> > > >
> > > > diff --git a/fs/cifs/file.c b/fs/cifs/file.c
> > > > index 7e97aeabd616..7609b9739ce4 100644
> > > > --- a/fs/cifs/file.c
> > > > +++ b/fs/cifs/file.c
> > > > @@ -2378,7 +2378,7 @@ wdata_send_pages(struct cifs_writedata *wdata, unsigned int nr_pages,
> > > >       return rc;
> > > >  }
> > > >
> > > > -static int cifs_writepages(struct address_space *mapping,
> > > > +static int cifs_writepages_final(struct address_space *mapping,
> > > >                          struct writeback_control *wbc)
> > > >  {
> > > >       struct inode *inode = mapping->host;
> > > > @@ -2543,6 +2543,21 @@ static int cifs_writepages(struct address_space *mapping,
> > > >       return rc;
> > > >  }
> > > >
> > > > +static int cifs_writepages(struct address_space *mapping,
> > > > +                        struct writeback_control *wbc)
> > > > +{
> > > > +     int rc;
> > > > +     struct inode *inode = mapping->host;
> > > > +     struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
> > > > +     struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
> > > > +
> > > > +     do {
> > > > +             rc = cifs_writepages_final(mapping, wbc);
> > > > +     } while (tcon->ses->chan_count > 1 && rc == -EDEADLK);
> > > > +
> > > > +     return rc;
> > > > +}
> > >
> > > cifs_writepages can issue multiple writes. I suspect it can do some
> > > successful writes before it runs out of credit, and I'm not sure if
> > > individual pages are marked as flushed or not. Basically this func might
> > > not be idempotent so that's one codepath that we should definitely try I
> > > think (unless someone knows more and can explain me).
> > >
> > > Same goes for /some/ of the the other funcs... I think this should be
> > > documented/tried.
> > > >
> > > > +static int
> > > > +smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
> > > > +              struct cifs_sb_info *cifs_sb, const char *full_path,
> > > > +              __u32 desired_access, __u32 create_disposition,
> > > > +              __u32 create_options, umode_t mode, void *ptr, int command,
> > > > +              bool check_open, bool writable_path)
> > > > +{
> > >
> > > I would use an int for flags intead of passing those 2 booleans. This
> > > way adding more flags in the future will be easier, and you can use
> > > named enum or defines in the code instead of true/false which will be
> > > more understandable.
> > >
> > > Cheers,
> > > --
> > > Aurélien Aptel / SUSE Labs Samba Team
> > > GPG: 1839 CB5F 9F5B FB9B AA97  8C99 03C8 A49B 521B D5D3
> > > SUSE Software Solutions Germany GmbH, Maxfeldstr. 5, 90409 Nürnberg, DE
> > > GF: Felix Imendörffer, Mary Higgins, Sri Rasiah HRB 247165 (AG München)
> > >
> >
> >
> > --
> > Thanks,
> >
> > Steve



-- 
Regards,
Shyam

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

end of thread, other threads:[~2021-04-29  8:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-26 16:18 [PATCH] cifs: when out of credits on one channel, retry on another Shyam Prasad N
2021-04-27 23:13 ` Steve French
2021-04-28 15:23 ` Aurélien Aptel
2021-04-28 15:33   ` Steve French
2021-04-28 19:31     ` Pavel Shilovsky
2021-04-29  8:24       ` Shyam Prasad N

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).