]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
cifs: make retry logic in read/write path consistent with other paths
authorShyam Prasad N <sprasad@microsoft.com>
Sat, 31 Jan 2026 08:33:06 +0000 (14:03 +0530)
committerSteve French <stfrench@microsoft.com>
Sun, 8 Feb 2026 23:07:43 +0000 (17:07 -0600)
Today in most other code paths in cifs.ko, the decision of whether
to retry a command depends on two mount options: retrans and hard.
However, the read/write code paths diverged from this and would only
retry if the error returned was -EAGAIN. However, there are other
replayable errors in cifs.ko, for which is_replayable_errors helper
was written. This change makes read/write codepaths consistent with
other code-paths.

This change also does the following:
1. The SMB2 read/write code diverged significantly (presumably since
they were changed during netfs refactor at different times). This
changes the response verification logic to be consistent.
2. Moves the netfs tracepoints to slightly different locations in order
to make debugging easier.

Cc: David Howells <dhowells@redhat.com>
Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cifsglob.h
fs/smb/client/smb2pdu.c

index 3eca5bfb7030301265eac7addbe8ffcdd4bee9d8..f6ebd3fd176d7118eb75948170947b5513c40142 100644 (file)
@@ -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
index 7d75ba675f7747fabc0beb80462b67061d80b4b0..fa22702a61a6e9701672f654decf7de5f6a598e9 100644 (file)
@@ -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,