diff options
author | NeilBrown <neilb@suse.de> | 2014-07-13 21:28:20 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-08-03 17:14:12 -0400 |
commit | 912a108da767ae75cc929d2854e698aff527ec5d (patch) | |
tree | 7ae1fda9f3512f9fabd1cc6d2467b4a9c416d745 /fs/nfs | |
parent | f3324a2a94c229831cfd42d871902cd4a9bd5e0f (diff) |
NFS: teach nfs_neg_need_reval to understand LOOKUP_RCU
This requires nfs_check_verifier to take an rcu_walk flag, and requires
an rcu version of nfs_revalidate_inode which returns -ECHILD rather
than making an RPC call.
With this, nfs_lookup_revalidate can call nfs_neg_need_reval in
RCU-walk mode.
We can also move the LOOKUP_RCU check past the nfs_check_verifier()
call in nfs_lookup_revalidate.
If RCU_WALK prevents nfs_check_verifier or nfs_neg_need_reval from
doing a full check, they return a status indicating that a revalidation
is required. As this revalidation will not be possible in RCU_WALK
mode, -ECHILD will ultimately be returned, which is the desired result.
Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/dir.c | 59 | ||||
-rw-r--r-- | fs/nfs/inode.c | 9 |
2 files changed, 51 insertions, 17 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 8a3c36984fc4..dcd4fe5831d6 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -988,9 +988,13 @@ EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate); | |||
988 | * A check for whether or not the parent directory has changed. | 988 | * A check for whether or not the parent directory has changed. |
989 | * In the case it has, we assume that the dentries are untrustworthy | 989 | * In the case it has, we assume that the dentries are untrustworthy |
990 | * and may need to be looked up again. | 990 | * and may need to be looked up again. |
991 | * If rcu_walk prevents us from performing a full check, return 0. | ||
991 | */ | 992 | */ |
992 | static int nfs_check_verifier(struct inode *dir, struct dentry *dentry) | 993 | static int nfs_check_verifier(struct inode *dir, struct dentry *dentry, |
994 | int rcu_walk) | ||
993 | { | 995 | { |
996 | int ret; | ||
997 | |||
994 | if (IS_ROOT(dentry)) | 998 | if (IS_ROOT(dentry)) |
995 | return 1; | 999 | return 1; |
996 | if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE) | 1000 | if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE) |
@@ -998,7 +1002,11 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry) | |||
998 | if (!nfs_verify_change_attribute(dir, dentry->d_time)) | 1002 | if (!nfs_verify_change_attribute(dir, dentry->d_time)) |
999 | return 0; | 1003 | return 0; |
1000 | /* Revalidate nfsi->cache_change_attribute before we declare a match */ | 1004 | /* Revalidate nfsi->cache_change_attribute before we declare a match */ |
1001 | if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0) | 1005 | if (rcu_walk) |
1006 | ret = nfs_revalidate_inode_rcu(NFS_SERVER(dir), dir); | ||
1007 | else | ||
1008 | ret = nfs_revalidate_inode(NFS_SERVER(dir), dir); | ||
1009 | if (ret < 0) | ||
1002 | return 0; | 1010 | return 0; |
1003 | if (!nfs_verify_change_attribute(dir, dentry->d_time)) | 1011 | if (!nfs_verify_change_attribute(dir, dentry->d_time)) |
1004 | return 0; | 1012 | return 0; |
@@ -1054,6 +1062,9 @@ out_force: | |||
1054 | * | 1062 | * |
1055 | * If parent mtime has changed, we revalidate, else we wait for a | 1063 | * If parent mtime has changed, we revalidate, else we wait for a |
1056 | * period corresponding to the parent's attribute cache timeout value. | 1064 | * period corresponding to the parent's attribute cache timeout value. |
1065 | * | ||
1066 | * If LOOKUP_RCU prevents us from performing a full check, return 1 | ||
1067 | * suggesting a reval is needed. | ||
1057 | */ | 1068 | */ |
1058 | static inline | 1069 | static inline |
1059 | int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, | 1070 | int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, |
@@ -1064,7 +1075,7 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry, | |||
1064 | return 0; | 1075 | return 0; |
1065 | if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG) | 1076 | if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG) |
1066 | return 1; | 1077 | return 1; |
1067 | return !nfs_check_verifier(dir, dentry); | 1078 | return !nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU); |
1068 | } | 1079 | } |
1069 | 1080 | ||
1070 | /* | 1081 | /* |
@@ -1101,11 +1112,11 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) | |||
1101 | inode = dentry->d_inode; | 1112 | inode = dentry->d_inode; |
1102 | 1113 | ||
1103 | if (!inode) { | 1114 | if (!inode) { |
1104 | if (flags & LOOKUP_RCU) | 1115 | if (nfs_neg_need_reval(dir, dentry, flags)) { |
1105 | return -ECHILD; | 1116 | if (flags & LOOKUP_RCU) |
1106 | 1117 | return -ECHILD; | |
1107 | if (nfs_neg_need_reval(dir, dentry, flags)) | ||
1108 | goto out_bad; | 1118 | goto out_bad; |
1119 | } | ||
1109 | goto out_valid_noent; | 1120 | goto out_valid_noent; |
1110 | } | 1121 | } |
1111 | 1122 | ||
@@ -1120,16 +1131,21 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) | |||
1120 | if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ)) | 1131 | if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ)) |
1121 | goto out_set_verifier; | 1132 | goto out_set_verifier; |
1122 | 1133 | ||
1123 | if (flags & LOOKUP_RCU) | ||
1124 | return -ECHILD; | ||
1125 | |||
1126 | /* Force a full look up iff the parent directory has changed */ | 1134 | /* Force a full look up iff the parent directory has changed */ |
1127 | if (!nfs_is_exclusive_create(dir, flags) && nfs_check_verifier(dir, dentry)) { | 1135 | if (!nfs_is_exclusive_create(dir, flags) && |
1136 | nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) { | ||
1137 | |||
1138 | if (flags & LOOKUP_RCU) | ||
1139 | return -ECHILD; | ||
1140 | |||
1128 | if (nfs_lookup_verify_inode(inode, flags)) | 1141 | if (nfs_lookup_verify_inode(inode, flags)) |
1129 | goto out_zap_parent; | 1142 | goto out_zap_parent; |
1130 | goto out_valid; | 1143 | goto out_valid; |
1131 | } | 1144 | } |
1132 | 1145 | ||
1146 | if (flags & LOOKUP_RCU) | ||
1147 | return -ECHILD; | ||
1148 | |||
1133 | if (NFS_STALE(inode)) | 1149 | if (NFS_STALE(inode)) |
1134 | goto out_bad; | 1150 | goto out_bad; |
1135 | 1151 | ||
@@ -1566,14 +1582,23 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags) | |||
1566 | struct dentry *parent; | 1582 | struct dentry *parent; |
1567 | struct inode *dir; | 1583 | struct inode *dir; |
1568 | 1584 | ||
1569 | if (flags & LOOKUP_RCU) | 1585 | if (flags & LOOKUP_RCU) { |
1570 | return -ECHILD; | 1586 | parent = rcu_dereference(dentry); |
1571 | 1587 | dir = ACCESS_ONCE(parent->d_inode); | |
1572 | parent = dget_parent(dentry); | 1588 | if (!dir) |
1573 | dir = parent->d_inode; | 1589 | return -ECHILD; |
1590 | } else { | ||
1591 | parent = dget_parent(dentry); | ||
1592 | dir = parent->d_inode; | ||
1593 | } | ||
1574 | if (!nfs_neg_need_reval(dir, dentry, flags)) | 1594 | if (!nfs_neg_need_reval(dir, dentry, flags)) |
1575 | ret = 1; | 1595 | ret = 1; |
1576 | dput(parent); | 1596 | else if (flags & LOOKUP_RCU) |
1597 | ret = -ECHILD; | ||
1598 | if (!(flags & LOOKUP_RCU)) | ||
1599 | dput(parent); | ||
1600 | else if (parent != rcu_dereference(dentry)) | ||
1601 | return -ECHILD; | ||
1577 | goto out; | 1602 | goto out; |
1578 | } | 1603 | } |
1579 | 1604 | ||
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 9927913c97c2..147fd17e7920 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -1002,6 +1002,15 @@ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode) | |||
1002 | } | 1002 | } |
1003 | EXPORT_SYMBOL_GPL(nfs_revalidate_inode); | 1003 | EXPORT_SYMBOL_GPL(nfs_revalidate_inode); |
1004 | 1004 | ||
1005 | int nfs_revalidate_inode_rcu(struct nfs_server *server, struct inode *inode) | ||
1006 | { | ||
1007 | if (!(NFS_I(inode)->cache_validity & | ||
1008 | (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL)) | ||
1009 | && !nfs_attribute_cache_expired(inode)) | ||
1010 | return NFS_STALE(inode) ? -ESTALE : 0; | ||
1011 | return -ECHILD; | ||
1012 | } | ||
1013 | |||
1005 | static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping) | 1014 | static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping) |
1006 | { | 1015 | { |
1007 | struct nfs_inode *nfsi = NFS_I(inode); | 1016 | struct nfs_inode *nfsi = NFS_I(inode); |