aboutsummaryrefslogtreecommitdiffstats
path: root/net/sunrpc/xdr.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/sunrpc/xdr.c')
-rw-r--r--net/sunrpc/xdr.c211
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}
112EXPORT_SYMBOL_GPL(xdr_decode_string_inplace); 112EXPORT_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 */
120void
121xdr_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}
129EXPORT_SYMBOL(xdr_terminate_string);
130
114void 131void
115xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base, 132xdr_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}
531EXPORT_SYMBOL_GPL(xdr_write_pages); 553EXPORT_SYMBOL_GPL(xdr_write_pages);
532 554
555static 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
568static 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
601static 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
612static 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 */
539void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p) 629void 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}
551EXPORT_SYMBOL_GPL(xdr_init_decode); 639EXPORT_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 */
648void 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}
658EXPORT_SYMBOL_GPL(xdr_init_decode_pages);
659
660static __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 */
682void 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}
687EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer);
688
689static __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}
573EXPORT_SYMBOL_GPL(xdr_inline_decode); 732EXPORT_SYMBOL_GPL(xdr_inline_decode);
574 733
@@ -628,16 +787,12 @@ EXPORT_SYMBOL_GPL(xdr_read_pages);
628 */ 787 */
629void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) 788void 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}
642EXPORT_SYMBOL_GPL(xdr_enter_page); 797EXPORT_SYMBOL_GPL(xdr_enter_page);
643 798