Skip to content

Commit

Permalink
NFS: Adapt readdirplus to application usage patterns
Browse files Browse the repository at this point in the history
While the use of READDIRPLUS is significantly more efficient than
READDIR followed by many LOOKUP calls, it is still less efficient
than just READDIR if the attributes are not required.

This patch tracks when lookups are attempted on the directory,
and uses that information to selectively disable READDIRPLUS
on that directory.
The first 'readdir' call is always served using READDIRPLUS.
Subsequent calls only use READDIRPLUS if there was a successful
lookup or revalidation on a child in the mean time.

Credit for the original idea should go to Neil Brown. See:
      http://www.spinics.net/lists/linux-nfs/msg19996.html
However, the implementation in this patch differs from Neil's
in that it focuses on tracking lookups rather than calls to
stat().

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: Neil Brown <neilb@suse.de>
  • Loading branch information
Trond Myklebust authored and Trond Myklebust committed May 1, 2012
1 parent 8582715 commit d69ee9b
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 9 deletions.
33 changes: 31 additions & 2 deletions fs/nfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,29 @@ int nfs_same_file(struct dentry *dentry, struct nfs_entry *entry)
return 0;
}

static
bool nfs_use_readdirplus(struct inode *dir, struct file *filp)
{
if (!nfs_server_capable(dir, NFS_CAP_READDIRPLUS))
return false;
if (test_and_clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags))
return true;
if (filp->f_pos == 0)
return true;
return false;
}

/*
* This function is called by the lookup code to request the use of
* readdirplus to accelerate any future lookups in the same
* directory.
*/
static
void nfs_advise_use_readdirplus(struct inode *dir)
{
set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags);
}

static
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
{
Expand Down Expand Up @@ -874,7 +897,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
desc->file = filp;
desc->dir_cookie = &dir_ctx->dir_cookie;
desc->decode = NFS_PROTO(inode)->decode_dirent;
desc->plus = NFS_USE_READDIRPLUS(inode);
desc->plus = nfs_use_readdirplus(inode, filp) ? 1 : 0;

nfs_block_sillyrename(dentry);
res = nfs_revalidate_mapping(inode, filp->f_mapping);
Expand Down Expand Up @@ -1114,7 +1137,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
if (!inode) {
if (nfs_neg_need_reval(dir, dentry, nd))
goto out_bad;
goto out_valid;
goto out_valid_noent;
}

if (is_bad_inode(inode)) {
Expand Down Expand Up @@ -1156,6 +1179,9 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
out_set_verifier:
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
out_valid:
/* Success: notify readdir to use READDIRPLUS */
nfs_advise_use_readdirplus(dir);
out_valid_noent:
dput(parent);
dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n",
__func__, dentry->d_parent->d_name.name,
Expand Down Expand Up @@ -1311,6 +1337,9 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
if (IS_ERR(res))
goto out_unblock_sillyrename;

/* Success: notify readdir to use READDIRPLUS */
nfs_advise_use_readdirplus(dir);

no_entry:
res = d_materialise_unique(dentry, inode);
if (res != NULL) {
Expand Down
2 changes: 0 additions & 2 deletions fs/nfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,6 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
inode->i_fop = &nfs_dir_operations;
inode->i_data.a_ops = &nfs_dir_aops;
if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
/* Deal with crossing mountpoints */
if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
Expand Down
5 changes: 0 additions & 5 deletions include/linux/nfs_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -314,11 +314,6 @@ static inline int nfs_server_capable(struct inode *inode, int cap)
return NFS_SERVER(inode)->caps & cap;
}

static inline int NFS_USE_READDIRPLUS(struct inode *inode)
{
return test_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
}

static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
{
dentry->d_time = verf;
Expand Down

0 comments on commit d69ee9b

Please sign in to comment.