@@ -4385,14 +4385,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)
@@ -4404,6 +4408,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;
@@ -4418,18 +4423,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);
@@ -4453,6 +4464,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)
@@ -4476,21 +4488,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++;
}