diff options
-rw-r--r-- | fs/nfs/dir.c | 42 | ||||
-rw-r--r-- | fs/nfs/inode.c | 34 | ||||
-rw-r--r-- | fs/nfs/internal.h | 1 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 1 |
4 files changed, 67 insertions, 11 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index be38b573495a..c8e48c26418b 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -69,21 +69,28 @@ const struct address_space_operations nfs_dir_aops = { | |||
69 | 69 | ||
70 | static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred) | 70 | static struct nfs_open_dir_context *alloc_nfs_open_dir_context(struct inode *dir, struct rpc_cred *cred) |
71 | { | 71 | { |
72 | struct nfs_inode *nfsi = NFS_I(dir); | ||
72 | struct nfs_open_dir_context *ctx; | 73 | struct nfs_open_dir_context *ctx; |
73 | ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); | 74 | ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); |
74 | if (ctx != NULL) { | 75 | if (ctx != NULL) { |
75 | ctx->duped = 0; | 76 | ctx->duped = 0; |
76 | ctx->attr_gencount = NFS_I(dir)->attr_gencount; | 77 | ctx->attr_gencount = nfsi->attr_gencount; |
77 | ctx->dir_cookie = 0; | 78 | ctx->dir_cookie = 0; |
78 | ctx->dup_cookie = 0; | 79 | ctx->dup_cookie = 0; |
79 | ctx->cred = get_rpccred(cred); | 80 | ctx->cred = get_rpccred(cred); |
81 | spin_lock(&dir->i_lock); | ||
82 | list_add(&ctx->list, &nfsi->open_files); | ||
83 | spin_unlock(&dir->i_lock); | ||
80 | return ctx; | 84 | return ctx; |
81 | } | 85 | } |
82 | return ERR_PTR(-ENOMEM); | 86 | return ERR_PTR(-ENOMEM); |
83 | } | 87 | } |
84 | 88 | ||
85 | static void put_nfs_open_dir_context(struct nfs_open_dir_context *ctx) | 89 | static void put_nfs_open_dir_context(struct inode *dir, struct nfs_open_dir_context *ctx) |
86 | { | 90 | { |
91 | spin_lock(&dir->i_lock); | ||
92 | list_del(&ctx->list); | ||
93 | spin_unlock(&dir->i_lock); | ||
87 | put_rpccred(ctx->cred); | 94 | put_rpccred(ctx->cred); |
88 | kfree(ctx); | 95 | kfree(ctx); |
89 | } | 96 | } |
@@ -126,7 +133,7 @@ out: | |||
126 | static int | 133 | static int |
127 | nfs_closedir(struct inode *inode, struct file *filp) | 134 | nfs_closedir(struct inode *inode, struct file *filp) |
128 | { | 135 | { |
129 | put_nfs_open_dir_context(filp->private_data); | 136 | put_nfs_open_dir_context(filp->f_path.dentry->d_inode, filp->private_data); |
130 | return 0; | 137 | return 0; |
131 | } | 138 | } |
132 | 139 | ||
@@ -437,6 +444,22 @@ void nfs_advise_use_readdirplus(struct inode *dir) | |||
437 | set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags); | 444 | set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags); |
438 | } | 445 | } |
439 | 446 | ||
447 | /* | ||
448 | * This function is mainly for use by nfs_getattr(). | ||
449 | * | ||
450 | * If this is an 'ls -l', we want to force use of readdirplus. | ||
451 | * Do this by checking if there is an active file descriptor | ||
452 | * and calling nfs_advise_use_readdirplus, then forcing a | ||
453 | * cache flush. | ||
454 | */ | ||
455 | void nfs_force_use_readdirplus(struct inode *dir) | ||
456 | { | ||
457 | if (!list_empty(&NFS_I(dir)->open_files)) { | ||
458 | nfs_advise_use_readdirplus(dir); | ||
459 | nfs_zap_mapping(dir, dir->i_mapping); | ||
460 | } | ||
461 | } | ||
462 | |||
440 | static | 463 | static |
441 | void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) | 464 | void nfs_prime_dcache(struct dentry *parent, struct nfs_entry *entry) |
442 | { | 465 | { |
@@ -815,6 +838,17 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc) | |||
815 | goto out; | 838 | goto out; |
816 | } | 839 | } |
817 | 840 | ||
841 | static bool nfs_dir_mapping_need_revalidate(struct inode *dir) | ||
842 | { | ||
843 | struct nfs_inode *nfsi = NFS_I(dir); | ||
844 | |||
845 | if (nfs_attribute_cache_expired(dir)) | ||
846 | return true; | ||
847 | if (nfsi->cache_validity & NFS_INO_INVALID_DATA) | ||
848 | return true; | ||
849 | return false; | ||
850 | } | ||
851 | |||
818 | /* The file offset position represents the dirent entry number. A | 852 | /* The file offset position represents the dirent entry number. A |
819 | last cookie cache takes care of the common case of reading the | 853 | last cookie cache takes care of the common case of reading the |
820 | whole directory. | 854 | whole directory. |
@@ -847,7 +881,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx) | |||
847 | desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0; | 881 | desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0; |
848 | 882 | ||
849 | nfs_block_sillyrename(dentry); | 883 | nfs_block_sillyrename(dentry); |
850 | if (ctx->pos == 0 || nfs_attribute_cache_expired(inode)) | 884 | if (ctx->pos == 0 || nfs_dir_mapping_need_revalidate(inode)) |
851 | res = nfs_revalidate_mapping(inode, file->f_mapping); | 885 | res = nfs_revalidate_mapping(inode, file->f_mapping); |
852 | if (res < 0) | 886 | if (res < 0) |
853 | goto out; | 887 | goto out; |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 360114ae8b82..9dbef878a2b2 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -588,6 +588,25 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr) | |||
588 | } | 588 | } |
589 | EXPORT_SYMBOL_GPL(nfs_setattr_update_inode); | 589 | EXPORT_SYMBOL_GPL(nfs_setattr_update_inode); |
590 | 590 | ||
591 | static void nfs_request_parent_use_readdirplus(struct dentry *dentry) | ||
592 | { | ||
593 | struct dentry *parent; | ||
594 | |||
595 | parent = dget_parent(dentry); | ||
596 | nfs_force_use_readdirplus(parent->d_inode); | ||
597 | dput(parent); | ||
598 | } | ||
599 | |||
600 | static bool nfs_need_revalidate_inode(struct inode *inode) | ||
601 | { | ||
602 | if (NFS_I(inode)->cache_validity & | ||
603 | (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) | ||
604 | return true; | ||
605 | if (nfs_attribute_cache_expired(inode)) | ||
606 | return true; | ||
607 | return false; | ||
608 | } | ||
609 | |||
591 | int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) | 610 | int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) |
592 | { | 611 | { |
593 | struct inode *inode = dentry->d_inode; | 612 | struct inode *inode = dentry->d_inode; |
@@ -616,10 +635,13 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) | |||
616 | ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))) | 635 | ((mnt->mnt_flags & MNT_NODIRATIME) && S_ISDIR(inode->i_mode))) |
617 | need_atime = 0; | 636 | need_atime = 0; |
618 | 637 | ||
619 | if (need_atime) | 638 | if (need_atime || nfs_need_revalidate_inode(inode)) { |
620 | err = __nfs_revalidate_inode(NFS_SERVER(inode), inode); | 639 | struct nfs_server *server = NFS_SERVER(inode); |
621 | else | 640 | |
622 | err = nfs_revalidate_inode(NFS_SERVER(inode), inode); | 641 | if (server->caps & NFS_CAP_READDIRPLUS) |
642 | nfs_request_parent_use_readdirplus(dentry); | ||
643 | err = __nfs_revalidate_inode(server, inode); | ||
644 | } | ||
623 | if (!err) { | 645 | if (!err) { |
624 | generic_fillattr(inode, stat); | 646 | generic_fillattr(inode, stat); |
625 | stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode)); | 647 | stat->ino = nfs_compat_user_ino64(NFS_FILEID(inode)); |
@@ -961,9 +983,7 @@ int nfs_attribute_cache_expired(struct inode *inode) | |||
961 | */ | 983 | */ |
962 | int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) | 984 | int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) |
963 | { | 985 | { |
964 | if (!(NFS_I(inode)->cache_validity & | 986 | if (!nfs_need_revalidate_inode(inode)) |
965 | (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) | ||
966 | && !nfs_attribute_cache_expired(inode)) | ||
967 | return NFS_STALE(inode) ? -ESTALE : 0; | 987 | return NFS_STALE(inode) ? -ESTALE : 0; |
968 | return __nfs_revalidate_inode(server, inode); | 988 | return __nfs_revalidate_inode(server, inode); |
969 | } | 989 | } |
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index fafdddac8271..7f7c476d0c2c 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -300,6 +300,7 @@ extern struct nfs_client *nfs_init_client(struct nfs_client *clp, | |||
300 | const char *ip_addr); | 300 | const char *ip_addr); |
301 | 301 | ||
302 | /* dir.c */ | 302 | /* dir.c */ |
303 | extern void nfs_force_use_readdirplus(struct inode *dir); | ||
303 | extern unsigned long nfs_access_cache_count(struct shrinker *shrink, | 304 | extern unsigned long nfs_access_cache_count(struct shrinker *shrink, |
304 | struct shrink_control *sc); | 305 | struct shrink_control *sc); |
305 | extern unsigned long nfs_access_cache_scan(struct shrinker *shrink, | 306 | extern unsigned long nfs_access_cache_scan(struct shrinker *shrink, |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 0ae5807480f4..f55a90bed0b4 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -92,6 +92,7 @@ struct nfs_open_context { | |||
92 | }; | 92 | }; |
93 | 93 | ||
94 | struct nfs_open_dir_context { | 94 | struct nfs_open_dir_context { |
95 | struct list_head list; | ||
95 | struct rpc_cred *cred; | 96 | struct rpc_cred *cred; |
96 | unsigned long attr_gencount; | 97 | unsigned long attr_gencount; |
97 | __u64 dir_cookie; | 98 | __u64 dir_cookie; |