Skip to content

Commit 4f1fffa

Browse files
sprasad-microsoftSteve French
authored andcommitted
cifs: commands that are retried should have replay flag set
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>
1 parent 64cc377 commit 4f1fffa

File tree

6 files changed

+404
-45
lines changed

6 files changed

+404
-45
lines changed

fs/smb/client/cached_dir.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,21 +145,27 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
145145
struct cached_fid *cfid;
146146
struct cached_fids *cfids;
147147
const char *npath;
148+
int retries = 0, cur_sleep = 1;
148149

149150
if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache ||
150151
is_smb1_server(tcon->ses->server) || (dir_cache_timeout == 0))
151152
return -EOPNOTSUPP;
152153

153154
ses = tcon->ses;
154-
server = cifs_pick_channel(ses);
155155
cfids = tcon->cfids;
156156

157-
if (!server->ops->new_lease_key)
158-
return -EIO;
159-
160157
if (cifs_sb->root == NULL)
161158
return -ENOENT;
162159

160+
replay_again:
161+
/* reinitialize for possible replay */
162+
flags = 0;
163+
oplock = SMB2_OPLOCK_LEVEL_II;
164+
server = cifs_pick_channel(ses);
165+
166+
if (!server->ops->new_lease_key)
167+
return -EIO;
168+
163169
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
164170
if (!utf16_path)
165171
return -ENOMEM;
@@ -268,6 +274,11 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
268274
*/
269275
cfid->has_lease = true;
270276

277+
if (retries) {
278+
smb2_set_replay(server, &rqst[0]);
279+
smb2_set_replay(server, &rqst[1]);
280+
}
281+
271282
rc = compound_send_recv(xid, ses, server,
272283
flags, 2, rqst,
273284
resp_buftype, rsp_iov);
@@ -368,6 +379,10 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
368379
}
369380
kfree(utf16_path);
370381

382+
if (is_replayable_error(rc) &&
383+
smb2_should_replay(tcon, &retries, &cur_sleep))
384+
goto replay_again;
385+
371386
return rc;
372387
}
373388

fs/smb/client/cifsglob.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@
4949
*/
5050
#define CIFS_DEF_ACTIMEO (1 * HZ)
5151

52+
/*
53+
* max sleep time before retry to server
54+
*/
55+
#define CIFS_MAX_SLEEP 2000
56+
5257
/*
5358
* max attribute cache timeout (jiffies) - 2^30
5459
*/

fs/smb/client/smb2inode.c

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,15 +120,21 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
120120
unsigned int size[2];
121121
void *data[2];
122122
int len;
123+
int retries = 0, cur_sleep = 1;
124+
125+
replay_again:
126+
/* reinitialize for possible replay */
127+
flags = 0;
128+
oplock = SMB2_OPLOCK_LEVEL_NONE;
129+
num_rqst = 0;
130+
server = cifs_pick_channel(ses);
123131

124132
vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
125133
if (vars == NULL)
126134
return -ENOMEM;
127135
rqst = &vars->rqst[0];
128136
rsp_iov = &vars->rsp_iov[0];
129137

130-
server = cifs_pick_channel(ses);
131-
132138
if (smb3_encryption_required(tcon))
133139
flags |= CIFS_TRANSFORM_REQ;
134140

@@ -463,15 +469,24 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
463469
num_rqst++;
464470

465471
if (cfile) {
472+
if (retries)
473+
for (i = 1; i < num_rqst - 2; i++)
474+
smb2_set_replay(server, &rqst[i]);
475+
466476
rc = compound_send_recv(xid, ses, server,
467477
flags, num_rqst - 2,
468478
&rqst[1], &resp_buftype[1],
469479
&rsp_iov[1]);
470-
} else
480+
} else {
481+
if (retries)
482+
for (i = 0; i < num_rqst; i++)
483+
smb2_set_replay(server, &rqst[i]);
484+
471485
rc = compound_send_recv(xid, ses, server,
472486
flags, num_rqst,
473487
rqst, resp_buftype,
474488
rsp_iov);
489+
}
475490

476491
finished:
477492
num_rqst = 0;
@@ -620,9 +635,6 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
620635
}
621636
SMB2_close_free(&rqst[num_rqst]);
622637

623-
if (cfile)
624-
cifsFileInfo_put(cfile);
625-
626638
num_cmds += 2;
627639
if (out_iov && out_buftype) {
628640
memcpy(out_iov, rsp_iov, num_cmds * sizeof(*out_iov));
@@ -632,7 +644,16 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
632644
for (i = 0; i < num_cmds; i++)
633645
free_rsp_buf(resp_buftype[i], rsp_iov[i].iov_base);
634646
}
647+
num_cmds -= 2; /* correct num_cmds as there could be a retry */
635648
kfree(vars);
649+
650+
if (is_replayable_error(rc) &&
651+
smb2_should_replay(tcon, &retries, &cur_sleep))
652+
goto replay_again;
653+
654+
if (cfile)
655+
cifsFileInfo_put(cfile);
656+
636657
return rc;
637658
}
638659

fs/smb/client/smb2ops.c

Lines changed: 114 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1108,7 +1108,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
11081108
{
11091109
struct smb2_compound_vars *vars;
11101110
struct cifs_ses *ses = tcon->ses;
1111-
struct TCP_Server_Info *server = cifs_pick_channel(ses);
1111+
struct TCP_Server_Info *server;
11121112
struct smb_rqst *rqst;
11131113
struct kvec *rsp_iov;
11141114
__le16 *utf16_path = NULL;
@@ -1124,6 +1124,13 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
11241124
struct smb2_file_full_ea_info *ea = NULL;
11251125
struct smb2_query_info_rsp *rsp;
11261126
int rc, used_len = 0;
1127+
int retries = 0, cur_sleep = 1;
1128+
1129+
replay_again:
1130+
/* reinitialize for possible replay */
1131+
flags = CIFS_CP_CREATE_CLOSE_OP;
1132+
oplock = SMB2_OPLOCK_LEVEL_NONE;
1133+
server = cifs_pick_channel(ses);
11271134

11281135
if (smb3_encryption_required(tcon))
11291136
flags |= CIFS_TRANSFORM_REQ;
@@ -1244,6 +1251,12 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
12441251
goto sea_exit;
12451252
smb2_set_related(&rqst[2]);
12461253

1254+
if (retries) {
1255+
smb2_set_replay(server, &rqst[0]);
1256+
smb2_set_replay(server, &rqst[1]);
1257+
smb2_set_replay(server, &rqst[2]);
1258+
}
1259+
12471260
rc = compound_send_recv(xid, ses, server,
12481261
flags, 3, rqst,
12491262
resp_buftype, rsp_iov);
@@ -1260,6 +1273,11 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
12601273
kfree(vars);
12611274
out_free_path:
12621275
kfree(utf16_path);
1276+
1277+
if (is_replayable_error(rc) &&
1278+
smb2_should_replay(tcon, &retries, &cur_sleep))
1279+
goto replay_again;
1280+
12631281
return rc;
12641282
}
12651283
#endif
@@ -1484,7 +1502,7 @@ smb2_ioctl_query_info(const unsigned int xid,
14841502
struct smb_rqst *rqst;
14851503
struct kvec *rsp_iov;
14861504
struct cifs_ses *ses = tcon->ses;
1487-
struct TCP_Server_Info *server = cifs_pick_channel(ses);
1505+
struct TCP_Server_Info *server;
14881506
char __user *arg = (char __user *)p;
14891507
struct smb_query_info qi;
14901508
struct smb_query_info __user *pqi;
@@ -1501,6 +1519,13 @@ smb2_ioctl_query_info(const unsigned int xid,
15011519
void *data[2];
15021520
int create_options = is_dir ? CREATE_NOT_FILE : CREATE_NOT_DIR;
15031521
void (*free_req1_func)(struct smb_rqst *r);
1522+
int retries = 0, cur_sleep = 1;
1523+
1524+
replay_again:
1525+
/* reinitialize for possible replay */
1526+
flags = CIFS_CP_CREATE_CLOSE_OP;
1527+
oplock = SMB2_OPLOCK_LEVEL_NONE;
1528+
server = cifs_pick_channel(ses);
15041529

15051530
vars = kzalloc(sizeof(*vars), GFP_ATOMIC);
15061531
if (vars == NULL)
@@ -1641,6 +1666,12 @@ smb2_ioctl_query_info(const unsigned int xid,
16411666
goto free_req_1;
16421667
smb2_set_related(&rqst[2]);
16431668

1669+
if (retries) {
1670+
smb2_set_replay(server, &rqst[0]);
1671+
smb2_set_replay(server, &rqst[1]);
1672+
smb2_set_replay(server, &rqst[2]);
1673+
}
1674+
16441675
rc = compound_send_recv(xid, ses, server,
16451676
flags, 3, rqst,
16461677
resp_buftype, rsp_iov);
@@ -1701,6 +1732,11 @@ smb2_ioctl_query_info(const unsigned int xid,
17011732
kfree(buffer);
17021733
free_vars:
17031734
kfree(vars);
1735+
1736+
if (is_replayable_error(rc) &&
1737+
smb2_should_replay(tcon, &retries, &cur_sleep))
1738+
goto replay_again;
1739+
17041740
return rc;
17051741
}
17061742

@@ -2227,8 +2263,14 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
22272263
struct cifs_open_parms oparms;
22282264
struct smb2_query_directory_rsp *qd_rsp = NULL;
22292265
struct smb2_create_rsp *op_rsp = NULL;
2230-
struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
2231-
int retry_count = 0;
2266+
struct TCP_Server_Info *server;
2267+
int retries = 0, cur_sleep = 1;
2268+
2269+
replay_again:
2270+
/* reinitialize for possible replay */
2271+
flags = 0;
2272+
oplock = SMB2_OPLOCK_LEVEL_NONE;
2273+
server = cifs_pick_channel(tcon->ses);
22322274

22332275
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
22342276
if (!utf16_path)
@@ -2278,14 +2320,15 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
22782320

22792321
smb2_set_related(&rqst[1]);
22802322

2281-
again:
2323+
if (retries) {
2324+
smb2_set_replay(server, &rqst[0]);
2325+
smb2_set_replay(server, &rqst[1]);
2326+
}
2327+
22822328
rc = compound_send_recv(xid, tcon->ses, server,
22832329
flags, 2, rqst,
22842330
resp_buftype, rsp_iov);
22852331

2286-
if (rc == -EAGAIN && retry_count++ < 10)
2287-
goto again;
2288-
22892332
/* If the open failed there is nothing to do */
22902333
op_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
22912334
if (op_rsp == NULL || op_rsp->hdr.Status != STATUS_SUCCESS) {
@@ -2333,6 +2376,11 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
23332376
SMB2_query_directory_free(&rqst[1]);
23342377
free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
23352378
free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
2379+
2380+
if (is_replayable_error(rc) &&
2381+
smb2_should_replay(tcon, &retries, &cur_sleep))
2382+
goto replay_again;
2383+
23362384
return rc;
23372385
}
23382386

@@ -2457,6 +2505,22 @@ smb2_oplock_response(struct cifs_tcon *tcon, __u64 persistent_fid,
24572505
CIFS_CACHE_READ(cinode) ? 1 : 0);
24582506
}
24592507

2508+
void
2509+
smb2_set_replay(struct TCP_Server_Info *server, struct smb_rqst *rqst)
2510+
{
2511+
struct smb2_hdr *shdr;
2512+
2513+
if (server->dialect < SMB30_PROT_ID)
2514+
return;
2515+
2516+
shdr = (struct smb2_hdr *)(rqst->rq_iov[0].iov_base);
2517+
if (shdr == NULL) {
2518+
cifs_dbg(FYI, "shdr NULL in smb2_set_related\n");
2519+
return;
2520+
}
2521+
shdr->Flags |= SMB2_FLAGS_REPLAY_OPERATION;
2522+
}
2523+
24602524
void
24612525
smb2_set_related(struct smb_rqst *rqst)
24622526
{
@@ -2529,6 +2593,27 @@ smb2_set_next_command(struct cifs_tcon *tcon, struct smb_rqst *rqst)
25292593
shdr->NextCommand = cpu_to_le32(len);
25302594
}
25312595

2596+
/*
2597+
* helper function for exponential backoff and check if replayable
2598+
*/
2599+
bool smb2_should_replay(struct cifs_tcon *tcon,
2600+
int *pretries,
2601+
int *pcur_sleep)
2602+
{
2603+
if (!pretries || !pcur_sleep)
2604+
return false;
2605+
2606+
if (tcon->retry || (*pretries)++ < tcon->ses->server->retrans) {
2607+
msleep(*pcur_sleep);
2608+
(*pcur_sleep) = ((*pcur_sleep) << 1);
2609+
if ((*pcur_sleep) > CIFS_MAX_SLEEP)
2610+
(*pcur_sleep) = CIFS_MAX_SLEEP;
2611+
return true;
2612+
}
2613+
2614+
return false;
2615+
}
2616+
25322617
/*
25332618
* Passes the query info response back to the caller on success.
25342619
* Caller need to free this with free_rsp_buf().
@@ -2542,7 +2627,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
25422627
{
25432628
struct smb2_compound_vars *vars;
25442629
struct cifs_ses *ses = tcon->ses;
2545-
struct TCP_Server_Info *server = cifs_pick_channel(ses);
2630+
struct TCP_Server_Info *server;
25462631
int flags = CIFS_CP_CREATE_CLOSE_OP;
25472632
struct smb_rqst *rqst;
25482633
int resp_buftype[3];
@@ -2553,6 +2638,13 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
25532638
int rc;
25542639
__le16 *utf16_path;
25552640
struct cached_fid *cfid = NULL;
2641+
int retries = 0, cur_sleep = 1;
2642+
2643+
replay_again:
2644+
/* reinitialize for possible replay */
2645+
flags = CIFS_CP_CREATE_CLOSE_OP;
2646+
oplock = SMB2_OPLOCK_LEVEL_NONE;
2647+
server = cifs_pick_channel(ses);
25562648

25572649
if (!path)
25582650
path = "";
@@ -2633,6 +2725,14 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
26332725
goto qic_exit;
26342726
smb2_set_related(&rqst[2]);
26352727

2728+
if (retries) {
2729+
if (!cfid) {
2730+
smb2_set_replay(server, &rqst[0]);
2731+
smb2_set_replay(server, &rqst[2]);
2732+
}
2733+
smb2_set_replay(server, &rqst[1]);
2734+
}
2735+
26362736
if (cfid) {
26372737
rc = compound_send_recv(xid, ses, server,
26382738
flags, 1, &rqst[1],
@@ -2665,6 +2765,11 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
26652765
kfree(vars);
26662766
out_free_path:
26672767
kfree(utf16_path);
2768+
2769+
if (is_replayable_error(rc) &&
2770+
smb2_should_replay(tcon, &retries, &cur_sleep))
2771+
goto replay_again;
2772+
26682773
return rc;
26692774
}
26702775

0 commit comments

Comments
 (0)