From: Anna Schumaker <Anna.Schumaker@xxxxxxxxxx> However, we still only reply to the READ_PLUS call with a single segment at this time. Signed-off-by: Anna Schumaker <Anna.Schumaker@xxxxxxxxxx> --- fs/nfsd/nfs4proc.c | 2 +- fs/nfsd/nfs4xdr.c | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 9630d33211f2..36305b846f5a 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -2528,7 +2528,7 @@ static inline u32 nfsd4_read_plus_rsize(struct svc_rqst *rqstp, struct nfsd4_op u32 maxcount = svc_max_payload(rqstp); u32 rlen = min(op->u.read.rd_length, maxcount); /* enough extra xdr space for encoding either a hole or data segment. */ - u32 segments = 1 + 2 + 2; + u32 segments = 2 * (1 + 2 + 2); return (op_encode_hdr_size + 2 + segments + XDR_QUADLEN(rlen)) * sizeof(__be32); } diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 9af92f538000..2fa39217c256 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -4378,10 +4378,14 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, struct xdr_stream *xdr = &resp->xdr; struct file *file = read->rd_nf->nf_file; int starting_len = xdr->buf->len; + loff_t hole_pos = vfs_llseek(file, read->rd_offset, SEEK_HOLE); __be32 nfserr; __be32 *p, tmp; __be64 tmp64; + if (hole_pos > read->rd_offset) + maxcount = min_t(unsigned long, maxcount, hole_pos - read->rd_offset); + /* Content type, offset, byte count */ p = xdr_reserve_space(xdr, 4 + 8 + 4); if (!p) @@ -4405,6 +4409,27 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, return nfs_ok; } +static __be32 +nfsd4_encode_read_plus_hole(struct nfsd4_compoundres *resp, + struct nfsd4_read *read, + unsigned long maxcount, u32 *eof) +{ + struct file *file = read->rd_nf->nf_file; + __be32 *p; + + /* Content type, offset, byte count */ + p = xdr_reserve_space(&resp->xdr, 4 + 8 + 8); + if (!p) + return nfserr_resource; + + *p++ = htonl(NFS4_CONTENT_HOLE); + p = xdr_encode_hyper(p, read->rd_offset); + p = xdr_encode_hyper(p, maxcount); + + *eof = (read->rd_offset + maxcount) >= i_size_read(file_inode(file)); + return nfs_ok; +} + static __be32 nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_read *read) @@ -4415,6 +4440,7 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr, int starting_len = xdr->buf->len; int segments = 0; __be32 *p, tmp; + loff_t pos; u32 eof; if (nfserr) @@ -4433,11 +4459,23 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr, maxcount = min_t(unsigned long, maxcount, read->rd_length); eof = read->rd_offset >= i_size_read(file_inode(file)); - if (!eof) { + if (eof) + goto out; + + pos = vfs_llseek(file, read->rd_offset, SEEK_DATA); + if (pos == -ENXIO) + pos = i_size_read(file_inode(file)); + + if (pos > read->rd_offset) { + maxcount = pos - read->rd_offset; + nfserr = nfsd4_encode_read_plus_hole(resp, read, maxcount, &eof); + segments++; + } else { nfserr = nfsd4_encode_read_plus_data(resp, read, maxcount, &eof); segments++; } +out: if (nfserr) xdr_truncate_encode(xdr, starting_len); else { -- 2.28.0