diff options
| author | J. Bruce Fields <bfields@redhat.com> | 2014-02-25 17:44:21 -0500 |
|---|---|---|
| committer | J. Bruce Fields <bfields@redhat.com> | 2014-05-30 17:31:47 -0400 |
| commit | 3e19ce762b537dd9aeefdd0849ba5f2f01ff83cf (patch) | |
| tree | 3b261560ccf50dd7efadc2f61a6762566cfdbf45 | |
| parent | 6ac90391c6e36c536cfcedbe4801a77e304205b1 (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.h | 1 | ||||
| -rw-r--r-- | net/sunrpc/xdr.c | 66 |
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 | ||
| 216 | extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); | 216 | extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); |
| 217 | extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); | 217 | extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes); |
| 218 | extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len); | ||
| 218 | extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, | 219 | extern 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); |
| 220 | extern unsigned int xdr_stream_pos(const struct xdr_stream *xdr); | 221 | extern 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) | |||
| 509 | EXPORT_SYMBOL_GPL(xdr_reserve_space); | 509 | EXPORT_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 | */ | ||
| 530 | void 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 | } | ||
| 575 | EXPORT_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 |
