Skip to content

Commit

Permalink
NFS: readdir with vmapped pages
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Bryan Schumaker authored and Trond Myklebust committed Oct 23, 2010
1 parent afa8ccc commit 56e4ebf
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 23 deletions.
4 changes: 2 additions & 2 deletions fs/nfs/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -903,8 +903,8 @@ static void nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *
server->wtmult = nfs_block_bits(fsinfo->wtmult, NULL);

server->dtsize = nfs_block_size(fsinfo->dtpref, NULL);
if (server->dtsize > PAGE_CACHE_SIZE)
server->dtsize = PAGE_CACHE_SIZE;
if (server->dtsize > PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES)
server->dtsize = PAGE_CACHE_SIZE * NFS_MAX_READDIR_PAGES;
if (server->dtsize > server->rsize)
server->dtsize = server->rsize;

Expand Down
66 changes: 54 additions & 12 deletions fs/nfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <linux/namei.h>
#include <linux/mount.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>

#include "delegation.h"
#include "iostat.h"
Expand Down Expand Up @@ -327,7 +328,7 @@ int nfs_readdir_search_array(nfs_readdir_descriptor_t *desc)

/* Fill a page with xdr information before transferring to the cache page */
static
int nfs_readdir_xdr_filler(struct page *xdr_page, nfs_readdir_descriptor_t *desc,
int nfs_readdir_xdr_filler(struct page **pages, nfs_readdir_descriptor_t *desc,
struct nfs_entry *entry, struct file *file, struct inode *inode)
{
struct rpc_cred *cred = nfs_file_cred(file);
Expand All @@ -337,7 +338,7 @@ int nfs_readdir_xdr_filler(struct page *xdr_page, nfs_readdir_descriptor_t *desc
again:
timestamp = jiffies;
gencount = nfs_inc_attr_generation_counter();
error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, xdr_page,
error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, entry->cookie, pages,
NFS_SERVER(inode)->dtsize, desc->plus);
if (error < 0) {
/* We requested READDIRPLUS, but the server doesn't grok it */
Expand Down Expand Up @@ -436,11 +437,11 @@ void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
/* Perform conversion from xdr to cache array */
static
void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *entry,
struct page *xdr_page, struct page *page, unsigned int buflen)
void *xdr_page, struct page *page, unsigned int buflen)
{
struct xdr_stream stream;
struct xdr_buf buf;
__be32 *ptr = kmap(xdr_page);
__be32 *ptr = xdr_page;

buf.head->iov_base = xdr_page;
buf.head->iov_len = buflen;
Expand All @@ -458,18 +459,59 @@ void nfs_readdir_page_filler(nfs_readdir_descriptor_t *desc, struct nfs_entry *e
if (desc->plus == 1)
nfs_prime_dcache(desc->file->f_path.dentry, entry);
}
kunmap(xdr_page);
}

static
void nfs_readdir_free_pagearray(struct page **pages, unsigned int npages)
{
unsigned int i;
for (i = 0; i < npages; i++)
put_page(pages[i]);
}

static
void nfs_readdir_free_large_page(void *ptr, struct page **pages,
unsigned int npages)
{
vm_unmap_ram(ptr, npages);
nfs_readdir_free_pagearray(pages, npages);
}

/*
* nfs_readdir_large_page will allocate pages that must be freed with a call
* to nfs_readdir_free_large_page
*/
static
void *nfs_readdir_large_page(struct page **pages, unsigned int npages)
{
void *ptr;
unsigned int i;

for (i = 0; i < npages; i++) {
struct page *page = alloc_page(GFP_KERNEL);
if (page == NULL)
goto out_freepages;
pages[i] = page;
}

ptr = vm_map_ram(pages, NFS_MAX_READDIR_PAGES, 0, PAGE_KERNEL);
if (!IS_ERR_OR_NULL(ptr))
return ptr;
out_freepages:
nfs_readdir_free_pagearray(pages, i);
return NULL;
}

static
int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page, struct inode *inode)
{
struct page *xdr_page;
struct page *pages[NFS_MAX_READDIR_PAGES];
void *pages_ptr = NULL;
struct nfs_entry entry;
struct file *file = desc->file;
struct nfs_cache_array *array;
int status = 0;
unsigned int array_size = 1;
unsigned int array_size = ARRAY_SIZE(pages);

entry.prev_cookie = 0;
entry.cookie = *desc->dir_cookie;
Expand All @@ -483,18 +525,18 @@ int nfs_readdir_xdr_to_array(nfs_readdir_descriptor_t *desc, struct page *page,
memset(array, 0, sizeof(struct nfs_cache_array));
array->eof_index = -1;

xdr_page = alloc_page(GFP_KERNEL);
if (!xdr_page)
pages_ptr = nfs_readdir_large_page(pages, array_size);
if (!pages_ptr)
goto out_release_array;
do {
status = nfs_readdir_xdr_filler(xdr_page, desc, &entry, file, inode);
status = nfs_readdir_xdr_filler(pages, desc, &entry, file, inode);

if (status < 0)
break;
nfs_readdir_page_filler(desc, &entry, xdr_page, page, array_size * PAGE_SIZE);
nfs_readdir_page_filler(desc, &entry, pages_ptr, page, array_size * PAGE_SIZE);
} while (array->eof_index < 0 && array->size < MAX_READDIR_ARRAY);

put_page(xdr_page);
nfs_readdir_free_large_page(pages_ptr, pages, array_size);
out_release_array:
nfs_readdir_release_array(page);
out:
Expand Down
6 changes: 6 additions & 0 deletions fs/nfs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ struct nfs_clone_mount {
*/
#define NFS_UNSPEC_PORT (-1)

/*
* Maximum number of pages that readdir can use for creating
* a vmapped array of pages.
*/
#define NFS_MAX_READDIR_PAGES 8

/*
* In-kernel mount arguments
*/
Expand Down
4 changes: 2 additions & 2 deletions fs/nfs/nfs3proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ nfs3_proc_rmdir(struct inode *dir, struct qstr *name)
*/
static int
nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page *page, unsigned int count, int plus)
u64 cookie, struct page **pages, unsigned int count, int plus)
{
struct inode *dir = dentry->d_inode;
__be32 *verf = NFS_COOKIEVERF(dir);
Expand All @@ -640,7 +640,7 @@ nfs3_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
.verf = {verf[0], verf[1]},
.plus = plus,
.count = count,
.pages = &page
.pages = pages
};
struct nfs3_readdirres res = {
.verf = verf,
Expand Down
8 changes: 4 additions & 4 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2823,12 +2823,12 @@ static int nfs4_proc_mkdir(struct inode *dir, struct dentry *dentry,
}

static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page *page, unsigned int count, int plus)
u64 cookie, struct page **pages, unsigned int count, int plus)
{
struct inode *dir = dentry->d_inode;
struct nfs4_readdir_arg args = {
.fh = NFS_FH(dir),
.pages = &page,
.pages = pages,
.pgbase = 0,
.count = count,
.bitmask = NFS_SERVER(dentry->d_inode)->attr_bitmask,
Expand Down Expand Up @@ -2859,14 +2859,14 @@ static int _nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
}

static int nfs4_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page *page, unsigned int count, int plus)
u64 cookie, struct page **pages, unsigned int count, int plus)
{
struct nfs4_exception exception = { };
int err;
do {
err = nfs4_handle_exception(NFS_SERVER(dentry->d_inode),
_nfs4_proc_readdir(dentry, cred, cookie,
page, count, plus),
pages, count, plus),
&exception);
} while (exception.retry);
return err;
Expand Down
4 changes: 2 additions & 2 deletions fs/nfs/proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -534,14 +534,14 @@ nfs_proc_rmdir(struct inode *dir, struct qstr *name)
*/
static int
nfs_proc_readdir(struct dentry *dentry, struct rpc_cred *cred,
u64 cookie, struct page *page, unsigned int count, int plus)
u64 cookie, struct page **pages, unsigned int count, int plus)
{
struct inode *dir = dentry->d_inode;
struct nfs_readdirargs arg = {
.fh = NFS_FH(dir),
.cookie = cookie,
.count = count,
.pages = &page,
.pages = pages,
};
struct rpc_message msg = {
.rpc_proc = &nfs_procedures[NFSPROC_READDIR],
Expand Down
2 changes: 1 addition & 1 deletion include/linux/nfs_xdr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1026,7 +1026,7 @@ struct nfs_rpc_ops {
int (*mkdir) (struct inode *, struct dentry *, struct iattr *);
int (*rmdir) (struct inode *, struct qstr *);
int (*readdir) (struct dentry *, struct rpc_cred *,
u64, struct page *, unsigned int, int);
u64, struct page **, unsigned int, int);
int (*mknod) (struct inode *, struct dentry *, struct iattr *,
dev_t);
int (*statfs) (struct nfs_server *, struct nfs_fh *,
Expand Down

0 comments on commit 56e4ebf

Please sign in to comment.