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>
This commit is contained in:
parent
f3324a2a94
commit
912a108da7
3 changed files with 52 additions and 17 deletions
59
fs/nfs/dir.c
59
fs/nfs/dir.c
|
@ -988,9 +988,13 @@ EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate);
|
||||||
* A check for whether or not the parent directory has changed.
|
* A check for whether or not the parent directory has changed.
|
||||||
* In the case it has, we assume that the dentries are untrustworthy
|
* In the case it has, we assume that the dentries are untrustworthy
|
||||||
* and may need to be looked up again.
|
* and may need to be looked up again.
|
||||||
|
* If rcu_walk prevents us from performing a full check, return 0.
|
||||||
*/
|
*/
|
||||||
static int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
|
static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
|
||||||
|
int rcu_walk)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
if (IS_ROOT(dentry))
|
if (IS_ROOT(dentry))
|
||||||
return 1;
|
return 1;
|
||||||
if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE)
|
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)
|
||||||
if (!nfs_verify_change_attribute(dir, dentry->d_time))
|
if (!nfs_verify_change_attribute(dir, dentry->d_time))
|
||||||
return 0;
|
return 0;
|
||||||
/* Revalidate nfsi->cache_change_attribute before we declare a match */
|
/* Revalidate nfsi->cache_change_attribute before we declare a match */
|
||||||
if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0)
|
if (rcu_walk)
|
||||||
|
ret = nfs_revalidate_inode_rcu(NFS_SERVER(dir), dir);
|
||||||
|
else
|
||||||
|
ret = nfs_revalidate_inode(NFS_SERVER(dir), dir);
|
||||||
|
if (ret < 0)
|
||||||
return 0;
|
return 0;
|
||||||
if (!nfs_verify_change_attribute(dir, dentry->d_time))
|
if (!nfs_verify_change_attribute(dir, dentry->d_time))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1054,6 +1062,9 @@ out_force:
|
||||||
*
|
*
|
||||||
* If parent mtime has changed, we revalidate, else we wait for a
|
* If parent mtime has changed, we revalidate, else we wait for a
|
||||||
* period corresponding to the parent's attribute cache timeout value.
|
* period corresponding to the parent's attribute cache timeout value.
|
||||||
|
*
|
||||||
|
* If LOOKUP_RCU prevents us from performing a full check, return 1
|
||||||
|
* suggesting a reval is needed.
|
||||||
*/
|
*/
|
||||||
static inline
|
static inline
|
||||||
int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
|
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,
|
||||||
return 0;
|
return 0;
|
||||||
if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG)
|
if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG)
|
||||||
return 1;
|
return 1;
|
||||||
return !nfs_check_verifier(dir, dentry);
|
return !nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1101,11 +1112,11 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
inode = dentry->d_inode;
|
inode = dentry->d_inode;
|
||||||
|
|
||||||
if (!inode) {
|
if (!inode) {
|
||||||
if (flags & LOOKUP_RCU)
|
if (nfs_neg_need_reval(dir, dentry, flags)) {
|
||||||
return -ECHILD;
|
if (flags & LOOKUP_RCU)
|
||||||
|
return -ECHILD;
|
||||||
if (nfs_neg_need_reval(dir, dentry, flags))
|
|
||||||
goto out_bad;
|
goto out_bad;
|
||||||
|
}
|
||||||
goto out_valid_noent;
|
goto out_valid_noent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1120,16 +1131,21 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
|
if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
|
||||||
goto out_set_verifier;
|
goto out_set_verifier;
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU)
|
|
||||||
return -ECHILD;
|
|
||||||
|
|
||||||
/* Force a full look up iff the parent directory has changed */
|
/* Force a full look up iff the parent directory has changed */
|
||||||
if (!nfs_is_exclusive_create(dir, flags) && nfs_check_verifier(dir, dentry)) {
|
if (!nfs_is_exclusive_create(dir, flags) &&
|
||||||
|
nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) {
|
||||||
|
|
||||||
|
if (flags & LOOKUP_RCU)
|
||||||
|
return -ECHILD;
|
||||||
|
|
||||||
if (nfs_lookup_verify_inode(inode, flags))
|
if (nfs_lookup_verify_inode(inode, flags))
|
||||||
goto out_zap_parent;
|
goto out_zap_parent;
|
||||||
goto out_valid;
|
goto out_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flags & LOOKUP_RCU)
|
||||||
|
return -ECHILD;
|
||||||
|
|
||||||
if (NFS_STALE(inode))
|
if (NFS_STALE(inode))
|
||||||
goto out_bad;
|
goto out_bad;
|
||||||
|
|
||||||
|
@ -1566,14 +1582,23 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
struct dentry *parent;
|
struct dentry *parent;
|
||||||
struct inode *dir;
|
struct inode *dir;
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU)
|
if (flags & LOOKUP_RCU) {
|
||||||
return -ECHILD;
|
parent = rcu_dereference(dentry);
|
||||||
|
dir = ACCESS_ONCE(parent->d_inode);
|
||||||
parent = dget_parent(dentry);
|
if (!dir)
|
||||||
dir = parent->d_inode;
|
return -ECHILD;
|
||||||
|
} else {
|
||||||
|
parent = dget_parent(dentry);
|
||||||
|
dir = parent->d_inode;
|
||||||
|
}
|
||||||
if (!nfs_neg_need_reval(dir, dentry, flags))
|
if (!nfs_neg_need_reval(dir, dentry, flags))
|
||||||
ret = 1;
|
ret = 1;
|
||||||
dput(parent);
|
else if (flags & LOOKUP_RCU)
|
||||||
|
ret = -ECHILD;
|
||||||
|
if (!(flags & LOOKUP_RCU))
|
||||||
|
dput(parent);
|
||||||
|
else if (parent != rcu_dereference(dentry))
|
||||||
|
return -ECHILD;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1002,6 +1002,15 @@ int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(nfs_revalidate_inode);
|
EXPORT_SYMBOL_GPL(nfs_revalidate_inode);
|
||||||
|
|
||||||
|
int nfs_revalidate_inode_rcu(struct nfs_server *server, struct inode *inode)
|
||||||
|
{
|
||||||
|
if (!(NFS_I(inode)->cache_validity &
|
||||||
|
(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_LABEL))
|
||||||
|
&& !nfs_attribute_cache_expired(inode))
|
||||||
|
return NFS_STALE(inode) ? -ESTALE : 0;
|
||||||
|
return -ECHILD;
|
||||||
|
}
|
||||||
|
|
||||||
static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
|
static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
|
||||||
{
|
{
|
||||||
struct nfs_inode *nfsi = NFS_I(inode);
|
struct nfs_inode *nfsi = NFS_I(inode);
|
||||||
|
|
|
@ -352,6 +352,7 @@ extern int nfs_release(struct inode *, struct file *);
|
||||||
extern int nfs_attribute_timeout(struct inode *inode);
|
extern int nfs_attribute_timeout(struct inode *inode);
|
||||||
extern int nfs_attribute_cache_expired(struct inode *inode);
|
extern int nfs_attribute_cache_expired(struct inode *inode);
|
||||||
extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
|
extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
|
||||||
|
extern int nfs_revalidate_inode_rcu(struct nfs_server *server, struct inode *inode);
|
||||||
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
|
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
|
||||||
extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
|
extern int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
|
||||||
extern int nfs_setattr(struct dentry *, struct iattr *);
|
extern int nfs_setattr(struct dentry *, struct iattr *);
|
||||||
|
|
Loading…
Add table
Reference in a new issue