aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/dir.c44
-rw-r--r--fs/nfs/nfs2xdr.c6
-rw-r--r--fs/nfs/nfs3xdr.c6
-rw-r--r--fs/nfs/nfs4xdr.c6
-rw-r--r--include/linux/sunrpc/xdr.h4
-rw-r--r--net/sunrpc/xdr.c155
6 files changed, 148 insertions, 73 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 996dd8989a91..0108cf4f3403 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -33,7 +33,6 @@
33#include <linux/namei.h> 33#include <linux/namei.h>
34#include <linux/mount.h> 34#include <linux/mount.h>
35#include <linux/sched.h> 35#include <linux/sched.h>
36#include <linux/vmalloc.h>
37#include <linux/kmemleak.h> 36#include <linux/kmemleak.h>
38 37
39#include "delegation.h" 38#include "delegation.h"
@@ -459,25 +458,26 @@ out:
459/* Perform conversion from xdr to cache array */ 458/* Perform conversion from xdr to cache array */
460static 459static
461int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, 460int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry,
462 void *xdr_page, struct page *page, unsigned int buflen) 461 struct page **xdr_pages, struct page *page, unsigned int buflen)
463{ 462{
464 struct xdr_stream stream; 463 struct xdr_stream stream;
465 struct xdr_buf buf; 464 struct xdr_buf buf = {
466 __be32 *ptr = xdr_page; 465 .pages = xdr_pages,
466 .page_len = buflen,
467 .buflen = buflen,
468 .len = buflen,
469 };
470 struct page *scratch;
467 struct nfs_cache_array *array; 471 struct nfs_cache_array *array;
468 unsigned int count = 0; 472 unsigned int count = 0;
469 int status; 473 int status;
470 474
471 buf.head->iov_base = xdr_page; 475 scratch = alloc_page(GFP_KERNEL);
472 buf.head->iov_len = buflen; 476 if (scratch == NULL)
473 buf.tail->iov_len = 0; 477 return -ENOMEM;
474 buf.page_base = 0;
475 buf.page_len = 0;
476 buf.buflen = buf.head->iov_len;
477 buf.len = buf.head->iov_len;
478
479 xdr_init_decode(&stream, &buf, ptr);
480 478
479 xdr_init_decode(&stream, &buf, NULL);
480 xdr_set_scratch_buffer(&stream, page_address(scratch), PAGE_SIZE);
481 481
482 do { 482 do {
483 status = xdr_decode(desc, entry, &stream); 483 status = xdr_decode(desc, entry, &stream);
@@ -506,6 +506,8 @@ int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *en
506 } else 506 } else
507 status = PTR_ERR(array); 507 status = PTR_ERR(array);
508 } 508 }
509
510 put_page(scratch);
509 return status; 511 return status;
510} 512}
511 513
@@ -521,7 +523,6 @@ static
521void nfs_readdir_free_large_page(void *ptr, struct page **pages, 523void nfs_readdir_free_large_page(void *ptr, struct page **pages,
522 unsigned int npages) 524 unsigned int npages)
523{ 525{
524 vm_unmap_ram(ptr, npages);
525 nfs_readdir_free_pagearray(pages, npages); 526 nfs_readdir_free_pagearray(pages, npages);
526} 527}
527 528
@@ -530,9 +531,8 @@ void nfs_readdir_free_large_page(void *ptr, struct page **pages,
530 * to nfs_readdir_free_large_page 531 * to nfs_readdir_free_large_page
531 */ 532 */
532static 533static
533void *nfs_readdir_large_page(struct page **pages, unsigned int npages) 534int nfs_readdir_large_page(struct page **pages, unsigned int npages)
534{ 535{
535 void *ptr;
536 unsigned int i; 536 unsigned int i;
537 537
538 for (i = 0; i < npages; i++) { 538 for (i = 0; i < npages; i++) {
@@ -541,13 +541,11 @@ void *nfs_readdir_large_page(struct page **pages, unsigned int npages)
541 goto out_freepages; 541 goto out_freepages;
542 pages[i] = page; 542 pages[i] = page;
543 } 543 }
544 return 0;
544 545
545 ptr = vm_map_ram(pages, npages, 0, PAGE_KERNEL);
546 if (!IS_ERR_OR_NULL(ptr))
547 return ptr;
548out_freepages: 546out_freepages:
549 nfs_readdir_free_pagearray(pages, i); 547 nfs_readdir_free_pagearray(pages, i);
550 return NULL; 548 return -ENOMEM;
551} 549}
552 550
553static 551static
@@ -577,8 +575,8 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
577 memset(array, 0, sizeof(struct nfs_cache_array)); 575 memset(array, 0, sizeof(struct nfs_cache_array));
578 array->eof_index = -1; 576 array->eof_index = -1;
579 577
580 pages_ptr = nfs_readdir_large_page(pages, array_size); 578 status = nfs_readdir_large_page(pages, array_size);
581 if (!pages_ptr) 579 if (status < 0)
582 goto out_release_array; 580 goto out_release_array;
583 do { 581 do {
584 unsigned int pglen; 582 unsigned int pglen;
@@ -587,7 +585,7 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
587 if (status < 0) 585 if (status < 0)
588 break; 586 break;
589 pglen = status; 587 pglen = status;
590 status = nfs_readdir_page_filler(desc, &entry, pages_ptr, page, pglen); 588 status = nfs_readdir_page_filler(desc, &entry, pages, page, pglen);
591 if (status < 0) { 589 if (status < 0) {
592 if (status == -ENOSPC) 590 if (status == -ENOSPC)
593 status = 0; 591 status = 0;
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
index 5914a1911c95..b382a1b5e7e4 100644
--- a/fs/nfs/nfs2xdr.c
+++ b/fs/nfs/nfs2xdr.c
@@ -487,12 +487,6 @@ nfs_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_se
487 487
488 entry->d_type = DT_UNKNOWN; 488 entry->d_type = DT_UNKNOWN;
489 489
490 p = xdr_inline_peek(xdr, 8);
491 if (p != NULL)
492 entry->eof = !p[0] && p[1];
493 else
494 entry->eof = 0;
495
496 return p; 490 return p;
497 491
498out_overflow: 492out_overflow:
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
index f6cc60f06dac..ba91236c6ee7 100644
--- a/fs/nfs/nfs3xdr.c
+++ b/fs/nfs/nfs3xdr.c
@@ -647,12 +647,6 @@ nfs3_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry, struct nfs_s
647 memset((u8*)(entry->fh), 0, sizeof(*entry->fh)); 647 memset((u8*)(entry->fh), 0, sizeof(*entry->fh));
648 } 648 }
649 649
650 p = xdr_inline_peek(xdr, 8);
651 if (p != NULL)
652 entry->eof = !p[0] && p[1];
653 else
654 entry->eof = 0;
655
656 return p; 650 return p;
657 651
658out_overflow: 652out_overflow:
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 9f1826b012e6..0662a9821df5 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -6215,12 +6215,6 @@ __be32 *nfs4_decode_dirent(struct xdr_stream *xdr, struct nfs_entry *entry,
6215 if (verify_attr_len(xdr, p, len) < 0) 6215 if (verify_attr_len(xdr, p, len) < 0)
6216 goto out_overflow; 6216 goto out_overflow;
6217 6217
6218 p = xdr_inline_peek(xdr, 8);
6219 if (p != NULL)
6220 entry->eof = !p[0] && p[1];
6221 else
6222 entry->eof = 0;
6223
6224 return p; 6218 return p;
6225 6219
6226out_overflow: 6220out_overflow:
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 498ab93a81e4..7783c687c777 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -201,6 +201,8 @@ struct xdr_stream {
201 201
202 __be32 *end; /* end of available buffer space */ 202 __be32 *end; /* end of available buffer space */
203 struct kvec *iov; /* pointer to the current kvec */ 203 struct kvec *iov; /* pointer to the current kvec */
204 struct kvec scratch; /* Scratch buffer */
205 struct page **page_ptr; /* pointer to the current page */
204}; 206};
205 207
206extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); 208extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
@@ -208,7 +210,7 @@ extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
208extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, 210extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages,
209 unsigned int base, unsigned int len); 211 unsigned int base, unsigned int len);
210extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); 212extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
211extern __be32 *xdr_inline_peek(struct xdr_stream *xdr, size_t nbytes); 213extern void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen);
212extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); 214extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes);
213extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len); 215extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len);
214extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); 216extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len);
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}
553EXPORT_SYMBOL_GPL(xdr_write_pages); 553EXPORT_SYMBOL_GPL(xdr_write_pages);
554 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
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 */
561void 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)
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}
573EXPORT_SYMBOL_GPL(xdr_init_decode); 639EXPORT_SYMBOL_GPL(xdr_init_decode);
574 640
575/** 641static __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}
594EXPORT_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 */
663void 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}
668EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer);
669
670static __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}
616EXPORT_SYMBOL_GPL(xdr_inline_decode); 713EXPORT_SYMBOL_GPL(xdr_inline_decode);
617 714
@@ -671,16 +768,12 @@ EXPORT_SYMBOL_GPL(xdr_read_pages);
671 */ 768 */
672void xdr_enter_page(struct xdr_stream *xdr, unsigned int len) 769void 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}
685EXPORT_SYMBOL_GPL(xdr_enter_page); 778EXPORT_SYMBOL_GPL(xdr_enter_page);
686 779