diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-01-08 17:45:38 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-01-10 14:45:01 -0500 |
commit | 6650239a4b01077e80d5a4468562756d77afaa59 (patch) | |
tree | 505c9a1dc26c26608ce6a5981093a6357d4f3638 /fs/nfs/dir.c | |
parent | 3c0eee3fe6a3a1c745379547c7e7c904aa64f6d5 (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]
Diffstat (limited to 'fs/nfs/dir.c')
-rw-r--r-- | fs/nfs/dir.c | 44 |
1 files changed, 21 insertions, 23 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; |