Skip to content

Commit 6a5dcd4

Browse files
dhowellsSteve French
authored andcommitted
cifs: Fix lack of credit renegotiation on read retry
When netfslib asks cifs to issue a read operation, it prefaces this with a call to ->clamp_length() which cifs uses to negotiate credits, providing receive capacity on the server; however, in the event that a read op needs reissuing, netfslib doesn't call ->clamp_length() again as that could shorten the subrequest, leaving a gap. This causes the retried read to be done with zero credits which causes the server to reject it with STATUS_INVALID_PARAMETER. This is a problem for a DIO read that is requested that would go over the EOF. The short read will be retried, causing EINVAL to be returned to the user when it fails. Fix this by making cifs_req_issue_read() negotiate new credits if retrying (NETFS_SREQ_RETRYING now gets set in the read side as well as the write side in this instance). This isn't sufficient, however: the new credits might not be sufficient to complete the remainder of the read, so also add an additional field, rreq->actual_len, that holds the actual size of the op we want to perform without having to alter subreq->len. We then rely on repeated short reads being retried until we finish the read or reach the end of file and make a zero-length read. Also fix a couple of places where the subrequest start and length need to be altered by the amount so far transferred when being used. Fixes: 69c3c02 ("cifs: Implement netfslib hooks") Signed-off-by: David Howells <dhowells@redhat.com> cc: Steve French <sfrench@samba.org> cc: Paulo Alcantara <pc@manguebit.com> cc: Jeff Layton <jlayton@kernel.org> cc: linux-cifs@vger.kernel.org cc: netfs@lists.linux.dev cc: linux-fsdevel@vger.kernel.org Signed-off-by: Steve French <stfrench@microsoft.com>
1 parent 86987d8 commit 6a5dcd4

File tree

6 files changed

+55
-16
lines changed

6 files changed

+55
-16
lines changed

fs/netfs/io.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,13 +306,15 @@ static bool netfs_rreq_perform_resubmissions(struct netfs_io_request *rreq)
306306
break;
307307
subreq->source = NETFS_DOWNLOAD_FROM_SERVER;
308308
subreq->error = 0;
309+
__set_bit(NETFS_SREQ_RETRYING, &subreq->flags);
309310
netfs_stat(&netfs_n_rh_download_instead);
310311
trace_netfs_sreq(subreq, netfs_sreq_trace_download_instead);
311312
netfs_get_subrequest(subreq, netfs_sreq_trace_get_resubmit);
312313
atomic_inc(&rreq->nr_outstanding);
313314
netfs_reset_subreq_iter(rreq, subreq);
314315
netfs_read_from_server(rreq, subreq);
315316
} else if (test_bit(NETFS_SREQ_SHORT_IO, &subreq->flags)) {
317+
__set_bit(NETFS_SREQ_RETRYING, &subreq->flags);
316318
netfs_reset_subreq_iter(rreq, subreq);
317319
netfs_rreq_short_read(rreq, subreq);
318320
}

fs/smb/client/cifsglob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,6 +1485,7 @@ struct cifs_io_subrequest {
14851485
struct cifs_io_request *req;
14861486
};
14871487
ssize_t got_bytes;
1488+
size_t actual_len;
14881489
unsigned int xid;
14891490
int result;
14901491
bool have_xid;

fs/smb/client/file.c

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ static void cifs_issue_write(struct netfs_io_subrequest *subreq)
111111
goto fail;
112112
}
113113

114+
wdata->actual_len = wdata->subreq.len;
114115
rc = adjust_credits(wdata->server, wdata, cifs_trace_rw_credits_issue_write_adjust);
115116
if (rc)
116117
goto fail;
@@ -153,7 +154,7 @@ static bool cifs_clamp_length(struct netfs_io_subrequest *subreq)
153154
struct cifs_io_request *req = container_of(subreq->rreq, struct cifs_io_request, rreq);
154155
struct TCP_Server_Info *server = req->server;
155156
struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb);
156-
size_t rsize = 0;
157+
size_t rsize;
157158
int rc;
158159

159160
rdata->xid = get_xid();
@@ -166,8 +167,8 @@ static bool cifs_clamp_length(struct netfs_io_subrequest *subreq)
166167
cifs_sb->ctx);
167168

168169

169-
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize, &rsize,
170-
&rdata->credits);
170+
rc = server->ops->wait_mtu_credits(server, cifs_sb->ctx->rsize,
171+
&rsize, &rdata->credits);
171172
if (rc) {
172173
subreq->error = rc;
173174
return false;
@@ -183,7 +184,8 @@ static bool cifs_clamp_length(struct netfs_io_subrequest *subreq)
183184
server->credits, server->in_flight, 0,
184185
cifs_trace_rw_credits_read_submit);
185186

186-
subreq->len = min_t(size_t, subreq->len, rsize);
187+
subreq->len = umin(subreq->len, rsize);
188+
rdata->actual_len = subreq->len;
187189

188190
#ifdef CONFIG_CIFS_SMB_DIRECT
189191
if (server->smbd_conn)
@@ -203,12 +205,39 @@ static void cifs_req_issue_read(struct netfs_io_subrequest *subreq)
203205
struct netfs_io_request *rreq = subreq->rreq;
204206
struct cifs_io_subrequest *rdata = container_of(subreq, struct cifs_io_subrequest, subreq);
205207
struct cifs_io_request *req = container_of(subreq->rreq, struct cifs_io_request, rreq);
208+
struct TCP_Server_Info *server = req->server;
209+
struct cifs_sb_info *cifs_sb = CIFS_SB(rreq->inode->i_sb);
206210
int rc = 0;
207211

208212
cifs_dbg(FYI, "%s: op=%08x[%x] mapping=%p len=%zu/%zu\n",
209213
__func__, rreq->debug_id, subreq->debug_index, rreq->mapping,
210214
subreq->transferred, subreq->len);
211215

216+
if (test_bit(NETFS_SREQ_RETRYING, &subreq->flags)) {
217+
/*
218+
* As we're issuing a retry, we need to negotiate some new
219+
* credits otherwise the server may reject the op with
220+
* INVALID_PARAMETER. Note, however, we may get back less
221+
* credit than we need to complete the op, in which case, we
222+
* shorten the op and rely on additional rounds of retry.
223+
*/
224+
size_t rsize = umin(subreq->len - subreq->transferred,
225+
cifs_sb->ctx->rsize);
226+
227+
rc = server->ops->wait_mtu_credits(server, rsize, &rdata->actual_len,
228+
&rdata->credits);
229+
if (rc)
230+
goto out;
231+
232+
rdata->credits.in_flight_check = 1;
233+
234+
trace_smb3_rw_credits(rdata->rreq->debug_id,
235+
rdata->subreq.debug_index,
236+
rdata->credits.value,
237+
server->credits, server->in_flight, 0,
238+
cifs_trace_rw_credits_read_resubmit);
239+
}
240+
212241
if (req->cfile->invalidHandle) {
213242
do {
214243
rc = cifs_reopen_file(req->cfile, true);

fs/smb/client/smb2ops.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server,
301301
unsigned int /*enum smb3_rw_credits_trace*/ trace)
302302
{
303303
struct cifs_credits *credits = &subreq->credits;
304-
int new_val = DIV_ROUND_UP(subreq->subreq.len, SMB2_MAX_BUFFER_SIZE);
304+
int new_val = DIV_ROUND_UP(subreq->actual_len, SMB2_MAX_BUFFER_SIZE);
305305
int scredits, in_flight;
306306

307307
if (!credits->value || credits->value == new_val)

fs/smb/client/smb2pdu.c

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4529,9 +4529,9 @@ smb2_readv_callback(struct mid_q_entry *mid)
45294529
"rdata server %p != mid server %p",
45304530
rdata->server, mid->server);
45314531

4532-
cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%zu\n",
4532+
cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%zu/%zu\n",
45334533
__func__, mid->mid, mid->mid_state, rdata->result,
4534-
rdata->subreq.len);
4534+
rdata->actual_len, rdata->subreq.len - rdata->subreq.transferred);
45354535

45364536
switch (mid->mid_state) {
45374537
case MID_RESPONSE_RECEIVED:
@@ -4585,15 +4585,18 @@ smb2_readv_callback(struct mid_q_entry *mid)
45854585
rdata->subreq.debug_index,
45864586
rdata->xid,
45874587
rdata->req->cfile->fid.persistent_fid,
4588-
tcon->tid, tcon->ses->Suid, rdata->subreq.start,
4589-
rdata->subreq.len, rdata->result);
4588+
tcon->tid, tcon->ses->Suid,
4589+
rdata->subreq.start + rdata->subreq.transferred,
4590+
rdata->actual_len,
4591+
rdata->result);
45904592
} else
45914593
trace_smb3_read_done(rdata->rreq->debug_id,
45924594
rdata->subreq.debug_index,
45934595
rdata->xid,
45944596
rdata->req->cfile->fid.persistent_fid,
45954597
tcon->tid, tcon->ses->Suid,
4596-
rdata->subreq.start, rdata->got_bytes);
4598+
rdata->subreq.start + rdata->subreq.transferred,
4599+
rdata->got_bytes);
45974600

45984601
if (rdata->result == -ENODATA) {
45994602
/* We may have got an EOF error because fallocate
@@ -4621,6 +4624,7 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
46214624
{
46224625
int rc, flags = 0;
46234626
char *buf;
4627+
struct netfs_io_subrequest *subreq = &rdata->subreq;
46244628
struct smb2_hdr *shdr;
46254629
struct cifs_io_parms io_parms;
46264630
struct smb_rqst rqst = { .rq_iov = rdata->iov,
@@ -4631,15 +4635,15 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
46314635
int credit_request;
46324636

46334637
cifs_dbg(FYI, "%s: offset=%llu bytes=%zu\n",
4634-
__func__, rdata->subreq.start, rdata->subreq.len);
4638+
__func__, subreq->start, subreq->len);
46354639

46364640
if (!rdata->server)
46374641
rdata->server = cifs_pick_channel(tcon->ses);
46384642

46394643
io_parms.tcon = tlink_tcon(rdata->req->cfile->tlink);
46404644
io_parms.server = server = rdata->server;
4641-
io_parms.offset = rdata->subreq.start;
4642-
io_parms.length = rdata->subreq.len;
4645+
io_parms.offset = subreq->start + subreq->transferred;
4646+
io_parms.length = rdata->actual_len;
46434647
io_parms.persistent_fid = rdata->req->cfile->fid.persistent_fid;
46444648
io_parms.volatile_fid = rdata->req->cfile->fid.volatile_fid;
46454649
io_parms.pid = rdata->req->pid;
@@ -4654,11 +4658,13 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
46544658

46554659
rdata->iov[0].iov_base = buf;
46564660
rdata->iov[0].iov_len = total_len;
4661+
rdata->got_bytes = 0;
4662+
rdata->result = 0;
46574663

46584664
shdr = (struct smb2_hdr *)buf;
46594665

46604666
if (rdata->credits.value > 0) {
4661-
shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->subreq.len,
4667+
shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->actual_len,
46624668
SMB2_MAX_BUFFER_SIZE));
46634669
credit_request = le16_to_cpu(shdr->CreditCharge) + 8;
46644670
if (server->credits >= server->max_credits)
@@ -4682,11 +4688,11 @@ smb2_async_readv(struct cifs_io_subrequest *rdata)
46824688
if (rc) {
46834689
cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
46844690
trace_smb3_read_err(rdata->rreq->debug_id,
4685-
rdata->subreq.debug_index,
4691+
subreq->debug_index,
46864692
rdata->xid, io_parms.persistent_fid,
46874693
io_parms.tcon->tid,
46884694
io_parms.tcon->ses->Suid,
4689-
io_parms.offset, io_parms.length, rc);
4695+
io_parms.offset, rdata->actual_len, rc);
46904696
}
46914697

46924698
async_readv_out:

fs/smb/client/trace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
EM(cifs_trace_rw_credits_old_session, "old-session") \
3131
EM(cifs_trace_rw_credits_read_response_add, "rd-resp-add") \
3232
EM(cifs_trace_rw_credits_read_response_clear, "rd-resp-clr") \
33+
EM(cifs_trace_rw_credits_read_resubmit, "rd-resubmit") \
3334
EM(cifs_trace_rw_credits_read_submit, "rd-submit ") \
3435
EM(cifs_trace_rw_credits_write_prepare, "wr-prepare ") \
3536
EM(cifs_trace_rw_credits_write_response_add, "wr-resp-add") \

0 commit comments

Comments
 (0)