Commit 83a63072 authored by Trond Myklebust's avatar Trond Myklebust Committed by J. Bruce Fields

nfsd: fix nfs read eof detection

Currently, the knfsd server assumes that a short read indicates an
end of file. That assumption is incorrect. The short read means that
either we've hit the end of file, or we've hit a read error.

In the case of a read error, the client may want to retry (as per the
implementation recommendations in RFC1813 and RFC7530), but currently it
is being told that it hit an eof.

Move the code to detect eof from version specific code into the generic
nfsd read.

Report eof only in the two following cases:
1) read() returns a zero length short read with no error.
2) the offset+length of the read is >= the file size.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 65643f4c
...@@ -172,13 +172,8 @@ nfsd3_proc_read(struct svc_rqst *rqstp) ...@@ -172,13 +172,8 @@ nfsd3_proc_read(struct svc_rqst *rqstp)
nfserr = nfsd_read(rqstp, &resp->fh, nfserr = nfsd_read(rqstp, &resp->fh,
argp->offset, argp->offset,
rqstp->rq_vec, argp->vlen, rqstp->rq_vec, argp->vlen,
&resp->count); &resp->count,
if (nfserr == 0) { &resp->eof);
struct inode *inode = d_inode(resp->fh.fh_dentry);
resp->eof = nfsd_eof_on_read(cnt, resp->count, argp->offset,
inode->i_size);
}
RETURN_STATUS(nfserr); RETURN_STATUS(nfserr);
} }
......
...@@ -3472,7 +3472,7 @@ static __be32 nfsd4_encode_splice_read( ...@@ -3472,7 +3472,7 @@ static __be32 nfsd4_encode_splice_read(
len = maxcount; len = maxcount;
nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp, nfserr = nfsd_splice_read(read->rd_rqstp, read->rd_fhp,
file, read->rd_offset, &maxcount); file, read->rd_offset, &maxcount, &eof);
read->rd_length = maxcount; read->rd_length = maxcount;
if (nfserr) { if (nfserr) {
/* /*
...@@ -3484,9 +3484,6 @@ static __be32 nfsd4_encode_splice_read( ...@@ -3484,9 +3484,6 @@ static __be32 nfsd4_encode_splice_read(
return nfserr; return nfserr;
} }
eof = nfsd_eof_on_read(len, maxcount, read->rd_offset,
d_inode(read->rd_fhp->fh_dentry)->i_size);
*(p++) = htonl(eof); *(p++) = htonl(eof);
*(p++) = htonl(maxcount); *(p++) = htonl(maxcount);
...@@ -3557,15 +3554,13 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, ...@@ -3557,15 +3554,13 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp,
len = maxcount; len = maxcount;
nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset, nfserr = nfsd_readv(resp->rqstp, read->rd_fhp, file, read->rd_offset,
resp->rqstp->rq_vec, read->rd_vlen, &maxcount); resp->rqstp->rq_vec, read->rd_vlen, &maxcount,
&eof);
read->rd_length = maxcount; read->rd_length = maxcount;
if (nfserr) if (nfserr)
return nfserr; return nfserr;
xdr_truncate_encode(xdr, starting_len + 8 + ((maxcount+3)&~3)); xdr_truncate_encode(xdr, starting_len + 8 + ((maxcount+3)&~3));
eof = nfsd_eof_on_read(len, maxcount, read->rd_offset,
d_inode(read->rd_fhp->fh_dentry)->i_size);
tmp = htonl(eof); tmp = htonl(eof);
write_bytes_to_xdr_buf(xdr->buf, starting_len , &tmp, 4); write_bytes_to_xdr_buf(xdr->buf, starting_len , &tmp, 4);
tmp = htonl(maxcount); tmp = htonl(maxcount);
......
...@@ -172,6 +172,7 @@ nfsd_proc_read(struct svc_rqst *rqstp) ...@@ -172,6 +172,7 @@ nfsd_proc_read(struct svc_rqst *rqstp)
struct nfsd_readargs *argp = rqstp->rq_argp; struct nfsd_readargs *argp = rqstp->rq_argp;
struct nfsd_readres *resp = rqstp->rq_resp; struct nfsd_readres *resp = rqstp->rq_resp;
__be32 nfserr; __be32 nfserr;
u32 eof;
dprintk("nfsd: READ %s %d bytes at %d\n", dprintk("nfsd: READ %s %d bytes at %d\n",
SVCFH_fmt(&argp->fh), SVCFH_fmt(&argp->fh),
...@@ -195,7 +196,8 @@ nfsd_proc_read(struct svc_rqst *rqstp) ...@@ -195,7 +196,8 @@ nfsd_proc_read(struct svc_rqst *rqstp)
nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh), nfserr = nfsd_read(rqstp, fh_copy(&resp->fh, &argp->fh),
argp->offset, argp->offset,
rqstp->rq_vec, argp->vlen, rqstp->rq_vec, argp->vlen,
&resp->count); &resp->count,
&eof);
if (nfserr) return nfserr; if (nfserr) return nfserr;
return fh_getattr(&resp->fh, &resp->stat); return fh_getattr(&resp->fh, &resp->stat);
......
...@@ -834,12 +834,23 @@ static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe, ...@@ -834,12 +834,23 @@ static int nfsd_direct_splice_actor(struct pipe_inode_info *pipe,
return __splice_from_pipe(pipe, sd, nfsd_splice_actor); return __splice_from_pipe(pipe, sd, nfsd_splice_actor);
} }
static u32 nfsd_eof_on_read(struct file *file, loff_t offset, ssize_t len,
size_t expected)
{
if (expected != 0 && len == 0)
return 1;
if (offset+len >= i_size_read(file_inode(file)))
return 1;
return 0;
}
static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp, static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset, struct file *file, loff_t offset,
unsigned long *count, int host_err) unsigned long *count, u32 *eof, ssize_t host_err)
{ {
if (host_err >= 0) { if (host_err >= 0) {
nfsdstats.io_read += host_err; nfsdstats.io_read += host_err;
*eof = nfsd_eof_on_read(file, offset, host_err, *count);
*count = host_err; *count = host_err;
fsnotify_access(file); fsnotify_access(file);
trace_nfsd_read_io_done(rqstp, fhp, offset, *count); trace_nfsd_read_io_done(rqstp, fhp, offset, *count);
...@@ -851,7 +862,8 @@ static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -851,7 +862,8 @@ static __be32 nfsd_finish_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
} }
__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset, unsigned long *count) struct file *file, loff_t offset, unsigned long *count,
u32 *eof)
{ {
struct splice_desc sd = { struct splice_desc sd = {
.len = 0, .len = 0,
...@@ -859,25 +871,27 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -859,25 +871,27 @@ __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
.pos = offset, .pos = offset,
.u.data = rqstp, .u.data = rqstp,
}; };
int host_err; ssize_t host_err;
trace_nfsd_read_splice(rqstp, fhp, offset, *count); trace_nfsd_read_splice(rqstp, fhp, offset, *count);
rqstp->rq_next_page = rqstp->rq_respages + 1; rqstp->rq_next_page = rqstp->rq_respages + 1;
host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor);
return nfsd_finish_read(rqstp, fhp, file, offset, count, host_err); return nfsd_finish_read(rqstp, fhp, file, offset, count, eof, host_err);
} }
__be32 nfsd_readv(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_readv(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset, struct file *file, loff_t offset,
struct kvec *vec, int vlen, unsigned long *count) struct kvec *vec, int vlen, unsigned long *count,
u32 *eof)
{ {
struct iov_iter iter; struct iov_iter iter;
int host_err; loff_t ppos = offset;
ssize_t host_err;
trace_nfsd_read_vector(rqstp, fhp, offset, *count); trace_nfsd_read_vector(rqstp, fhp, offset, *count);
iov_iter_kvec(&iter, READ, vec, vlen, *count); iov_iter_kvec(&iter, READ, vec, vlen, *count);
host_err = vfs_iter_read(file, &iter, &offset, 0); host_err = vfs_iter_read(file, &iter, &ppos, 0);
return nfsd_finish_read(rqstp, fhp, file, offset, count, host_err); return nfsd_finish_read(rqstp, fhp, file, offset, count, eof, host_err);
} }
/* /*
...@@ -984,7 +998,8 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -984,7 +998,8 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
* N.B. After this call fhp needs an fh_put * N.B. After this call fhp needs an fh_put
*/ */
__be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
loff_t offset, struct kvec *vec, int vlen, unsigned long *count) loff_t offset, struct kvec *vec, int vlen, unsigned long *count,
u32 *eof)
{ {
struct nfsd_file *nf; struct nfsd_file *nf;
struct file *file; struct file *file;
...@@ -997,9 +1012,9 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -997,9 +1012,9 @@ __be32 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
file = nf->nf_file; file = nf->nf_file;
if (file->f_op->splice_read && test_bit(RQ_SPLICE_OK, &rqstp->rq_flags)) if (file->f_op->splice_read && test_bit(RQ_SPLICE_OK, &rqstp->rq_flags))
err = nfsd_splice_read(rqstp, fhp, file, offset, count); err = nfsd_splice_read(rqstp, fhp, file, offset, count, eof);
else else
err = nfsd_readv(rqstp, fhp, file, offset, vec, vlen, count); err = nfsd_readv(rqstp, fhp, file, offset, vec, vlen, count, eof);
nfsd_file_put(nf); nfsd_file_put(nf);
......
...@@ -80,13 +80,16 @@ __be32 nfsd_open_verified(struct svc_rqst *, struct svc_fh *, umode_t, ...@@ -80,13 +80,16 @@ __be32 nfsd_open_verified(struct svc_rqst *, struct svc_fh *, umode_t,
int, struct file **); int, struct file **);
__be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_splice_read(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset, struct file *file, loff_t offset,
unsigned long *count); unsigned long *count,
u32 *eof);
__be32 nfsd_readv(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_readv(struct svc_rqst *rqstp, struct svc_fh *fhp,
struct file *file, loff_t offset, struct file *file, loff_t offset,
struct kvec *vec, int vlen, struct kvec *vec, int vlen,
unsigned long *count); unsigned long *count,
u32 *eof);
__be32 nfsd_read(struct svc_rqst *, struct svc_fh *, __be32 nfsd_read(struct svc_rqst *, struct svc_fh *,
loff_t, struct kvec *, int, unsigned long *); loff_t, struct kvec *, int, unsigned long *,
u32 *eof);
__be32 nfsd_write(struct svc_rqst *, struct svc_fh *, loff_t, __be32 nfsd_write(struct svc_rqst *, struct svc_fh *, loff_t,
struct kvec *, int, unsigned long *, int); struct kvec *, int, unsigned long *, int);
__be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, __be32 nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp,
...@@ -149,23 +152,4 @@ static inline int nfsd_create_is_exclusive(int createmode) ...@@ -149,23 +152,4 @@ static inline int nfsd_create_is_exclusive(int createmode)
|| createmode == NFS4_CREATE_EXCLUSIVE4_1; || createmode == NFS4_CREATE_EXCLUSIVE4_1;
} }
static inline bool nfsd_eof_on_read(long requested, long read,
loff_t offset, loff_t size)
{
/* We assume a short read means eof: */
if (requested > read)
return true;
/*
* A non-short read might also reach end of file. The spec
* still requires us to set eof in that case.
*
* Further operations may have modified the file size since
* the read, so the following check is not atomic with the read.
* We've only seen that cause a problem for a client in the case
* where the read returned a count of 0 without setting eof.
* That case was fixed by the addition of the above check.
*/
return (offset + read >= size);
}
#endif /* LINUX_NFSD_VFS_H */ #endif /* LINUX_NFSD_VFS_H */
...@@ -151,7 +151,7 @@ struct nfsd3_readres { ...@@ -151,7 +151,7 @@ struct nfsd3_readres {
__be32 status; __be32 status;
struct svc_fh fh; struct svc_fh fh;
unsigned long count; unsigned long count;
int eof; __u32 eof;
}; };
struct nfsd3_writeres { struct nfsd3_writeres {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment