summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/smb/client/cifsglob.h2
-rw-r--r--fs/smb/client/smb2pdu.c79
2 files changed, 74 insertions, 7 deletions
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 3eca5bfb7030..f6ebd3fd176d 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -1507,6 +1507,8 @@ struct cifs_io_subrequest {
int result;
bool have_xid;
bool replay;
+ unsigned int retries; /* number of retries so far */
+ unsigned int cur_sleep; /* time to sleep before replay */
struct kvec iov[2];
struct TCP_Server_Info *server;
#ifdef CONFIG_CIFS_SMB_DIRECT
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 7d75ba675f77..fa22702a61a6 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -4650,9 +4650,19 @@ smb2_readv_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
iov_iter_truncate(&rqst.rq_iter, rdata->got_bytes);
rc = smb2_verify_signature(&rqst, server);
- if (rc)
+ if (rc) {
cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n",
- rc);
+ rc);
+ rdata->subreq.error = rc;
+ rdata->result = rc;
+
+ if (is_replayable_error(rc)) {
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed);
+ __set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags);
+ } else
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_bad);
+ } else
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_progress);
}
/* FIXME: should this be counted toward the initiating task? */
task_io_account_read(rdata->got_bytes);
@@ -4728,6 +4738,14 @@ do_retry:
if (rdata->got_bytes)
__set_bit(NETFS_SREQ_MADE_PROGRESS, &rdata->subreq.flags);
}
+
+ /* see if we need to retry */
+ if (is_replayable_error(rdata->result) &&
+ smb2_should_replay(tcon,
+ &rdata->retries,
+ &rdata->cur_sleep))
+ rdata->replay = true;
+
trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, rdata->credits.value,
server->credits, server->in_flight,
0, cifs_trace_rw_credits_read_response_clear);
@@ -4776,7 +4794,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
rc = smb2_new_read_req(
(void **) &buf, &total_len, &io_parms, rdata, 0, 0);
if (rc)
- return rc;
+ goto out;
if (smb3_encryption_required(io_parms.tcon))
flags |= CIFS_TRANSFORM_REQ;
@@ -4788,6 +4806,13 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
shdr = (struct smb2_hdr *)buf;
+ if (rdata->replay) {
+ /* Back-off before retry */
+ if (rdata->cur_sleep)
+ msleep(rdata->cur_sleep);
+ smb2_set_replay(server, &rqst);
+ }
+
if (rdata->credits.value > 0) {
shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(io_parms.length,
SMB2_MAX_BUFFER_SIZE));
@@ -4823,6 +4848,17 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
async_readv_out:
cifs_small_buf_release(buf);
+
+out:
+ /* if the send error is retryable, let netfs know about it */
+ if (is_replayable_error(rc) &&
+ smb2_should_replay(tcon,
+ &rdata->retries,
+ &rdata->cur_sleep)) {
+ trace_netfs_sreq(&rdata->subreq, netfs_sreq_trace_io_retry_needed);
+ __set_bit(NETFS_SREQ_NEED_RETRY, &rdata->subreq.flags);
+ }
+
return rc;
}
@@ -4936,14 +4972,20 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
switch (mid->mid_state) {
case MID_RESPONSE_RECEIVED:
- trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_progress);
credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
credits.instance = server->reconnect_instance;
result = smb2_check_receive(mid, server, 0);
if (result != 0) {
- trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_bad);
+ if (is_replayable_error(result)) {
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_retry_needed);
+ __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
+ } else {
+ wdata->subreq.error = result;
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_bad);
+ }
break;
}
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_progress);
written = le32_to_cpu(rsp->DataLength);
/*
@@ -4958,7 +5000,7 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
cifs_stats_bytes_written(tcon, written);
if (written < wdata->subreq.len) {
- wdata->result = -ENOSPC;
+ result = -ENOSPC;
} else if (written > 0) {
wdata->subreq.len = written;
__set_bit(NETFS_SREQ_MADE_PROGRESS, &wdata->subreq.flags);
@@ -5000,6 +5042,7 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
}
#endif
if (result) {
+ wdata->result = result;
cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
trace_smb3_write_err(wdata->rreq->debug_id,
wdata->subreq.debug_index,
@@ -5022,6 +5065,14 @@ smb2_writev_callback(struct TCP_Server_Info *server, struct mid_q_entry *mid)
server->credits, server->in_flight,
0, cifs_trace_rw_credits_write_response_clear);
wdata->credits.value = 0;
+
+ /* see if we need to retry */
+ if (is_replayable_error(wdata->result) &&
+ smb2_should_replay(tcon,
+ &wdata->retries,
+ &wdata->cur_sleep))
+ wdata->replay = true;
+
cifs_write_subrequest_terminated(wdata, result ?: written);
release_mid(server, mid);
trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, 0,
@@ -5140,8 +5191,12 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
}
#endif
- if (wdata->subreq.retry_count > 0)
+ if (wdata->replay) {
+ /* Back-off before retry */
+ if (wdata->cur_sleep)
+ msleep(wdata->cur_sleep);
smb2_set_replay(server, &rqst);
+ }
cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n",
io_parms->offset, io_parms->length, iov_iter_count(&wdata->subreq.io_iter));
@@ -5187,6 +5242,16 @@ smb2_async_writev(struct cifs_io_subrequest *wdata)
async_writev_out:
cifs_small_buf_release(req);
out:
+ /* if the send error is retryable, let netfs know about it */
+ if (is_replayable_error(rc) &&
+ smb2_should_replay(tcon,
+ &wdata->retries,
+ &wdata->cur_sleep)) {
+ wdata->replay = true;
+ trace_netfs_sreq(&wdata->subreq, netfs_sreq_trace_io_retry_needed);
+ __set_bit(NETFS_SREQ_NEED_RETRY, &wdata->subreq.flags);
+ }
+
if (rc) {
trace_smb3_rw_credits(wdata->rreq->debug_id,
wdata->subreq.debug_index,