Commit 311324ad authored by Trond Myklebust's avatar Trond Myklebust

NFS: Be more aggressive in using readdirplus for 'ls -l' situations

Try to detect 'ls -l' by having nfs_getattr() look at whether or not
there is an opendir() file descriptor for the parent directory.
If so, then assume that we want to force use of readdirplus in order
to avoid the multiple GETATTR calls over the wire.
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
parent 2ea24497
...@@ -69,21 +69,28 @@ const struct address_space_operations nfs_dir_aops = { ...@@ -69,21 +69,28 @@ const struct address_space_operations nfs_dir_aops = {
static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred) static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred)
{ {
struct nfs_inode *nfsi = NFS_I(dir);
struct nfs_open_dir_context *ctx; struct nfs_open_dir_context *ctx;
ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
if (ctx != NULL) { if (ctx != NULL) {
ctx->duped = 0; ctx->duped = 0;
ctx->attr_gencount = NFS_I(dir)->attr_gencount; ctx->attr_gencount = nfsi->attr_gencount;
ctx->dir_cookie = 0; ctx->dir_cookie = 0;
ctx->dup_cookie = 0; ctx->dup_cookie = 0;
ctx->cred = get_rpccred(cred); ctx->cred = get_rpccred(cred);
spin_lock(&dir->i_lock);
list_add(&ctx->list, &nfsi->open_files);
spin_unlock(&dir->i_lock);
return ctx; return ctx;
} }
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx) static void put_nfs_open_dir_context(struct inode *dir, struct nfs_open_dir_context *ctx)
{ {
spin_lock(&dir->i_lock);
list_del(&ctx->list);
spin_unlock(&dir->i_lock);
put_rpccred(ctx->cred); put_rpccred(ctx->cred);
kfree(ctx); kfree(ctx);
} }
...@@ -126,7 +133,7 @@ nfs_opendir(struct inode *inode, struct file *filp) ...@@ -126,7 +133,7 @@ nfs_opendir(struct inode *inode, struct file *filp)
static int static int
nfs_closedir(struct inode *inode, struct file *filp) nfs_closedir(struct inode *inode, struct file *filp)
{ {
put_nfs_open_dir_context(filp->private_data); put_nfs_open_dir_context(filp->f_path.dentry->d_inode, filp->private_data);
return 0; return 0;
} }
...@@ -437,6 +444,22 @@ void nfs_advise_use_readdirplus(struct inode *dir) ...@@ -437,6 +444,22 @@ void nfs_advise_use_readdirplus(struct inode *dir)
set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags); set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags);
} }
/*
* This function is mainly for use by nfs_getattr().
*
* If this is an 'ls -l', we want to force use of readdirplus.
* Do this by checking if there is an active file descriptor
* and calling nfs_advise_use_readdirplus, then forcing a
* cache flush.
*/
void nfs_force_use_readdirplus(struct inode *dir)
{
if (!list_empty(&NFS_I(dir)->open_files)) {
nfs_advise_use_readdirplus(dir);
nfs_zap_mapping(dir, dir->i_mapping);
}
}
static static
void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
{ {
...@@ -815,6 +838,17 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc) ...@@ -815,6 +838,17 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
goto out; goto out;
} }
static bool nfs_dir_mapping_need_revalidate(struct inode *dir)
{
struct nfs_inode *nfsi = NFS_I(dir);
if (nfs_attribute_cache_expired(dir))
return true;
if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
return true;
return false;
}
/* The file offset position represents the dirent entry number. A /* The file offset position represents the dirent entry number. A
last cookie cache takes care of the common case of reading the last cookie cache takes care of the common case of reading the
whole directory. whole directory.
...@@ -847,7 +881,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx) ...@@ -847,7 +881,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0; desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0;
nfs_block_sillyrename(dentry); nfs_block_sillyrename(dentry);
if (ctx->pos == 0 || nfs_attribute_cache_expired(inode)) if (ctx->pos == 0 || nfs_dir_mapping_need_revalidate(inode))
res = nfs_revalidate_mapping(inode, file->f_mapping); res = nfs_revalidate_mapping(inode, file->f_mapping);
if (res < 0) if (res < 0)
goto out; goto out;
......
...@@ -588,6 +588,25 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr) ...@@ -588,6 +588,25 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
} }
EXPORT_SYMBOL_GPL(nfs_setattr_update_inode); EXPORT_SYMBOL_GPL(nfs_setattr_update_inode);
static void nfs_request_parent_use_readdirplus(struct dentry *dentry)
{
struct dentry *parent;
parent = dget_parent(dentry);
nfs_force_use_readdirplus(parent->d_inode);
dput(parent);
}
static bool nfs_need_revalidate_inode(struct inode *inode)
{
if (NFS_I(inode)->cache_validity &
(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
return true;
if (nfs_attribute_cache_expired(inode))
return true;
return false;
}
int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -616,10 +635,13 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) ...@@ -616,10 +635,13 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))) ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode)))
need_atime = 0; need_atime = 0;
if (need_atime) if (need_atime || nfs_need_revalidate_inode(inode)) {
err = __nfs_revalidate_inode(NFS_SERVER(inode), inode); struct nfs_server *server = NFS_SERVER(inode);
else
err = nfs_revalidate_inode(NFS_SERVER(inode), inode); if (server->caps & NFS_CAP_READDIRPLUS)
nfs_request_parent_use_readdirplus(dentry);
err = __nfs_revalidate_inode(server, inode);
}
if (!err) { if (!err) {
generic_fillattr(inode, stat); generic_fillattr(inode, stat);
stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode)); stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode));
...@@ -961,9 +983,7 @@ int nfs_attribute_cache_expired(struct inode *inode) ...@@ -961,9 +983,7 @@ int nfs_attribute_cache_expired(struct inode *inode)
*/ */
int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
{ {
if (!(NFS_I(inode)->cache_validity & if (!nfs_need_revalidate_inode(inode))
(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
&& !nfs_attribute_cache_expired(inode))
return NFS_STALE(inode) ? -ESTALE : 0; return NFS_STALE(inode) ? -ESTALE : 0;
return __nfs_revalidate_inode(server, inode); return __nfs_revalidate_inode(server, inode);
} }
......
...@@ -300,6 +300,7 @@ extern struct nfs_client *nfs_init_client(struct nfs_client *clp, ...@@ -300,6 +300,7 @@ extern struct nfs_client *nfs_init_client(struct nfs_client *clp,
const char *ip_addr); const char *ip_addr);
/* dir.c */ /* dir.c */
extern void nfs_force_use_readdirplus(struct inode *dir);
extern unsigned long nfs_access_cache_count(struct shrinker *shrink, extern unsigned long nfs_access_cache_count(struct shrinker *shrink,
struct shrink_control *sc); struct shrink_control *sc);
extern unsigned long nfs_access_cache_scan(struct shrinker *shrink, extern unsigned long nfs_access_cache_scan(struct shrinker *shrink,
......
...@@ -92,6 +92,7 @@ struct nfs_open_context { ...@@ -92,6 +92,7 @@ struct nfs_open_context {
}; };
struct nfs_open_dir_context { struct nfs_open_dir_context {
struct list_head list;
struct rpc_cred *cred; struct rpc_cred *cred;
unsigned long attr_gencount; unsigned long attr_gencount;
__u64 dir_cookie; __u64 dir_cookie;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment