aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2011-01-08 17:45:38 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2011-01-10 14:45:01 -0500
commit6650239a4b01077e80d5a4468562756d77afaa59 (patch)
tree505c9a1dc26c26608ce6a5981093a6357d4f3638
parent3c0eee3fe6a3a1c745379547c7e7c904aa64f6d5 (diff)
NFS: Don't use vm_map_ram() in readdir
vm_map_ram() is not available on NOMMU platforms, and causes trouble on incoherrent architectures such as ARM when we access the page data through both the direct and the virtual mapping. The alternative is to use the direct mapping to access page data for the case when we are not crossing a page boundary, but to copy the data into a linear scratch buffer when we are accessing data that spans page boundaries. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Tested-by: Marc Kleine-Budde <mkl@pengutronix.de> Cc: stable@kernel.org [2.6.37]
-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