diff options
Diffstat (limited to 'net/sunrpc/xdr.c')
-rw-r--r-- | net/sunrpc/xdr.c | 211 |
1 files changed, 183 insertions, 28 deletions
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index a1f82a87d34d..f008c14ad34c 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c | |||
@@ -111,6 +111,23 @@ xdr_decode_string_inplace(__be32 *p, char **sp, | |||
111 | } | 111 | } |
112 | EXPORT_SYMBOL_GPL(xdr_decode_string_inplace); | 112 | EXPORT_SYMBOL_GPL(xdr_decode_string_inplace); |
113 | 113 | ||
114 | /** | ||
115 | * xdr_terminate_string - '\0'-terminate a string residing in an xdr_buf | ||
116 | * @buf: XDR buffer where string resides | ||
117 | * @len: length of string, in bytes | ||
118 | * | ||
119 | */ | ||
120 | void | ||
121 | xdr_terminate_string(struct xdr_buf *buf, const u32 len) | ||
122 | { | ||
123 | char *kaddr; | ||
124 | |||
125 | kaddr = kmap_atomic(buf->pages[0], KM_USER0); | ||
126 | kaddr[buf->page_base + len] = '\0'; | ||
127 | kunmap_atomic(kaddr, KM_USER0); | ||
128 | } | ||
129 | EXPORT_SYMBOL(xdr_terminate_string); | ||
130 | |||
114 | void | 131 | void |
115 | xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, | 132 | xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, |
116 | unsigned int len) | 133 | unsigned int len) |
@@ -395,24 +412,29 @@ xdr_shrink_pagelen(struct xdr_buf *buf, size_t len) | |||
395 | { | 412 | { |
396 | struct kvec *tail; | 413 | struct kvec *tail; |
397 | size_t copy; | 414 | size_t copy; |
398 | char *p; | ||
399 | unsigned int pglen = buf->page_len; | 415 | unsigned int pglen = buf->page_len; |
416 | unsigned int tailbuf_len; | ||
400 | 417 | ||
401 | tail = buf->tail; | 418 | tail = buf->tail; |
402 | BUG_ON (len > pglen); | 419 | BUG_ON (len > pglen); |
403 | 420 | ||
421 | tailbuf_len = buf->buflen - buf->head->iov_len - buf->page_len; | ||
422 | |||
404 | /* Shift the tail first */ | 423 | /* Shift the tail first */ |
405 | if (tail->iov_len != 0) { | 424 | if (tailbuf_len != 0) { |
406 | p = (char *)tail->iov_base + len; | 425 | unsigned int free_space = tailbuf_len - tail->iov_len; |
426 | |||
427 | if (len < free_space) | ||
428 | free_space = len; | ||
429 | tail->iov_len += free_space; | ||
430 | |||
431 | copy = len; | ||
407 | if (tail->iov_len > len) { | 432 | if (tail->iov_len > len) { |
408 | copy = tail->iov_len - len; | 433 | char *p = (char *)tail->iov_base + len; |
409 | memmove(p, tail->iov_base, copy); | 434 | memmove(p, tail->iov_base, tail->iov_len - len); |
410 | } else | 435 | } else |
411 | buf->buflen -= len; | ||
412 | /* Copy from the inlined pages into the tail */ | ||
413 | copy = len; | ||
414 | if (copy > tail->iov_len) | ||
415 | copy = tail->iov_len; | 436 | copy = tail->iov_len; |
437 | /* Copy from the inlined pages into the tail */ | ||
416 | _copy_from_pages((char *)tail->iov_base, | 438 | _copy_from_pages((char *)tail->iov_base, |
417 | buf->pages, buf->page_base + pglen - len, | 439 | buf->pages, buf->page_base + pglen - len, |
418 | copy); | 440 | copy); |
@@ -530,6 +552,74 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b | |||
530 | } | 552 | } |
531 | EXPORT_SYMBOL_GPL(xdr_write_pages); | 553 | EXPORT_SYMBOL_GPL(xdr_write_pages); |
532 | 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 | |||
533 | /** | 623 | /** |
534 | * xdr_init_decode - Initialize an xdr_stream for decoding data. | 624 | * xdr_init_decode - Initialize an xdr_stream for decoding data. |
535 | * @xdr: pointer to xdr_stream struct | 625 | * @xdr: pointer to xdr_stream struct |
@@ -538,20 +628,86 @@ EXPORT_SYMBOL_GPL(xdr_write_pages); | |||
538 | */ | 628 | */ |
539 | 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) |
540 | { | 630 | { |
541 | struct kvec *iov = buf->head; | ||
542 | unsigned int len = iov->iov_len; | ||
543 | |||
544 | if (len > buf->len) | ||
545 | len = buf->len; | ||
546 | xdr->buf = buf; | 631 | xdr->buf = buf; |
547 | xdr->iov = iov; | 632 | xdr->scratch.iov_base = NULL; |
548 | xdr->p = p; | 633 | xdr->scratch.iov_len = 0; |
549 | 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); | ||
550 | } | 638 | } |
551 | EXPORT_SYMBOL_GPL(xdr_init_decode); | 639 | EXPORT_SYMBOL_GPL(xdr_init_decode); |
552 | 640 | ||
553 | /** | 641 | /** |
554 | * xdr_inline_decode - Retrieve non-page XDR data to decode | 642 | * xdr_init_decode - Initialize an xdr_stream for decoding data. |
643 | * @xdr: pointer to xdr_stream struct | ||
644 | * @buf: pointer to XDR buffer from which to decode data | ||
645 | * @pages: list of pages to decode into | ||
646 | * @len: length in bytes of buffer in pages | ||
647 | */ | ||
648 | void xdr_init_decode_pages(struct xdr_stream *xdr, struct xdr_buf *buf, | ||
649 | struct page **pages, unsigned int len) | ||
650 | { | ||
651 | memset(buf, 0, sizeof(*buf)); | ||
652 | buf->pages = pages; | ||
653 | buf->page_len = len; | ||
654 | buf->buflen = len; | ||
655 | buf->len = len; | ||
656 | xdr_init_decode(xdr, buf, NULL); | ||
657 | } | ||
658 | EXPORT_SYMBOL_GPL(xdr_init_decode_pages); | ||
659 | |||
660 | static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) | ||
661 | { | ||
662 | __be32 *p = xdr->p; | ||
663 | __be32 *q = p + XDR_QUADLEN(nbytes); | ||
664 | |||
665 | if (unlikely(q > xdr->end || q < p)) | ||
666 | return NULL; | ||
667 | xdr->p = q; | ||
668 | return p; | ||
669 | } | ||
670 | |||
671 | /** | ||
672 | * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data. | ||
673 | * @xdr: pointer to xdr_stream struct | ||
674 | * @buf: pointer to an empty buffer | ||
675 | * @buflen: size of 'buf' | ||
676 | * | ||
677 | * The scratch buffer is used when decoding from an array of pages. | ||
678 | * If an xdr_inline_decode() call spans across page boundaries, then | ||
679 | * we copy the data into the scratch buffer in order to allow linear | ||
680 | * access. | ||
681 | */ | ||
682 | void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen) | ||
683 | { | ||
684 | xdr->scratch.iov_base = buf; | ||
685 | xdr->scratch.iov_len = buflen; | ||
686 | } | ||
687 | EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer); | ||
688 | |||
689 | static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes) | ||
690 | { | ||
691 | __be32 *p; | ||
692 | void *cpdest = xdr->scratch.iov_base; | ||
693 | size_t cplen = (char *)xdr->end - (char *)xdr->p; | ||
694 | |||
695 | if (nbytes > xdr->scratch.iov_len) | ||
696 | return NULL; | ||
697 | memcpy(cpdest, xdr->p, cplen); | ||
698 | cpdest += cplen; | ||
699 | nbytes -= cplen; | ||
700 | if (!xdr_set_next_buffer(xdr)) | ||
701 | return NULL; | ||
702 | p = __xdr_inline_decode(xdr, nbytes); | ||
703 | if (p == NULL) | ||
704 | return NULL; | ||
705 | memcpy(cpdest, p, nbytes); | ||
706 | return xdr->scratch.iov_base; | ||
707 | } | ||
708 | |||
709 | /** | ||
710 | * xdr_inline_decode - Retrieve XDR data to decode | ||
555 | * @xdr: pointer to xdr_stream struct | 711 | * @xdr: pointer to xdr_stream struct |
556 | * @nbytes: number of bytes of data to decode | 712 | * @nbytes: number of bytes of data to decode |
557 | * | 713 | * |
@@ -562,13 +718,16 @@ EXPORT_SYMBOL_GPL(xdr_init_decode); | |||
562 | */ | 718 | */ |
563 | __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) | 719 | __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes) |
564 | { | 720 | { |
565 | __be32 *p = xdr->p; | 721 | __be32 *p; |
566 | __be32 *q = p + XDR_QUADLEN(nbytes); | ||
567 | 722 | ||
568 | if (unlikely(q > xdr->end || q < p)) | 723 | if (nbytes == 0) |
724 | return xdr->p; | ||
725 | if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr)) | ||
569 | return NULL; | 726 | return NULL; |
570 | xdr->p = q; | 727 | p = __xdr_inline_decode(xdr, nbytes); |
571 | return p; | 728 | if (p != NULL) |
729 | return p; | ||
730 | return xdr_copy_to_scratch(xdr, nbytes); | ||
572 | } | 731 | } |
573 | EXPORT_SYMBOL_GPL(xdr_inline_decode); | 732 | EXPORT_SYMBOL_GPL(xdr_inline_decode); |
574 | 733 | ||
@@ -628,16 +787,12 @@ EXPORT_SYMBOL_GPL(xdr_read_pages); | |||
628 | */ | 787 | */ |
629 | void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) | 788 | void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) |
630 | { | 789 | { |
631 | char * kaddr = page_address(xdr->buf->pages[0]); | ||
632 | xdr_read_pages(xdr, len); | 790 | xdr_read_pages(xdr, len); |
633 | /* | 791 | /* |
634 | * Position current pointer at beginning of tail, and | 792 | * Position current pointer at beginning of tail, and |
635 | * set remaining message length. | 793 | * set remaining message length. |
636 | */ | 794 | */ |
637 | if (len > PAGE_CACHE_SIZE - xdr->buf->page_base) | 795 | xdr_set_page_base(xdr, 0, len); |
638 | len = PAGE_CACHE_SIZE - xdr->buf->page_base; | ||
639 | xdr->p = (__be32 *)(kaddr + xdr->buf->page_base); | ||
640 | xdr->end = (__be32 *)((char *)xdr->p + len); | ||
641 | } | 796 | } |
642 | EXPORT_SYMBOL_GPL(xdr_enter_page); | 797 | EXPORT_SYMBOL_GPL(xdr_enter_page); |
643 | 798 | ||