Skip to content

Commit 1da29f2

Browse files
dhowellsSteve French
authored andcommitted
netfs, cifs: Fix handling of short DIO read
Short DIO reads, particularly in relation to cifs, are not being handled correctly by cifs and netfslib. This can be tested by doing a DIO read of a file where the size of read is larger than the size of the file. When it crosses the EOF, it gets a short read and this gets retried, and in the case of cifs, the retry read fails, with the failure being translated to ENODATA. Fix this by the following means: (1) Add a flag, NETFS_SREQ_HIT_EOF, for the filesystem to set when it detects that the read did hit the EOF. (2) Make the netfslib read assessment stop processing subrequests when it encounters one with that flag set. (3) Return rreq->transferred, the accumulated contiguous amount read to that point, to userspace for a DIO read. (4) Make cifs set the flag and clear the error if the read RPC returned ENODATA. (5) Make cifs set the flag and clear the error if a short read occurred without error and the read-to file position is now at the remote inode size. 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 6a5dcd4 commit 1da29f2

File tree

3 files changed

+21
-10
lines changed

3 files changed

+21
-10
lines changed

fs/netfs/io.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,8 @@ static void netfs_rreq_assess_dio(struct netfs_io_request *rreq)
368368
if (subreq->error || subreq->transferred == 0)
369369
break;
370370
transferred += subreq->transferred;
371-
if (subreq->transferred < subreq->len)
371+
if (subreq->transferred < subreq->len ||
372+
test_bit(NETFS_SREQ_HIT_EOF, &subreq->flags))
372373
break;
373374
}
374375

@@ -503,7 +504,8 @@ void netfs_subreq_terminated(struct netfs_io_subrequest *subreq,
503504

504505
subreq->error = 0;
505506
subreq->transferred += transferred_or_error;
506-
if (subreq->transferred < subreq->len)
507+
if (subreq->transferred < subreq->len &&
508+
!test_bit(NETFS_SREQ_HIT_EOF, &subreq->flags))
507509
goto incomplete;
508510

509511
complete:
@@ -782,10 +784,13 @@ int netfs_begin_read(struct netfs_io_request *rreq, bool sync)
782784
TASK_UNINTERRUPTIBLE);
783785

784786
ret = rreq->error;
785-
if (ret == 0 && rreq->submitted < rreq->len &&
786-
rreq->origin != NETFS_DIO_READ) {
787-
trace_netfs_failure(rreq, NULL, ret, netfs_fail_short_read);
788-
ret = -EIO;
787+
if (ret == 0) {
788+
if (rreq->origin == NETFS_DIO_READ) {
789+
ret = rreq->transferred;
790+
} else if (rreq->submitted < rreq->len) {
791+
trace_netfs_failure(rreq, NULL, ret, netfs_fail_short_read);
792+
ret = -EIO;
793+
}
789794
}
790795
} else {
791796
/* If we decrement nr_outstanding to 0, the ref belongs to us. */

fs/smb/client/smb2pdu.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4507,6 +4507,7 @@ static void
45074507
smb2_readv_callback(struct mid_q_entry *mid)
45084508
{
45094509
struct cifs_io_subrequest *rdata = mid->callback_data;
4510+
struct netfs_inode *ictx = netfs_inode(rdata->rreq->inode);
45104511
struct cifs_tcon *tcon = tlink_tcon(rdata->req->cfile->tlink);
45114512
struct TCP_Server_Info *server = rdata->server;
45124513
struct smb2_hdr *shdr =
@@ -4599,11 +4600,15 @@ smb2_readv_callback(struct mid_q_entry *mid)
45994600
rdata->got_bytes);
46004601

46014602
if (rdata->result == -ENODATA) {
4602-
/* We may have got an EOF error because fallocate
4603-
* failed to enlarge the file.
4604-
*/
4605-
if (rdata->subreq.start < rdata->subreq.rreq->i_size)
4603+
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
4604+
rdata->result = 0;
4605+
} else {
4606+
if (rdata->got_bytes < rdata->actual_len &&
4607+
rdata->subreq.start + rdata->subreq.transferred + rdata->got_bytes ==
4608+
ictx->remote_i_size) {
4609+
__set_bit(NETFS_SREQ_HIT_EOF, &rdata->subreq.flags);
46064610
rdata->result = 0;
4611+
}
46074612
}
46084613
trace_smb3_rw_credits(rreq_debug_id, subreq_debug_index, rdata->credits.value,
46094614
server->credits, server->in_flight,

include/linux/netfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ struct netfs_io_subrequest {
198198
#define NETFS_SREQ_NEED_RETRY 9 /* Set if the filesystem requests a retry */
199199
#define NETFS_SREQ_RETRYING 10 /* Set if we're retrying */
200200
#define NETFS_SREQ_FAILED 11 /* Set if the subreq failed unretryably */
201+
#define NETFS_SREQ_HIT_EOF 12 /* Set if we hit the EOF */
201202
};
202203

203204
enum netfs_io_origin {

0 commit comments

Comments
 (0)