diff options
Diffstat (limited to 'net/sunrpc/xdr.c')
| -rw-r--r-- | net/sunrpc/xdr.c | 155 |
1 files changed, 124 insertions, 31 deletions
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index cd9e841e7492..679cd674b81d 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c | |||
| @@ -552,6 +552,74 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b | |||
| 552 | } | 552 | } |
| 553 | EXPORT_SYMBOL_GPL(xdr_write_pages); | 553 | EXPORT_SYMBOL_GPL(xdr_write_pages); |
| 554 | 554 | ||
| 555 | static void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov, | ||
| 556 | __be32 *p, unsigned int len) | ||
| 557 | { | ||
| 558 | if (len > iov->iov_len) | ||
| 559 | len = iov->iov_len; | ||
| 560 | if (p == NULL) | ||
| 561 | p = (__be32*)iov->iov_base; | ||
| 562 | xdr->p = p; | ||
| 563 | xdr->end = (__be32*)(iov->iov_base + len); | ||
| 564 | xdr->iov = iov; | ||
| 565 | xdr->page_ptr = NULL; | ||
| 566 | } | ||
| 567 | |||
| 568 | static int xdr_set_page_base(struct xdr_stream *xdr, | ||
| 569 | unsigned int base, unsigned int len) | ||
| 570 | { | ||
| 571 | unsigned int pgnr; | ||
| 572 | unsigned int maxlen; | ||
| 573 | unsigned int pgoff; | ||
| 574 | unsigned int pgend; | ||
| 575 | void *kaddr; | ||
| 576 | |||
| 577 | maxlen = xdr->buf->page_len; | ||
| 578 | if (base >= maxlen) | ||
| 579 | return -EINVAL; | ||
| 580 | maxlen -= base; | ||
| 581 | if (len > maxlen) | ||
| 582 | len = maxlen; | ||
| 583 | |||
| 584 | base += xdr->buf->page_base; | ||
| 585 | |||
| 586 | pgnr = base >> PAGE_SHIFT; | ||
| 587 | xdr->page_ptr = &xdr->buf->pages[pgnr]; | ||
| 588 | kaddr = page_address(*xdr->page_ptr); | ||
| 589 | |||
| 590 | pgoff = base & ~PAGE_MASK; | ||
| 591 | xdr->p = (__be32*)(kaddr + pgoff); | ||
| 592 | |||
| 593 | pgend = pgoff + len; | ||
| 594 | if (pgend > PAGE_SIZE) | ||
| 595 | pgend = PAGE_SIZE; | ||
| 596 | xdr->end = (__be32*)(kaddr + pgend); | ||
| 597 | xdr->iov = NULL; | ||
| 598 | return 0; | ||
| 599 | } | ||
| 600 | |||
| 601 | static void xdr_set_next_page(struct xdr_stream *xdr) | ||
| 602 | { | ||
| 603 | unsigned int newbase; | ||
| 604 | |||
| 605 | newbase = (1 + xdr->page_ptr - xdr->buf->pages) << PAGE_SHIFT; | ||
| 606 | newbase -= xdr->buf->page_base; | ||
| 607 | |||
| 608 | if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0) | ||
| 609 | xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len); | ||
| 610 | } | ||
| 611 | |||
| 612 | static bool xdr_set_next_buffer(struct xdr_stream *xdr) | ||
| 613 | { | ||
| 614 | if (xdr->page_ptr != NULL) | ||
| 615 | xdr_set_next_page(xdr); | ||
| 616 | else if (xdr->iov == xdr->buf->head) { | ||
| 617 | if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0) | ||
| 618 | xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len); | ||
| 619 | } | ||
| 620 | return xdr->p != xdr->end; | ||
| 621 | } | ||
| 622 | |||
| 555 | /** | 623 | /** |
| 556 | * xdr_init_decode - Initialize an xdr_stream for decoding data. | 624 | * xdr_init_decode - Initialize an xdr_stream for decoding data. |
| 557 | * @xdr: pointer to xdr_stream struct | 625 | * @xdr: pointer to xdr_stream struct |
| @@ -560,41 +628,67 @@ EXPORT_SYMBOL_GPL(xdr_write_pages); | |||
| 560 | */ | 628 | */ |
| 561 | void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) | 629 | void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) |
| 562 | { | 630 | { |
| 563 | struct kvec *iov = buf->head; | ||
| 564 | unsigned int len = iov->iov_len; | ||
| 565 | |||
| 566 | if (len > buf->len) | ||
| 567 | len = buf->len; | ||
| 568 | xdr->buf = buf; | 631 | xdr->buf = buf; |
| 569 | xdr->iov = iov; | 632 | xdr->scratch.iov_base = NULL; |
| 570 | xdr->p = p; | 633 | xdr->scratch.iov_len = 0; |
| 571 | xdr->end = (__be32 *)((char *)iov->iov_base + len); | 634 | if (buf->head[0].iov_len != 0) |
| 635 | xdr_set_iov(xdr, buf->head, p, buf->len); | ||
| 636 | else if (buf->page_len != 0) | ||
| 637 | xdr_set_page_base(xdr, 0, buf->len); | ||
| 572 | } | 638 | } |
| 573 | EXPORT_SYMBOL_GPL(xdr_init_decode); | 639 | EXPORT_SYMBOL_GPL(xdr_init_decode); |
| 574 | 640 | ||
| 575 | /** | 641 | static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) |
| 576 | * xdr_inline_peek - Allow read-ahead in the XDR data stream | ||
| 577 | * @xdr: pointer to xdr_stream struct | ||
| 578 | * @nbytes: number of bytes of data to decode | ||
| 579 | * | ||
| 580 | * Check if the input buffer is long enough to enable us to decode | ||
| 581 | * 'nbytes' more bytes of data starting at the current position. | ||
| 582 | * If so return the current pointer without updating the current | ||
| 583 | * pointer position. | ||
| 584 | */ | ||
| 585 | __be32 * xdr_inline_peek(struct xdr_stream *xdr, size_t nbytes) | ||
| 586 | { | 642 | { |
| 587 | __be32 *p = xdr->p; | 643 | __be32 *p = xdr->p; |
| 588 | __be32 *q = p + XDR_QUADLEN(nbytes); | 644 | __be32 *q = p + XDR_QUADLEN(nbytes); |
| 589 | 645 | ||
| 590 | if (unlikely(q > xdr->end || q < p)) | 646 | if (unlikely(q > xdr->end || q < p)) |
| 591 | return NULL; | 647 | return NULL; |
| 648 | xdr->p = q; | ||
| 592 | return p; | 649 | return p; |
| 593 | } | 650 | } |
| 594 | EXPORT_SYMBOL_GPL(xdr_inline_peek); | ||
| 595 | 651 | ||
| 596 | /** | 652 | /** |
| 597 | * xdr_inline_decode - Retrieve non-page XDR data to decode | 653 | * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data. |
| 654 | * @xdr: pointer to xdr_stream struct | ||
| 655 | * @buf: pointer to an empty buffer | ||
| 656 | * @buflen: size of 'buf' | ||
| 657 | * | ||
| 658 | * The scratch buffer is used when decoding from an array of pages. | ||
| 659 | * If an xdr_inline_decode() call spans across page boundaries, then | ||
| 660 | * we copy the data into the scratch buffer in order to allow linear | ||
| 661 | * access. | ||
| 662 | */ | ||
| 663 | void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen) | ||
| 664 | { | ||
| 665 | xdr->scratch.iov_base = buf; | ||
| 666 | xdr->scratch.iov_len = buflen; | ||
| 667 | } | ||
| 668 | EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer); | ||
| 669 | |||
| 670 | static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes) | ||
| 671 | { | ||
| 672 | __be32 *p; | ||
| 673 | void *cpdest = xdr->scratch.iov_base; | ||
| 674 | size_t cplen = (char *)xdr->end - (char *)xdr->p; | ||
| 675 | |||
| 676 | if (nbytes > xdr->scratch.iov_len) | ||
| 677 | return NULL; | ||
| 678 | memcpy(cpdest, xdr->p, cplen); | ||
| 679 | cpdest += cplen; | ||
| 680 | nbytes -= cplen; | ||
| 681 | if (!xdr_set_next_buffer(xdr)) | ||
| 682 | return NULL; | ||
| 683 | p = __xdr_inline_decode(xdr, nbytes); | ||
| 684 | if (p == NULL) | ||
| 685 | return NULL; | ||
| 686 | memcpy(cpdest, p, nbytes); | ||
| 687 | return xdr->scratch.iov_base; | ||
| 688 | } | ||
| 689 | |||
| 690 | /** | ||
| 691 | * xdr_inline_decode - Retrieve XDR data to decode | ||
| 598 | * @xdr: pointer to xdr_stream struct | 692 | * @xdr: pointer to xdr_stream struct |
| 599 | * @nbytes: number of bytes of data to decode | 693 | * @nbytes: number of bytes of data to decode |
| 600 | * | 694 | * |
| @@ -605,13 +699,16 @@ EXPORT_SYMBOL_GPL(xdr_inline_peek); | |||
| 605 | */ | 699 | */ |
| 606 | __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) | 700 | __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) |
| 607 | { | 701 | { |
| 608 | __be32 *p = xdr->p; | 702 | __be32 *p; |
| 609 | __be32 *q = p + XDR_QUADLEN(nbytes); | ||
| 610 | 703 | ||
| 611 | if (unlikely(q > xdr->end || q < p)) | 704 | if (nbytes == 0) |
| 705 | return xdr->p; | ||
| 706 | if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr)) | ||
| 612 | return NULL; | 707 | return NULL; |
| 613 | xdr->p = q; | 708 | p = __xdr_inline_decode(xdr, nbytes); |
| 614 | return p; | 709 | if (p != NULL) |
| 710 | return p; | ||
| 711 | return xdr_copy_to_scratch(xdr, nbytes); | ||
| 615 | } | 712 | } |
| 616 | EXPORT_SYMBOL_GPL(xdr_inline_decode); | 713 | EXPORT_SYMBOL_GPL(xdr_inline_decode); |
| 617 | 714 | ||
| @@ -671,16 +768,12 @@ EXPORT_SYMBOL_GPL(xdr_read_pages); | |||
| 671 | */ | 768 | */ |
| 672 | void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) | 769 | void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) |
| 673 | { | 770 | { |
| 674 | char * kaddr = page_address(xdr->buf->pages[0]); | ||
| 675 | xdr_read_pages(xdr, len); | 771 | xdr_read_pages(xdr, len); |
| 676 | /* | 772 | /* |
| 677 | * Position current pointer at beginning of tail, and | 773 | * Position current pointer at beginning of tail, and |
| 678 | * set remaining message length. | 774 | * set remaining message length. |
| 679 | */ | 775 | */ |
| 680 | if (len > PAGE_CACHE_SIZE - xdr->buf->page_base) | 776 | xdr_set_page_base(xdr, 0, len); |
| 681 | len = PAGE_CACHE_SIZE - xdr->buf->page_base; | ||
| 682 | xdr->p = (__be32 *)(kaddr + xdr->buf->page_base); | ||
| 683 | xdr->end = (__be32 *)((char *)xdr->p + len); | ||
| 684 | } | 777 | } |
| 685 | EXPORT_SYMBOL_GPL(xdr_enter_page); | 778 | EXPORT_SYMBOL_GPL(xdr_enter_page); |
| 686 | 779 | ||
