aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2012-05-01 17:37:59 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2012-05-01 19:16:43 -0400
commitd69ee9b85541a69a1092f5da675bd23256dc62af (patch)
tree5f890def2d1d4bca93a5acaf073fa4cbe5f16a44
parent8582715e733d08bc98fe629db0601360d70de4dc (diff)
NFS: Adapt readdirplus to application usage patterns
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>
-rw-r--r--fs/nfs/dir.c33
-rw-r--r--fs/nfs/inode.c2
-rw-r--r--include/linux/nfs_fs.h5
3 files changed, 31 insertions, 9 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 82b42e2ea65c..d0884c0d9464 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -475,6 +475,29 @@ different:
475} 475}
476 476
477static 477static
478bool nfs_use_readdirplus(struct inode *dir, struct file *filp)
479{
480 if (!nfs_server_capable(dir, NFS_CAP_READDIRPLUS))
481 return false;
482 if (test_and_clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags))
483 return true;
484 if (filp->f_pos == 0)
485 return true;
486 return false;
487}
488
489/*
490 * This function is called by the lookup code to request the use of
491 * readdirplus to accelerate any future lookups in the same
492 * directory.
493 */
494static
495void nfs_advise_use_readdirplus(struct inode *dir)
496{
497 set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags);
498}
499
500static
478void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) 501void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry)
479{ 502{
480 struct qstr filename = { 503 struct qstr filename = {
@@ -874,7 +897,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
874 desc->file = filp; 897 desc->file = filp;
875 desc->dir_cookie = &dir_ctx->dir_cookie; 898 desc->dir_cookie = &dir_ctx->dir_cookie;
876 desc->decode = NFS_PROTO(inode)->decode_dirent; 899 desc->decode = NFS_PROTO(inode)->decode_dirent;
877 desc->plus = NFS_USE_READDIRPLUS(inode); 900 desc->plus = nfs_use_readdirplus(inode, filp) ? 1 : 0;
878 901
879 nfs_block_sillyrename(dentry); 902 nfs_block_sillyrename(dentry);
880 res = nfs_revalidate_mapping(inode, filp->f_mapping); 903 res = nfs_revalidate_mapping(inode, filp->f_mapping);
@@ -1114,7 +1137,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
1114 if (!inode) { 1137 if (!inode) {
1115 if (nfs_neg_need_reval(dir, dentry, nd)) 1138 if (nfs_neg_need_reval(dir, dentry, nd))
1116 goto out_bad; 1139 goto out_bad;
1117 goto out_valid; 1140 goto out_valid_noent;
1118 } 1141 }
1119 1142
1120 if (is_bad_inode(inode)) { 1143 if (is_bad_inode(inode)) {
@@ -1156,6 +1179,9 @@ static int nfs_lookup_revalidate(struct dentry *dentry, struct nameidata *nd)
1156out_set_verifier: 1179out_set_verifier:
1157 nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); 1180 nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
1158 out_valid: 1181 out_valid:
1182 /* Success: notify readdir to use READDIRPLUS */
1183 nfs_advise_use_readdirplus(dir);
1184 out_valid_noent:
1159 dput(parent); 1185 dput(parent);
1160 dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n", 1186 dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n",
1161 __func__, dentry->d_parent->d_name.name, 1187 __func__, dentry->d_parent->d_name.name,
@@ -1311,6 +1337,9 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
1311 if (IS_ERR(res)) 1337 if (IS_ERR(res))
1312 goto out_unblock_sillyrename; 1338 goto out_unblock_sillyrename;
1313 1339
1340 /* Success: notify readdir to use READDIRPLUS */
1341 nfs_advise_use_readdirplus(dir);
1342
1314no_entry: 1343no_entry:
1315 res = d_materialise_unique(dentry, inode); 1344 res = d_materialise_unique(dentry, inode);
1316 if (res != NULL) { 1345 if (res != NULL) {
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 0d53113207e5..9f17cd19e710 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -298,8 +298,6 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
298 inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops; 298 inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
299 inode->i_fop = &nfs_dir_operations; 299 inode->i_fop = &nfs_dir_operations;
300 inode->i_data.a_ops = &nfs_dir_aops; 300 inode->i_data.a_ops = &nfs_dir_aops;
301 if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
302 set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
303 /* Deal with crossing mountpoints */ 301 /* Deal with crossing mountpoints */
304 if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT || 302 if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
305 fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { 303 fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 8a88c16662c5..6cc7dbaf0695 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -314,11 +314,6 @@ static inline int nfs_server_capable(struct inode *inode, int cap)
314 return NFS_SERVER(inode)->caps & cap; 314 return NFS_SERVER(inode)->caps & cap;
315} 315}
316 316
317static inline int NFS_USE_READDIRPLUS(struct inode *inode)
318{
319 return test_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
320}
321
322static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf) 317static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
323{ 318{
324 dentry->d_time = verf; 319 dentry->d_time = verf;