aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2014-02-25 17:44:21 -0500
committerJ. Bruce Fields <bfields@redhat.com>2014-05-30 17:31:47 -0400
commit3e19ce762b537dd9aeefdd0849ba5f2f01ff83cf (patch)
tree3b261560ccf50dd7efadc2f61a6762566cfdbf45
parent6ac90391c6e36c536cfcedbe4801a77e304205b1 (diff)
rpc: xdr_truncate_encode
This will be used in the server side in a few cases: - when certain operations (read, readdir, readlink) fail after encoding a partial response. - when we run out of space after encoding a partial response. - in readlink, where we initially reserve PAGE_SIZE bytes for data, then truncate to the actual size. Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--include/linux/sunrpc/xdr.h1
-rw-r--r--net/sunrpc/xdr.c66
2 files changed, 67 insertions, 0 deletions
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 15f9204ee70b..e7bb2e3bd0fb 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -215,6 +215,7 @@ typedef int (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
215 215
216extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); 216extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
217extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); 217extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
218extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
218extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, 219extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
219 unsigned int base, unsigned int len); 220 unsigned int base, unsigned int len);
220extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr); 221extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr);
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index dd97ba3c4456..352f3b35bbe5 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -509,6 +509,72 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
509EXPORT_SYMBOL_GPL(xdr_reserve_space); 509EXPORT_SYMBOL_GPL(xdr_reserve_space);
510 510
511/** 511/**
512 * xdr_truncate_encode - truncate an encode buffer
513 * @xdr: pointer to xdr_stream
514 * @len: new length of buffer
515 *
516 * Truncates the xdr stream, so that xdr->buf->len == len,
517 * and xdr->p points at offset len from the start of the buffer, and
518 * head, tail, and page lengths are adjusted to correspond.
519 *
520 * If this means moving xdr->p to a different buffer, we assume that
521 * that the end pointer should be set to the end of the current page,
522 * except in the case of the head buffer when we assume the head
523 * buffer's current length represents the end of the available buffer.
524 *
525 * This is *not* safe to use on a buffer that already has inlined page
526 * cache pages (as in a zero-copy server read reply), except for the
527 * simple case of truncating from one position in the tail to another.
528 *
529 */
530void xdr_truncate_encode(struct xdr_stream *xdr, size_t len)
531{
532 struct xdr_buf *buf = xdr->buf;
533 struct kvec *head = buf->head;
534 struct kvec *tail = buf->tail;
535 int fraglen;
536 int new, old;
537
538 if (len > buf->len) {
539 WARN_ON_ONCE(1);
540 return;
541 }
542
543 fraglen = min_t(int, buf->len - len, tail->iov_len);
544 tail->iov_len -= fraglen;
545 buf->len -= fraglen;
546 if (tail->iov_len && buf->len == len) {
547 xdr->p = tail->iov_base + tail->iov_len;
548 /* xdr->end, xdr->iov should be set already */
549 return;
550 }
551 WARN_ON_ONCE(fraglen);
552 fraglen = min_t(int, buf->len - len, buf->page_len);
553 buf->page_len -= fraglen;
554 buf->len -= fraglen;
555
556 new = buf->page_base + buf->page_len;
557 old = new + fraglen;
558 xdr->page_ptr -= (old >> PAGE_SHIFT) - (new >> PAGE_SHIFT);
559
560 if (buf->page_len && buf->len == len) {
561 xdr->p = page_address(*xdr->page_ptr);
562 xdr->end = (void *)xdr->p + PAGE_SIZE;
563 xdr->p = (void *)xdr->p + (new % PAGE_SIZE);
564 /* xdr->iov should already be NULL */
565 return;
566 }
567 if (fraglen)
568 xdr->end = head->iov_base + head->iov_len;
569 /* (otherwise assume xdr->end is already set) */
570 head->iov_len = len;
571 buf->len = len;
572 xdr->p = head->iov_base + head->iov_len;
573 xdr->iov = buf->head;
574}
575EXPORT_SYMBOL(xdr_truncate_encode);
576
577/**
512 * xdr_write_pages - Insert a list of pages into an XDR buffer for sending 578 * xdr_write_pages - Insert a list of pages into an XDR buffer for sending
513 * @xdr: pointer to xdr_stream 579 * @xdr: pointer to xdr_stream
514 * @pages: list of pages 580 * @pages: list of pages