cifs: commands that are retried should have replay flag set
authorShyam Prasad N <sprasad@microsoft.com>
Sun, 21 Jan 2024 03:32:47 +0000 (03:32 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 25 May 2024 14:22:46 +0000 (16:22 +0200)
[ Upstream commit 4f1fffa2376922f3d1d506e49c0fd445b023a28e ]

MS-SMB2 states that the header flag SMB2_FLAGS_REPLAY_OPERATION
needs to be set when a command needs to be retried, so that
the server is aware that this is a replay for an operation that
appeared before.

This can be very important, for example, for state changing
operations and opens which get retried following a reconnect;
since the client maybe unaware of the status of the previous
open.

This is particularly important for multichannel scenario, since
disconnection of one connection does not mean that the session
is lost. The requests can be replayed on another channel.

This change also makes use of exponential back-off before replays
and also limits the number of retries to "retrans" mount option
value.

Also, this change does not modify the read/write codepath.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/smb/client/cached_dir.c
fs/smb/client/cifsglob.h
fs/smb/client/smb2inode.c
fs/smb/client/smb2ops.c
fs/smb/client/smb2pdu.c
fs/smb/client/smb2proto.h

index 567f718362c52582aac639cf4e0acbf81dd1a849..ca0fd25236ef4b5fa6affbf77897ba5021c5f167 100644 (file)
@@ -145,21 +145,27 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
        struct cached_fid *cfid;
        struct cached_fids *cfids;
        const char *npath;
+       int retries = 0, cur_sleep = 1;
 
        if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache ||
            is_smb1_server(tcon->ses->server) || (dir_cache_timeout == 0))
                return -EOPNOTSUPP;
 
        ses = tcon->ses;
-       server = cifs_pick_channel(ses);
        cfids = tcon->cfids;
 
-       if (!server->ops->new_lease_key)
-               return -EIO;
-
        if (cifs_sb->root == NULL)
                return -ENOENT;
 
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       oplock = SMB2_OPLOCK_LEVEL_II;
+       server = cifs_pick_channel(ses);
+
+       if (!server->ops->new_lease_key)
+               return -EIO;
+
        utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
        if (!utf16_path)
                return -ENOMEM;
@@ -269,6 +275,11 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
         */
        cfid->has_lease = true;
 
+       if (retries) {
+               smb2_set_replay(server, &rqst[0]);
+               smb2_set_replay(server, &rqst[1]);
+       }
+
        rc = compound_send_recv(xid, ses, server,
                                flags, 2, rqst,
                                resp_buftype, rsp_iov);
@@ -369,6 +380,10 @@ out:
        }
        kfree(utf16_path);
 
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
index 6acadb53ada790df75bf27e3cc4e039f3aba1018..479bf0d9ad589e6393930e20a69dfaa372646f81 100644 (file)
  */
 #define CIFS_DEF_ACTIMEO (1 * HZ)
 
+/*
+ * max sleep time before retry to server
+ */
+#define CIFS_MAX_SLEEP 2000
+
 /*
  * max attribute cache timeout (jiffies) - 2^30
  */
index 1388ce5421a892ae1f0ca02a29adbf70eb7a16b0..94df328a1965dcca8d33d64ddc906b8566de7897 100644 (file)
@@ -92,6 +92,14 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
        unsigned int size[2];
        void *data[2];
        int len;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       oplock = SMB2_OPLOCK_LEVEL_NONE;
+       num_rqst = 0;
+       server = cifs_pick_channel(ses);
 
        vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
        if (vars == NULL)
@@ -99,8 +107,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
        rqst = &vars->rqst[0];
        rsp_iov = &vars->rsp_iov[0];
 
-       server = cifs_pick_channel(ses);
-
        if (smb3_encryption_required(tcon))
                flags |= CIFS_TRANSFORM_REQ;
 
@@ -435,15 +441,24 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
        num_rqst++;
 
        if (cfile) {
+               if (retries)
+                       for (i = 1; i < num_rqst - 2; i++)
+                               smb2_set_replay(server, &rqst[i]);
+
                rc = compound_send_recv(xid, ses, server,
                                        flags, num_rqst - 2,
                                        &rqst[1], &resp_buftype[1],
                                        &rsp_iov[1]);
-       } else
+       } else {
+               if (retries)
+                       for (i = 0; i < num_rqst; i++)
+                               smb2_set_replay(server, &rqst[i]);
+
                rc = compound_send_recv(xid, ses, server,
                                        flags, num_rqst,
                                        rqst, resp_buftype,
                                        rsp_iov);
+       }
 
 finished:
        num_rqst = 0;
@@ -604,9 +619,6 @@ finished:
        }
        SMB2_close_free(&rqst[num_rqst]);
 
-       if (cfile)
-               cifsFileInfo_put(cfile);
-
        num_cmds += 2;
        if (out_iov && out_buftype) {
                memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));
@@ -616,7 +628,16 @@ finished:
                for (i = 0; i < num_cmds; i++)
                        free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);
        }
+       num_cmds -= 2; /* correct num_cmds as there could be a retry */
        kfree(vars);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
+       if (cfile)
+               cifsFileInfo_put(cfile);
+
        return rc;
 }
 
index 3e07ab1564ea704f4e29149ad497caa0d5467a64..06735c5685bf6cf2df9c168d4705c314de545bdc 100644 (file)
@@ -1108,7 +1108,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 {
        struct smb2_compound_vars *vars;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        struct smb_rqst *rqst;
        struct kvec *rsp_iov;
        __le16 *utf16_path = NULL;
@@ -1124,6 +1124,13 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
        struct smb2_file_full_ea_info *ea = NULL;
        struct smb2_query_info_rsp *rsp;
        int rc, used_len = 0;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = CIFS_CP_CREATE_CLOSE_OP;
+       oplock = SMB2_OPLOCK_LEVEL_NONE;
+       server = cifs_pick_channel(ses);
 
        if (smb3_encryption_required(tcon))
                flags |= CIFS_TRANSFORM_REQ;
@@ -1244,6 +1251,12 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
                goto sea_exit;
        smb2_set_related(&rqst[2]);
 
+       if (retries) {
+               smb2_set_replay(server, &rqst[0]);
+               smb2_set_replay(server, &rqst[1]);
+               smb2_set_replay(server, &rqst[2]);
+       }
+
        rc = compound_send_recv(xid, ses, server,
                                flags, 3, rqst,
                                resp_buftype, rsp_iov);
@@ -1260,6 +1273,11 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
        kfree(vars);
 out_free_path:
        kfree(utf16_path);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 #endif
@@ -1485,7 +1503,7 @@ smb2_ioctl_query_info(const unsigned int xid,
        struct smb_rqst *rqst;
        struct kvec *rsp_iov;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        char __user *arg = (char __user *)p;
        struct smb_query_info qi;
        struct smb_query_info __user *pqi;
@@ -1502,6 +1520,13 @@ smb2_ioctl_query_info(const unsigned int xid,
        void *data[2];
        int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR;
        void (*free_req1_func)(struct smb_rqst *r);
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = CIFS_CP_CREATE_CLOSE_OP;
+       oplock = SMB2_OPLOCK_LEVEL_NONE;
+       server = cifs_pick_channel(ses);
 
        vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
        if (vars == NULL)
@@ -1642,6 +1667,12 @@ smb2_ioctl_query_info(const unsigned int xid,
                goto free_req_1;
        smb2_set_related(&rqst[2]);
 
+       if (retries) {
+               smb2_set_replay(server, &rqst[0]);
+               smb2_set_replay(server, &rqst[1]);
+               smb2_set_replay(server, &rqst[2]);
+       }
+
        rc = compound_send_recv(xid, ses, server,
                                flags, 3, rqst,
                                resp_buftype, rsp_iov);
@@ -1702,6 +1733,11 @@ free_output_buffer:
        kfree(buffer);
 free_vars:
        kfree(vars);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -2228,8 +2264,14 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
        struct cifs_open_parms oparms;
        struct smb2_query_directory_rsp *qd_rsp = NULL;
        struct smb2_create_rsp *op_rsp = NULL;
-       struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
-       int retry_count = 0;
+       struct TCP_Server_Info *server;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       oplock = SMB2_OPLOCK_LEVEL_NONE;
+       server = cifs_pick_channel(tcon->ses);
 
        utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
        if (!utf16_path)
@@ -2279,14 +2321,15 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
 
        smb2_set_related(&rqst[1]);
 
-again:
+       if (retries) {
+               smb2_set_replay(server, &rqst[0]);
+               smb2_set_replay(server, &rqst[1]);
+       }
+
        rc = compound_send_recv(xid, tcon->ses, server,
                                flags, 2, rqst,
                                resp_buftype, rsp_iov);
 
-       if (rc == -EAGAIN && retry_count++ < 10)
-               goto again;
-
        /* If the open failed there is nothing to do */
        op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
        if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) {
@@ -2334,6 +2377,11 @@ again:
        SMB2_query_directory_free(&rqst[1]);
        free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
        free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -2460,6 +2508,22 @@ smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid,
                                 CIFS_CACHE_READ(cinode) ? 1 : 0);
 }
 
+void
+smb2_set_replay(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+{
+       struct smb2_hdr *shdr;
+
+       if (server->dialect < SMB30_PROT_ID)
+               return;
+
+       shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base);
+       if (shdr == NULL) {
+               cifs_dbg(FYI, "shdr NULL in smb2_set_related\n");
+               return;
+       }
+       shdr->Flags |= SMB2_FLAGS_REPLAY_OPERATION;
+}
+
 void
 smb2_set_related(struct smb_rqst *rqst)
 {
@@ -2532,6 +2596,27 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst)
        shdr->NextCommand = cpu_to_le32(len);
 }
 
+/*
+ * helper function for exponential backoff and check if replayable
+ */
+bool smb2_should_replay(struct cifs_tcon *tcon,
+                               int *pretries,
+                               int *pcur_sleep)
+{
+       if (!pretries || !pcur_sleep)
+               return false;
+
+       if (tcon->retry || (*pretries)++ < tcon->ses->server->retrans) {
+               msleep(*pcur_sleep);
+               (*pcur_sleep) = ((*pcur_sleep) << 1);
+               if ((*pcur_sleep) > CIFS_MAX_SLEEP)
+                       (*pcur_sleep) = CIFS_MAX_SLEEP;
+               return true;
+       }
+
+       return false;
+}
+
 /*
  * Passes the query info response back to the caller on success.
  * Caller need to free this with free_rsp_buf().
@@ -2545,7 +2630,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 {
        struct smb2_compound_vars *vars;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        int flags = CIFS_CP_CREATE_CLOSE_OP;
        struct smb_rqst *rqst;
        int resp_buftype[3];
@@ -2556,6 +2641,13 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
        int rc;
        __le16 *utf16_path;
        struct cached_fid *cfid = NULL;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = CIFS_CP_CREATE_CLOSE_OP;
+       oplock = SMB2_OPLOCK_LEVEL_NONE;
+       server = cifs_pick_channel(ses);
 
        if (!path)
                path = "";
@@ -2636,6 +2728,14 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
                goto qic_exit;
        smb2_set_related(&rqst[2]);
 
+       if (retries) {
+               if (!cfid) {
+                       smb2_set_replay(server, &rqst[0]);
+                       smb2_set_replay(server, &rqst[2]);
+               }
+               smb2_set_replay(server, &rqst[1]);
+       }
+
        if (cfid) {
                rc = compound_send_recv(xid, ses, server,
                                        flags, 1, &rqst[1],
@@ -2668,6 +2768,11 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
        kfree(vars);
 out_free_path:
        kfree(utf16_path);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
index 95b5b4bdb4b7fd96b56949619586b54114226a4b..6a5d478b3cef6b5a6de6c87cf1c975c9ca6c1461 100644 (file)
@@ -2795,7 +2795,14 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
        int flags = 0;
        unsigned int total_len;
        __le16 *utf16_path = NULL;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       n_iov = 2;
+       server = cifs_pick_channel(ses);
 
        cifs_dbg(FYI, "mkdir\n");
 
@@ -2899,6 +2906,10 @@ int smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
        /* no need to inc num_remote_opens because we close it just below */
        trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE,
                                    FILE_WRITE_ATTRIBUTES);
+
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        /* resource #4: response buffer */
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
@@ -2936,6 +2947,11 @@ err_free_req:
        cifs_small_buf_release(req);
 err_free_path:
        kfree(utf16_path);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -3131,12 +3147,18 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
        struct smb2_create_rsp *rsp = NULL;
        struct cifs_tcon *tcon = oparms->tcon;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        struct kvec iov[SMB2_CREATE_IOV_SIZE];
        struct kvec rsp_iov = {NULL, 0};
        int resp_buftype = CIFS_NO_BUFFER;
        int rc = 0;
        int flags = 0;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       server = cifs_pick_channel(ses);
 
        cifs_dbg(FYI, "create/open\n");
        if (!ses || !server)
@@ -3158,6 +3180,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
        trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path,
                oparms->create_options, oparms->desired_access);
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags,
                            &rsp_iov);
@@ -3211,6 +3236,11 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 creat_exit:
        SMB2_open_free(&rqst);
        free_rsp_buf(resp_buftype, rsp);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -3335,15 +3365,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
        int resp_buftype = CIFS_NO_BUFFER;
        int rc = 0;
        int flags = 0;
-
-       cifs_dbg(FYI, "SMB2 IOCTL\n");
-
-       if (out_data != NULL)
-               *out_data = NULL;
-
-       /* zero out returned data len, in case of error */
-       if (plen)
-               *plen = 0;
+       int retries = 0, cur_sleep = 1;
 
        if (!tcon)
                return -EIO;
@@ -3352,10 +3374,23 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
        if (!ses)
                return -EIO;
 
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
        server = cifs_pick_channel(ses);
+
        if (!server)
                return -EIO;
 
+       cifs_dbg(FYI, "SMB2 IOCTL\n");
+
+       if (out_data != NULL)
+               *out_data = NULL;
+
+       /* zero out returned data len, in case of error */
+       if (plen)
+               *plen = 0;
+
        if (smb3_encryption_required(tcon))
                flags |= CIFS_TRANSFORM_REQ;
 
@@ -3370,6 +3405,9 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
        if (rc)
                goto ioctl_exit;
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags,
                            &rsp_iov);
@@ -3439,6 +3477,11 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 ioctl_exit:
        SMB2_ioctl_free(&rqst);
        free_rsp_buf(resp_buftype, rsp);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -3510,13 +3553,20 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
        struct smb_rqst rqst;
        struct smb2_close_rsp *rsp = NULL;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        struct kvec iov[1];
        struct kvec rsp_iov;
        int resp_buftype = CIFS_NO_BUFFER;
        int rc = 0;
        int flags = 0;
        bool query_attrs = false;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       query_attrs = false;
+       server = cifs_pick_channel(ses);
 
        cifs_dbg(FYI, "Close\n");
 
@@ -3542,6 +3592,9 @@ __SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
        if (rc)
                goto close_exit;
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
        rsp = (struct smb2_close_rsp *)rsp_iov.iov_base;
@@ -3575,6 +3628,11 @@ close_exit:
                        cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n",
                                 persistent_fid, tmp_rc);
        }
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -3705,12 +3763,19 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
        struct TCP_Server_Info *server;
        int flags = 0;
        bool allocated = false;
+       int retries = 0, cur_sleep = 1;
 
        cifs_dbg(FYI, "Query Info\n");
 
        if (!ses)
                return -EIO;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       allocated = false;
        server = cifs_pick_channel(ses);
+
        if (!server)
                return -EIO;
 
@@ -3732,6 +3797,9 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
        trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid,
                                    ses->Suid, info_class, (__u32)info_type);
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
        rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
@@ -3774,6 +3842,11 @@ query_info(const unsigned int xid, struct cifs_tcon *tcon,
 qinf_exit:
        SMB2_query_info_free(&rqst);
        free_rsp_buf(resp_buftype, rsp);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -3874,7 +3947,7 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
                u32 *plen /* returned data len */)
 {
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        struct smb_rqst rqst;
        struct smb2_change_notify_rsp *smb_rsp;
        struct kvec iov[1];
@@ -3882,6 +3955,12 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
        int resp_buftype = CIFS_NO_BUFFER;
        int flags = 0;
        int rc = 0;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       server = cifs_pick_channel(ses);
 
        cifs_dbg(FYI, "change notify\n");
        if (!ses || !server)
@@ -3906,6 +3985,10 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
 
        trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid,
                                (u8)watch_tree, completion_filter);
+
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
 
@@ -3940,6 +4023,11 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
        if (rqst.rq_iov)
                cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */
        free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -4182,10 +4270,16 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
        struct smb_rqst rqst;
        struct kvec iov[1];
        struct kvec rsp_iov = {NULL, 0};
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        int resp_buftype = CIFS_NO_BUFFER;
        int flags = 0;
        int rc = 0;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       server = cifs_pick_channel(ses);
 
        cifs_dbg(FYI, "flush\n");
        if (!ses || !(ses->server))
@@ -4205,6 +4299,10 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
                goto flush_exit;
 
        trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid);
+
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
 
@@ -4219,6 +4317,11 @@ SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
  flush_exit:
        SMB2_flush_free(&rqst);
        free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -4856,18 +4959,21 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
        int flags = 0;
        unsigned int total_len;
        struct TCP_Server_Info *server;
+       int retries = 0, cur_sleep = 1;
 
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
        *nbytes = 0;
-
-       if (n_vec < 1)
-               return rc;
-
        if (!io_parms->server)
                io_parms->server = cifs_pick_channel(io_parms->tcon->ses);
        server = io_parms->server;
        if (server == NULL)
                return -ECONNABORTED;
 
+       if (n_vec < 1)
+               return rc;
+
        rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server,
                                 (void **) &req, &total_len);
        if (rc)
@@ -4901,6 +5007,9 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
        rqst.rq_iov = iov;
        rqst.rq_nvec = n_vec + 1;
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, io_parms->tcon->ses, server,
                            &rqst,
                            &resp_buftype, flags, &rsp_iov);
@@ -4925,6 +5034,11 @@ SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
 
        cifs_small_buf_release(req);
        free_rsp_buf(resp_buftype, rsp);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(io_parms->tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -5242,8 +5356,14 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
        struct kvec rsp_iov;
        int rc = 0;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        int flags = 0;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       server = cifs_pick_channel(ses);
 
        if (!ses || !(ses->server))
                return -EIO;
@@ -5263,6 +5383,9 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
        if (rc)
                goto qdir_exit;
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
        rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base;
@@ -5297,6 +5420,11 @@ SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
 qdir_exit:
        SMB2_query_directory_free(&rqst);
        free_rsp_buf(resp_buftype, rsp);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -5363,8 +5491,14 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
        int rc = 0;
        int resp_buftype;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        int flags = 0;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       server = cifs_pick_channel(ses);
 
        if (!ses || !server)
                return -EIO;
@@ -5392,6 +5526,8 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
                return rc;
        }
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
 
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags,
@@ -5407,6 +5543,11 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
 
        free_rsp_buf(resp_buftype, rsp);
        kfree(iov);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -5459,12 +5600,18 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
        int rc;
        struct smb2_oplock_break *req = NULL;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        int flags = CIFS_OBREAK_OP;
        unsigned int total_len;
        struct kvec iov[1];
        struct kvec rsp_iov;
        int resp_buf_type;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = CIFS_OBREAK_OP;
+       server = cifs_pick_channel(ses);
 
        cifs_dbg(FYI, "SMB2_oplock_break\n");
        rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server,
@@ -5489,15 +5636,21 @@ SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
        rqst.rq_iov = iov;
        rqst.rq_nvec = 1;
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buf_type, flags, &rsp_iov);
        cifs_small_buf_release(req);
-
        if (rc) {
                cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
                cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc);
        }
 
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -5583,9 +5736,15 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
        int rc = 0;
        int resp_buftype;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        FILE_SYSTEM_POSIX_INFO *info = NULL;
        int flags = 0;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       server = cifs_pick_channel(ses);
 
        rc = build_qfs_info_req(&iov, tcon, server,
                                FS_POSIX_INFORMATION,
@@ -5601,6 +5760,9 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
        rqst.rq_iov = &iov;
        rqst.rq_nvec = 1;
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
        free_qfs_info_req(&iov);
@@ -5620,6 +5782,11 @@ SMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
 
 posix_qfsinf_exit:
        free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -5634,9 +5801,15 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
        int rc = 0;
        int resp_buftype;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        struct smb2_fs_full_size_info *info = NULL;
        int flags = 0;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       server = cifs_pick_channel(ses);
 
        rc = build_qfs_info_req(&iov, tcon, server,
                                FS_FULL_SIZE_INFORMATION,
@@ -5652,6 +5825,9 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
        rqst.rq_iov = &iov;
        rqst.rq_nvec = 1;
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
        free_qfs_info_req(&iov);
@@ -5671,6 +5847,11 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
 
 qfsinf_exit:
        free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -5685,9 +5866,15 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
        int rc = 0;
        int resp_buftype, max_len, min_len;
        struct cifs_ses *ses = tcon->ses;
-       struct TCP_Server_Info *server = cifs_pick_channel(ses);
+       struct TCP_Server_Info *server;
        unsigned int rsp_len, offset;
        int flags = 0;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = 0;
+       server = cifs_pick_channel(ses);
 
        if (level == FS_DEVICE_INFORMATION) {
                max_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
@@ -5719,6 +5906,9 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
        rqst.rq_iov = &iov;
        rqst.rq_nvec = 1;
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, ses, server,
                            &rqst, &resp_buftype, flags, &rsp_iov);
        free_qfs_info_req(&iov);
@@ -5756,6 +5946,11 @@ SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
 
 qfsattr_exit:
        free_rsp_buf(resp_buftype, rsp_iov.iov_base);
+
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
@@ -5773,7 +5968,13 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
        unsigned int count;
        int flags = CIFS_NO_RSP_BUF;
        unsigned int total_len;
-       struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
+       struct TCP_Server_Info *server;
+       int retries = 0, cur_sleep = 1;
+
+replay_again:
+       /* reinitialize for possible replay */
+       flags = CIFS_NO_RSP_BUF;
+       server = cifs_pick_channel(tcon->ses);
 
        cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock);
 
@@ -5804,6 +6005,9 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
        rqst.rq_iov = iov;
        rqst.rq_nvec = 2;
 
+       if (retries)
+               smb2_set_replay(server, &rqst);
+
        rc = cifs_send_recv(xid, tcon->ses, server,
                            &rqst, &resp_buf_type, flags,
                            &rsp_iov);
@@ -5815,6 +6019,10 @@ smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
                                    tcon->ses->Suid, rc);
        }
 
+       if (is_replayable_error(rc) &&
+           smb2_should_replay(tcon, &retries, &cur_sleep))
+               goto replay_again;
+
        return rc;
 }
 
index 343ada691e763bfce3eb64f1c7871922f7eb7a76..330e36c6b91f006766e829c621dc8c98ed893221 100644 (file)
@@ -122,6 +122,11 @@ extern unsigned long smb_rqst_len(struct TCP_Server_Info *server,
 extern void smb2_set_next_command(struct cifs_tcon *tcon,
                                  struct smb_rqst *rqst);
 extern void smb2_set_related(struct smb_rqst *rqst);
+extern void smb2_set_replay(struct TCP_Server_Info *server,
+                           struct smb_rqst *rqst);
+extern bool smb2_should_replay(struct cifs_tcon *tcon,
+                         int *pretries,
+                         int *pcur_sleep);
 
 /*
  * SMB2 Worker functions - most of protocol specific implementation details