diff options
-rw-r--r-- | fs/nfs/dir.c | 44 | ||||
-rw-r--r-- | fs/nfs/nfs2xdr.c | 6 | ||||
-rw-r--r-- | fs/nfs/nfs3xdr.c | 6 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 6 | ||||
-rw-r--r-- | include/linux/sunrpc/xdr.h | 4 | ||||
-rw-r--r-- | net/sunrpc/xdr.c | 155 |
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 */ |
460 | static | 459 | static |
461 | int nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, | 460 | int 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 | |||
521 | void nfs_readdir_free_large_page(void *ptr, struct page **pages, | 523 | void 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 | */ |
532 | static | 533 | static |
533 | void *nfs_readdir_large_page(struct page **pages, unsigned int npages) | 534 | int 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; | ||
548 | out_freepages: | 546 | out_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 | ||
553 | static | 551 | static |
@@ -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 | ||
498 | out_overflow: | 492 | out_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 | ||
658 | out_overflow: | 652 | out_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 | ||
6226 | out_overflow: | 6220 | out_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 | ||
206 | extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); | 208 | extern 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); | |||
208 | extern void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, | 210 | extern 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); |
210 | extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); | 212 | extern void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p); |
211 | extern __be32 *xdr_inline_peek(struct xdr_stream *xdr, size_t nbytes); | 213 | extern void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen); |
212 | extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); | 214 | extern __be32 *xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes); |
213 | extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len); | 215 | extern void xdr_read_pages(struct xdr_stream *xdr, unsigned int len); |
214 | extern void xdr_enter_page(struct xdr_stream *xdr, unsigned int len); | 216 | extern 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 | } |
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 | ||