diff options
author | Bryan Schumaker <bjschuma@netapp.com> | 2010-10-20 15:44:37 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-10-23 15:27:35 -0400 |
commit | 56e4ebf877b6043c289bda32a5a7385b80c17dee (patch) | |
tree | 160ae8d5b5ee3871d02a9f5283187430c9ec5ffe /fs | |
parent | afa8ccc978c24d8ab22e3b3b8cbd1054c84c070b (diff) |
NFS: readdir with vmapped pages
We can use vmapped pages to read more information from the network at once.
This will reduce the number of calls needed to complete a readdir.
Signed-off-by: Bryan Schumaker <bjschuma@netapp.com>
[trondmy: Added #include for linux/vmalloc.h> in fs/nfs/dir.c]
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/client.c | 4 | ||||
-rw-r--r-- | fs/nfs/dir.c | 66 | ||||
-rw-r--r-- | fs/nfs/internal.h | 6 | ||||
-rw-r--r-- | fs/nfs/nfs3proc.c | 4 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 8 | ||||
-rw-r--r-- | fs/nfs/proc.c | 4 |
6 files changed, 70 insertions, 22 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 5f01f42b3991..876ba8592706 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c | |||
@@ -903,8 +903,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo * | |||
903 | server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); | 903 | server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL); |
904 | 904 | ||
905 | server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); | 905 | server->dtsize = nfs_block_size(fsinfo->dtpref, NULL); |
906 | if (server->dtsize > PAGE_CACHE_SIZE) | 906 | if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES) |
907 | server->dtsize = PAGE_CACHE_SIZE; | 907 | server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES; |
908 | if (server->dtsize > server->rsize) | 908 | if (server->dtsize > server->rsize) |
909 | server->dtsize = server->rsize; | 909 | server->dtsize = server->rsize; |
910 | 910 | ||
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 88cbcda76856..5d7da3ad4e85 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -33,6 +33,7 @@ | |||
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> | ||
36 | 37 | ||
37 | #include "delegation.h" | 38 | #include "delegation.h" |
38 | #include "iostat.h" | 39 | #include "iostat.h" |
@@ -327,7 +328,7 @@ out: | |||
327 | 328 | ||
328 | /* Fill a page with xdr information before transferring to the cache page */ | 329 | /* Fill a page with xdr information before transferring to the cache page */ |
329 | static | 330 | static |
330 | int nfs_readdir_xdr_filler(struct page *xdr_page, nfs_readdir_descriptor_t *desc, | 331 | int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc, |
331 | struct nfs_entry *entry, struct file *file, struct inode *inode) | 332 | struct nfs_entry *entry, struct file *file, struct inode *inode) |
332 | { | 333 | { |
333 | struct rpc_cred *cred = nfs_file_cred(file); | 334 | struct rpc_cred *cred = nfs_file_cred(file); |
@@ -337,7 +338,7 @@ int nfs_readdir_xdr_filler(struct page *xdr_page, nfs_readdir_descriptor_t *desc | |||
337 | again: | 338 | again: |
338 | timestamp = jiffies; | 339 | timestamp = jiffies; |
339 | gencount = nfs_inc_attr_generation_counter(); | 340 | gencount = nfs_inc_attr_generation_counter(); |
340 | error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, xdr_page, | 341 | error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, pages, |
341 | NFS_SERVER(inode)->dtsize, desc->plus); | 342 | NFS_SERVER(inode)->dtsize, desc->plus); |
342 | if (error < 0) { | 343 | if (error < 0) { |
343 | /* We requested READDIRPLUS, but the server doesn't grok it */ | 344 | /* We requested READDIRPLUS, but the server doesn't grok it */ |
@@ -436,11 +437,11 @@ out: | |||
436 | /* Perform conversion from xdr to cache array */ | 437 | /* Perform conversion from xdr to cache array */ |
437 | static | 438 | static |
438 | void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, | 439 | void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry, |
439 | struct page *xdr_page, struct page *page, unsigned int buflen) | 440 | void *xdr_page, struct page *page, unsigned int buflen) |
440 | { | 441 | { |
441 | struct xdr_stream stream; | 442 | struct xdr_stream stream; |
442 | struct xdr_buf buf; | 443 | struct xdr_buf buf; |
443 | __be32 *ptr = kmap(xdr_page); | 444 | __be32 *ptr = xdr_page; |
444 | 445 | ||
445 | buf.head->iov_base = xdr_page; | 446 | buf.head->iov_base = xdr_page; |
446 | buf.head->iov_len = buflen; | 447 | buf.head->iov_len = buflen; |
@@ -458,18 +459,59 @@ void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *e | |||
458 | if (desc->plus == 1) | 459 | if (desc->plus == 1) |
459 | nfs_prime_dcache(desc->file->f_path.dentry, entry); | 460 | nfs_prime_dcache(desc->file->f_path.dentry, entry); |
460 | } | 461 | } |
461 | kunmap(xdr_page); | 462 | } |
463 | |||
464 | static | ||
465 | void nfs_readdir_free_pagearray(struct page **pages, unsigned int npages) | ||
466 | { | ||
467 | unsigned int i; | ||
468 | for (i = 0; i < npages; i++) | ||
469 | put_page(pages[i]); | ||
470 | } | ||
471 | |||
472 | static | ||
473 | void nfs_readdir_free_large_page(void *ptr, struct page **pages, | ||
474 | unsigned int npages) | ||
475 | { | ||
476 | vm_unmap_ram(ptr, npages); | ||
477 | nfs_readdir_free_pagearray(pages, npages); | ||
478 | } | ||
479 | |||
480 | /* | ||
481 | * nfs_readdir_large_page will allocate pages that must be freed with a call | ||
482 | * to nfs_readdir_free_large_page | ||
483 | */ | ||
484 | static | ||
485 | void *nfs_readdir_large_page(struct page **pages, unsigned int npages) | ||
486 | { | ||
487 | void *ptr; | ||
488 | unsigned int i; | ||
489 | |||
490 | for (i = 0; i < npages; i++) { | ||
491 | struct page *page = alloc_page(GFP_KERNEL); | ||
492 | if (page == NULL) | ||
493 | goto out_freepages; | ||
494 | pages[i] = page; | ||
495 | } | ||
496 | |||
497 | ptr = vm_map_ram(pages, NFS_MAX_READDIR_PAGES, 0, PAGE_KERNEL); | ||
498 | if (!IS_ERR_OR_NULL(ptr)) | ||
499 | return ptr; | ||
500 | out_freepages: | ||
501 | nfs_readdir_free_pagearray(pages, i); | ||
502 | return NULL; | ||
462 | } | 503 | } |
463 | 504 | ||
464 | static | 505 | static |
465 | int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct inode *inode) | 506 | int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct inode *inode) |
466 | { | 507 | { |
467 | struct page *xdr_page; | 508 | struct page *pages[NFS_MAX_READDIR_PAGES]; |
509 | void *pages_ptr = NULL; | ||
468 | struct nfs_entry entry; | 510 | struct nfs_entry entry; |
469 | struct file *file = desc->file; | 511 | struct file *file = desc->file; |
470 | struct nfs_cache_array *array; | 512 | struct nfs_cache_array *array; |
471 | int status = 0; | 513 | int status = 0; |
472 | unsigned int array_size = 1; | 514 | unsigned int array_size = ARRAY_SIZE(pages); |
473 | 515 | ||
474 | entry.prev_cookie = 0; | 516 | entry.prev_cookie = 0; |
475 | entry.cookie = *desc->dir_cookie; | 517 | entry.cookie = *desc->dir_cookie; |
@@ -483,18 +525,18 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, | |||
483 | memset(array, 0, sizeof(struct nfs_cache_array)); | 525 | memset(array, 0, sizeof(struct nfs_cache_array)); |
484 | array->eof_index = -1; | 526 | array->eof_index = -1; |
485 | 527 | ||
486 | xdr_page = alloc_page(GFP_KERNEL); | 528 | pages_ptr = nfs_readdir_large_page(pages, array_size); |
487 | if (!xdr_page) | 529 | if (!pages_ptr) |
488 | goto out_release_array; | 530 | goto out_release_array; |
489 | do { | 531 | do { |
490 | status = nfs_readdir_xdr_filler(xdr_page, desc, &entry, file, inode); | 532 | status = nfs_readdir_xdr_filler(pages, desc, &entry, file, inode); |
491 | 533 | ||
492 | if (status < 0) | 534 | if (status < 0) |
493 | break; | 535 | break; |
494 | nfs_readdir_page_filler(desc, &entry, xdr_page, page, array_size * PAGE_SIZE); | 536 | nfs_readdir_page_filler(desc, &entry, pages_ptr, page, array_size * PAGE_SIZE); |
495 | } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); | 537 | } while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY); |
496 | 538 | ||
497 | put_page(xdr_page); | 539 | nfs_readdir_free_large_page(pages_ptr, pages, array_size); |
498 | out_release_array: | 540 | out_release_array: |
499 | nfs_readdir_release_array(page); | 541 | nfs_readdir_release_array(page); |
500 | out: | 542 | out: |
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 74b015598a43..7b0e894d00c8 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -63,6 +63,12 @@ struct nfs_clone_mount { | |||
63 | #define NFS_UNSPEC_PORT (-1) | 63 | #define NFS_UNSPEC_PORT (-1) |
64 | 64 | ||
65 | /* | 65 | /* |
66 | * Maximum number of pages that readdir can use for creating | ||
67 | * a vmapped array of pages. | ||
68 | */ | ||
69 | #define NFS_MAX_READDIR_PAGES 8 | ||
70 | |||
71 | /* | ||
66 | * In-kernel mount arguments | 72 | * In-kernel mount arguments |
67 | */ | 73 | */ |
68 | struct nfs_parsed_mount_data { | 74 | struct nfs_parsed_mount_data { |
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c index f8446b38dcb4..ce939c062a52 100644 --- a/fs/nfs/nfs3proc.c +++ b/fs/nfs/nfs3proc.c | |||
@@ -630,7 +630,7 @@ out: | |||
630 | */ | 630 | */ |
631 | static int | 631 | static int |
632 | nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | 632 | nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, |
633 | u64 cookie, struct page *page, unsigned int count, int plus) | 633 | u64 cookie, struct page **pages, unsigned int count, int plus) |
634 | { | 634 | { |
635 | struct inode *dir = dentry->d_inode; | 635 | struct inode *dir = dentry->d_inode; |
636 | __be32 *verf = NFS_COOKIEVERF(dir); | 636 | __be32 *verf = NFS_COOKIEVERF(dir); |
@@ -640,7 +640,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | |||
640 | .verf = {verf[0], verf[1]}, | 640 | .verf = {verf[0], verf[1]}, |
641 | .plus = plus, | 641 | .plus = plus, |
642 | .count = count, | 642 | .count = count, |
643 | .pages = &page | 643 | .pages = pages |
644 | }; | 644 | }; |
645 | struct nfs3_readdirres res = { | 645 | struct nfs3_readdirres res = { |
646 | .verf = verf, | 646 | .verf = verf, |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index aa771c1af115..cb33c73206e3 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -2823,12 +2823,12 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry, | |||
2823 | } | 2823 | } |
2824 | 2824 | ||
2825 | static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | 2825 | static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, |
2826 | u64 cookie, struct page *page, unsigned int count, int plus) | 2826 | u64 cookie, struct page **pages, unsigned int count, int plus) |
2827 | { | 2827 | { |
2828 | struct inode *dir = dentry->d_inode; | 2828 | struct inode *dir = dentry->d_inode; |
2829 | struct nfs4_readdir_arg args = { | 2829 | struct nfs4_readdir_arg args = { |
2830 | .fh = NFS_FH(dir), | 2830 | .fh = NFS_FH(dir), |
2831 | .pages = &page, | 2831 | .pages = pages, |
2832 | .pgbase = 0, | 2832 | .pgbase = 0, |
2833 | .count = count, | 2833 | .count = count, |
2834 | .bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask, | 2834 | .bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask, |
@@ -2859,14 +2859,14 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | |||
2859 | } | 2859 | } |
2860 | 2860 | ||
2861 | static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | 2861 | static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, |
2862 | u64 cookie, struct page *page, unsigned int count, int plus) | 2862 | u64 cookie, struct page **pages, unsigned int count, int plus) |
2863 | { | 2863 | { |
2864 | struct nfs4_exception exception = { }; | 2864 | struct nfs4_exception exception = { }; |
2865 | int err; | 2865 | int err; |
2866 | do { | 2866 | do { |
2867 | err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode), | 2867 | err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode), |
2868 | _nfs4_proc_readdir(dentry, cred, cookie, | 2868 | _nfs4_proc_readdir(dentry, cred, cookie, |
2869 | page, count, plus), | 2869 | pages, count, plus), |
2870 | &exception); | 2870 | &exception); |
2871 | } while (exception.retry); | 2871 | } while (exception.retry); |
2872 | return err; | 2872 | return err; |
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c index e5e84aa2af17..58e7f84fc1fd 100644 --- a/fs/nfs/proc.c +++ b/fs/nfs/proc.c | |||
@@ -534,14 +534,14 @@ nfs_proc_rmdir(struct inode *dir, struct qstr *name) | |||
534 | */ | 534 | */ |
535 | static int | 535 | static int |
536 | nfs_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, | 536 | nfs_proc_readdir(struct dentry *dentry, struct rpc_cred *cred, |
537 | u64 cookie, struct page *page, unsigned int count, int plus) | 537 | u64 cookie, struct page **pages, unsigned int count, int plus) |
538 | { | 538 | { |
539 | struct inode *dir = dentry->d_inode; | 539 | struct inode *dir = dentry->d_inode; |
540 | struct nfs_readdirargs arg = { | 540 | struct nfs_readdirargs arg = { |
541 | .fh = NFS_FH(dir), | 541 | .fh = NFS_FH(dir), |
542 | .cookie = cookie, | 542 | .cookie = cookie, |
543 | .count = count, | 543 | .count = count, |
544 | .pages = &page, | 544 | .pages = pages, |
545 | }; | 545 | }; |
546 | struct rpc_message msg = { | 546 | struct rpc_message msg = { |
547 | .rpc_proc = &nfs_procedures[NFSPROC_READDIR], | 547 | .rpc_proc = &nfs_procedures[NFSPROC_READDIR], |