From: Anna Schumaker <Anna.Schumaker@xxxxxxxxxx> Reply to the client with multiple hole and data segments. This might have performance issues due to the number of calls to vfs_llseek(), depending on the underlying filesystem used on the server. Signed-off-by: Anna Schumaker <Anna.Schumaker@xxxxxxxxxx> --- fs/nfsd/nfs4xdr.c | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 552972b35547..c63846729d0b 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -4270,14 +4270,18 @@ nfsd4_encode_offload_status(struct nfsd4_compoundres *resp, __be32 nfserr, static __be32 nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, - struct nfsd4_read *read, - unsigned long maxcount, u32 *eof) + struct nfsd4_read *read, u32 *eof) { struct xdr_stream *xdr = &resp->xdr; struct file *file = read->rd_nf->nf_file; + unsigned long maxcount = read->rd_length; + loff_t hole_pos = vfs_llseek(file, read->rd_offset, SEEK_HOLE); __be32 nfserr; __be32 *p; + 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) @@ -4289,6 +4293,7 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, nfserr = nfsd4_encode_splice_read(resp, read, file, &maxcount, eof); else nfserr = nfsd4_encode_readv(resp, read, file, &maxcount, eof); + clear_bit(RQ_SPLICE_OK, &resp->rqstp->rq_flags); if (nfserr) return nfserr; @@ -4303,18 +4308,24 @@ nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, } static __be32 -nfsd4_encode_read_plus_hole(struct nfsd4_compoundres *resp, struct nfsd4_read *read, - unsigned long maxcount, u32 *eof) +nfsd4_encode_read_plus_hole(struct nfsd4_compoundres *resp, + struct nfsd4_read *read, u32 *eof, loff_t data_pos) { struct file *file = read->rd_nf->nf_file; + unsigned long maxcount = read->rd_length; __be32 *p; + if (data_pos == 0) + data_pos = vfs_llseek(file, read->rd_offset, SEEK_DATA); + if (data_pos == -ENXIO) + data_pos = i_size_read(file_inode(file)); + /* Content type, offset, byte count */ p = xdr_reserve_space(&resp->xdr, 4 + 8 + 8); if (!p) return nfserr_resource; - maxcount = min_t(unsigned long, maxcount, read->rd_length); + maxcount = min_t(unsigned long, maxcount, data_pos - read->rd_offset); *p++ = cpu_to_be32(NFS4_CONTENT_HOLE); p = xdr_encode_hyper(p, read->rd_offset); @@ -4338,6 +4349,7 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr, int starting_len = xdr->buf->len; unsigned int segments = 0; loff_t data_pos; + bool is_data; __be32 *p; if (nfserr) @@ -4361,21 +4373,26 @@ nfsd4_encode_read_plus(struct nfsd4_compoundres *resp, __be32 nfserr, maxcount = min_t(unsigned long, maxcount, (xdr->buf->buflen - xdr->buf->len)); maxcount = min_t(unsigned long, maxcount, read->rd_length); + read->rd_length = maxcount; data_pos = vfs_llseek(file, read->rd_offset, SEEK_DATA); if (data_pos == -ENXIO) data_pos = i_size_read(file_inode(file)); else if (data_pos < 0) data_pos = read->rd_offset; + is_data = (data_pos == read->rd_offset); + eof = read->rd_offset > i_size_read(file_inode(file)); - if (data_pos > read->rd_offset) { - nfserr = nfsd4_encode_read_plus_hole(resp, read, - data_pos - read->rd_offset, &eof); - segments++; - } + while (read->rd_length > 0 && !eof) { + if (is_data) + nfserr = nfsd4_encode_read_plus_data(resp, read, &eof); + else + nfserr = nfsd4_encode_read_plus_hole(resp, read, &eof, data_pos); - if (!nfserr && !eof && read->rd_length > 0) { - nfserr = nfsd4_encode_read_plus_data(resp, read, maxcount, &eof); + if (nfserr) + break; + is_data = !is_data; + data_pos = 0; segments++; } -- 2.24.1