Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

NFSD: Encode a full READ_PLUS reply

Reply to the client with multiple hole and data segments. I use the
result of the first vfs_llseek() call for encoding as an optimization so
we don't have to immediately repeat the call. This also lets us encode
any remaining reply as data if we get an unexpected result while trying
to calculate a hole.

Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>

authored by

Anna Schumaker and committed by
J. Bruce Fields
9f0b5792 278765ea

+25 -24
+25 -24
fs/nfsd/nfs4xdr.c
··· 4603 4603 static __be32 4604 4604 nfsd4_encode_read_plus_data(struct nfsd4_compoundres *resp, 4605 4605 struct nfsd4_read *read, 4606 - unsigned long *maxcount, u32 *eof) 4606 + unsigned long *maxcount, u32 *eof, 4607 + loff_t *pos) 4607 4608 { 4608 4609 struct xdr_stream *xdr = &resp->xdr; 4609 4610 struct file *file = read->rd_nf->nf_file; 4610 4611 int starting_len = xdr->buf->len; 4611 - loff_t hole_pos = vfs_llseek(file, read->rd_offset, SEEK_HOLE); 4612 + loff_t hole_pos; 4612 4613 __be32 nfserr; 4613 4614 __be32 *p, tmp; 4614 4615 __be64 tmp64; 4615 4616 4617 + hole_pos = pos ? *pos : vfs_llseek(file, read->rd_offset, SEEK_HOLE); 4616 4618 if (hole_pos > read->rd_offset) 4617 4619 *maxcount = min_t(unsigned long, *maxcount, hole_pos - read->rd_offset); 4618 4620 *maxcount = min_t(unsigned long, *maxcount, (xdr->buf->buflen - xdr->buf->len)); ··· 4649 4647 { 4650 4648 struct file *file = read->rd_nf->nf_file; 4651 4649 loff_t data_pos = vfs_llseek(file, read->rd_offset, SEEK_DATA); 4650 + loff_t f_size = i_size_read(file_inode(file)); 4652 4651 unsigned long count; 4653 4652 __be32 *p; 4654 4653 4655 4654 if (data_pos == -ENXIO) 4656 - data_pos = i_size_read(file_inode(file)); 4657 - else if (data_pos <= read->rd_offset) 4658 - return nfserr_resource; 4655 + data_pos = f_size; 4656 + else if (data_pos <= read->rd_offset || (data_pos < f_size && data_pos % PAGE_SIZE)) 4657 + return nfsd4_encode_read_plus_data(resp, read, maxcount, eof, &f_size); 4659 4658 count = data_pos - read->rd_offset; 4660 4659 4661 4660 /* Content type, offset, byte count */ ··· 4668 4665 p = xdr_encode_hyper(p, read->rd_offset); 4669 4666 p = xdr_encode_hyper(p, count); 4670 4667 4671 - *eof = (read->rd_offset + count) >= i_size_read(file_inode(file)); 4668 + *eof = (read->rd_offset + count) >= f_size; 4672 4669 *maxcount = min_t(unsigned long, count, *maxcount); 4673 4670 return nfs_ok; 4674 4671 } ··· 4681 4678 struct xdr_stream *xdr = &resp->xdr; 4682 4679 struct file *file; 4683 4680 int starting_len = xdr->buf->len; 4681 + int last_segment = xdr->buf->len; 4684 4682 int segments = 0; 4685 4683 __be32 *p, tmp; 4684 + bool is_data; 4686 4685 loff_t pos; 4687 4686 u32 eof; 4688 4687 ··· 4708 4703 if (eof) 4709 4704 goto out; 4710 4705 4711 - pos = vfs_llseek(file, read->rd_offset, SEEK_DATA); 4712 - if (pos == -ENXIO) 4713 - pos = i_size_read(file_inode(file)); 4714 - else if (pos < 0) 4715 - pos = read->rd_offset; 4706 + pos = vfs_llseek(file, read->rd_offset, SEEK_HOLE); 4707 + is_data = pos > read->rd_offset; 4716 4708 4717 - if (pos == read->rd_offset) { 4709 + while (count > 0 && !eof) { 4718 4710 maxcount = count; 4719 - nfserr = nfsd4_encode_read_plus_data(resp, read, &maxcount, &eof); 4711 + if (is_data) 4712 + nfserr = nfsd4_encode_read_plus_data(resp, read, &maxcount, &eof, 4713 + segments == 0 ? &pos : NULL); 4714 + else 4715 + nfserr = nfsd4_encode_read_plus_hole(resp, read, &maxcount, &eof); 4720 4716 if (nfserr) 4721 4717 goto out; 4722 4718 count -= maxcount; 4723 4719 read->rd_offset += maxcount; 4724 - segments++; 4725 - } 4726 - 4727 - if (count > 0 && !eof) { 4728 - maxcount = count; 4729 - nfserr = nfsd4_encode_read_plus_hole(resp, read, &maxcount, &eof); 4730 - if (nfserr) 4731 - goto out; 4732 - count -= maxcount; 4733 - read->rd_offset += maxcount; 4720 + is_data = !is_data; 4721 + last_segment = xdr->buf->len; 4734 4722 segments++; 4735 4723 } 4736 4724 ··· 4735 4737 write_bytes_to_xdr_buf(xdr->buf, starting_len, &tmp, 4); 4736 4738 tmp = htonl(segments); 4737 4739 write_bytes_to_xdr_buf(xdr->buf, starting_len + 4, &tmp, 4); 4738 - nfserr = nfs_ok; 4740 + if (nfserr) { 4741 + xdr_truncate_encode(xdr, last_segment); 4742 + nfserr = nfs_ok; 4743 + } 4739 4744 } 4740 4745 4741 4746 return nfserr;