NFS: Refactor nfs_lookup_revalidate()
Refactor the code in nfs_lookup_revalidate() as a stepping stone towards optimising and fixing nfs4_lookup_revalidate(). Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
This commit is contained in:
parent
be189f7e7f
commit
5ceb9d7fda
222
fs/nfs/dir.c
222
fs/nfs/dir.c
|
@ -1072,6 +1072,100 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
|
||||||
return !nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU);
|
return !nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nfs_lookup_revalidate_done(struct inode *dir, struct dentry *dentry,
|
||||||
|
struct inode *inode, int error)
|
||||||
|
{
|
||||||
|
switch (error) {
|
||||||
|
case 1:
|
||||||
|
dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is valid\n",
|
||||||
|
__func__, dentry);
|
||||||
|
return 1;
|
||||||
|
case 0:
|
||||||
|
nfs_mark_for_revalidate(dir);
|
||||||
|
if (inode && S_ISDIR(inode->i_mode)) {
|
||||||
|
/* Purge readdir caches. */
|
||||||
|
nfs_zap_caches(inode);
|
||||||
|
/*
|
||||||
|
* We can't d_drop the root of a disconnected tree:
|
||||||
|
* its d_hash is on the s_anon list and d_drop() would hide
|
||||||
|
* it from shrink_dcache_for_unmount(), leading to busy
|
||||||
|
* inodes on unmount and further oopses.
|
||||||
|
*/
|
||||||
|
if (IS_ROOT(dentry))
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is invalid\n",
|
||||||
|
__func__, dentry);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) lookup returned error %d\n",
|
||||||
|
__func__, dentry, error);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nfs_lookup_revalidate_negative(struct inode *dir, struct dentry *dentry,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
int ret = 1;
|
||||||
|
if (nfs_neg_need_reval(dir, dentry, flags)) {
|
||||||
|
if (flags & LOOKUP_RCU)
|
||||||
|
return -ECHILD;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
return nfs_lookup_revalidate_done(dir, dentry, NULL, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nfs_lookup_revalidate_delegated(struct inode *dir, struct dentry *dentry,
|
||||||
|
struct inode *inode)
|
||||||
|
{
|
||||||
|
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||||
|
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nfs_lookup_revalidate_dentry(struct inode *dir, struct dentry *dentry,
|
||||||
|
struct inode *inode)
|
||||||
|
{
|
||||||
|
struct nfs_fh *fhandle;
|
||||||
|
struct nfs_fattr *fattr;
|
||||||
|
struct nfs4_label *label;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = -ENOMEM;
|
||||||
|
fhandle = nfs_alloc_fhandle();
|
||||||
|
fattr = nfs_alloc_fattr();
|
||||||
|
label = nfs4_label_alloc(NFS_SERVER(inode), GFP_KERNEL);
|
||||||
|
if (fhandle == NULL || fattr == NULL || IS_ERR(label))
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ret = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
|
||||||
|
if (ret < 0) {
|
||||||
|
if (ret == -ESTALE || ret == -ENOENT)
|
||||||
|
ret = 0;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
ret = 0;
|
||||||
|
if (nfs_compare_fh(NFS_FH(inode), fhandle))
|
||||||
|
goto out;
|
||||||
|
if (nfs_refresh_inode(inode, fattr) < 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
nfs_setsecurity(inode, fattr, label);
|
||||||
|
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
||||||
|
|
||||||
|
/* set a readdirplus hint that we had a cache miss */
|
||||||
|
nfs_force_use_readdirplus(dir);
|
||||||
|
ret = 1;
|
||||||
|
out:
|
||||||
|
nfs_free_fattr(fattr);
|
||||||
|
nfs_free_fhandle(fhandle);
|
||||||
|
nfs4_label_free(label);
|
||||||
|
return nfs_lookup_revalidate_done(dir, dentry, inode, ret);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is called every time the dcache has a lookup hit,
|
* This is called every time the dcache has a lookup hit,
|
||||||
* and we should check whether we can really trust that
|
* and we should check whether we can really trust that
|
||||||
|
@ -1083,58 +1177,36 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
|
||||||
* If the parent directory is seen to have changed, we throw out the
|
* If the parent directory is seen to have changed, we throw out the
|
||||||
* cached dentry and do a new lookup.
|
* cached dentry and do a new lookup.
|
||||||
*/
|
*/
|
||||||
static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
static int
|
||||||
|
nfs_do_lookup_revalidate(struct inode *dir, struct dentry *dentry,
|
||||||
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *dir;
|
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct dentry *parent;
|
|
||||||
struct nfs_fh *fhandle = NULL;
|
|
||||||
struct nfs_fattr *fattr = NULL;
|
|
||||||
struct nfs4_label *label = NULL;
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (flags & LOOKUP_RCU) {
|
|
||||||
parent = READ_ONCE(dentry->d_parent);
|
|
||||||
dir = d_inode_rcu(parent);
|
|
||||||
if (!dir)
|
|
||||||
return -ECHILD;
|
|
||||||
} else {
|
|
||||||
parent = dget_parent(dentry);
|
|
||||||
dir = d_inode(parent);
|
|
||||||
}
|
|
||||||
nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
|
nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
|
||||||
inode = d_inode(dentry);
|
inode = d_inode(dentry);
|
||||||
|
|
||||||
if (!inode) {
|
if (!inode)
|
||||||
if (nfs_neg_need_reval(dir, dentry, flags)) {
|
return nfs_lookup_revalidate_negative(dir, dentry, flags);
|
||||||
if (flags & LOOKUP_RCU)
|
|
||||||
return -ECHILD;
|
|
||||||
goto out_bad;
|
|
||||||
}
|
|
||||||
goto out_valid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_bad_inode(inode)) {
|
if (is_bad_inode(inode)) {
|
||||||
if (flags & LOOKUP_RCU)
|
|
||||||
return -ECHILD;
|
|
||||||
dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n",
|
dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n",
|
||||||
__func__, dentry);
|
__func__, dentry);
|
||||||
goto out_bad;
|
goto out_bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
|
if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
|
||||||
goto out_set_verifier;
|
return nfs_lookup_revalidate_delegated(dir, dentry, inode);
|
||||||
|
|
||||||
/* Force a full look up iff the parent directory has changed */
|
/* Force a full look up iff the parent directory has changed */
|
||||||
if (!(flags & (LOOKUP_EXCL | LOOKUP_REVAL)) &&
|
if (!(flags & (LOOKUP_EXCL | LOOKUP_REVAL)) &&
|
||||||
nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) {
|
nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) {
|
||||||
error = nfs_lookup_verify_inode(inode, flags);
|
error = nfs_lookup_verify_inode(inode, flags);
|
||||||
if (error) {
|
if (error) {
|
||||||
if (flags & LOOKUP_RCU)
|
|
||||||
return -ECHILD;
|
|
||||||
if (error == -ESTALE)
|
if (error == -ESTALE)
|
||||||
goto out_zap_parent;
|
nfs_zap_caches(dir);
|
||||||
goto out_error;
|
goto out_bad;
|
||||||
}
|
}
|
||||||
nfs_advise_use_readdirplus(dir);
|
nfs_advise_use_readdirplus(dir);
|
||||||
goto out_valid;
|
goto out_valid;
|
||||||
|
@ -1146,81 +1218,39 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
if (NFS_STALE(inode))
|
if (NFS_STALE(inode))
|
||||||
goto out_bad;
|
goto out_bad;
|
||||||
|
|
||||||
error = -ENOMEM;
|
|
||||||
fhandle = nfs_alloc_fhandle();
|
|
||||||
fattr = nfs_alloc_fattr();
|
|
||||||
if (fhandle == NULL || fattr == NULL)
|
|
||||||
goto out_error;
|
|
||||||
|
|
||||||
label = nfs4_label_alloc(NFS_SERVER(inode), GFP_NOWAIT);
|
|
||||||
if (IS_ERR(label))
|
|
||||||
goto out_error;
|
|
||||||
|
|
||||||
trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
|
trace_nfs_lookup_revalidate_enter(dir, dentry, flags);
|
||||||
error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr, label);
|
error = nfs_lookup_revalidate_dentry(dir, dentry, inode);
|
||||||
trace_nfs_lookup_revalidate_exit(dir, dentry, flags, error);
|
trace_nfs_lookup_revalidate_exit(dir, dentry, flags, error);
|
||||||
if (error == -ESTALE || error == -ENOENT)
|
return error;
|
||||||
goto out_bad;
|
out_valid:
|
||||||
if (error)
|
return nfs_lookup_revalidate_done(dir, dentry, inode, 1);
|
||||||
goto out_error;
|
out_bad:
|
||||||
if (nfs_compare_fh(NFS_FH(inode), fhandle))
|
if (flags & LOOKUP_RCU)
|
||||||
goto out_bad;
|
return -ECHILD;
|
||||||
if ((error = nfs_refresh_inode(inode, fattr)) != 0)
|
return nfs_lookup_revalidate_done(dir, dentry, inode, 0);
|
||||||
goto out_bad;
|
}
|
||||||
|
|
||||||
nfs_setsecurity(inode, fattr, label);
|
static int
|
||||||
|
nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct dentry *parent;
|
||||||
|
struct inode *dir;
|
||||||
|
int ret;
|
||||||
|
|
||||||
nfs_free_fattr(fattr);
|
|
||||||
nfs_free_fhandle(fhandle);
|
|
||||||
nfs4_label_free(label);
|
|
||||||
|
|
||||||
/* set a readdirplus hint that we had a cache miss */
|
|
||||||
nfs_force_use_readdirplus(dir);
|
|
||||||
|
|
||||||
out_set_verifier:
|
|
||||||
nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
|
|
||||||
out_valid:
|
|
||||||
if (flags & LOOKUP_RCU) {
|
if (flags & LOOKUP_RCU) {
|
||||||
|
parent = READ_ONCE(dentry->d_parent);
|
||||||
|
dir = d_inode_rcu(parent);
|
||||||
|
if (!dir)
|
||||||
|
return -ECHILD;
|
||||||
|
ret = nfs_do_lookup_revalidate(dir, dentry, flags);
|
||||||
if (parent != READ_ONCE(dentry->d_parent))
|
if (parent != READ_ONCE(dentry->d_parent))
|
||||||
return -ECHILD;
|
return -ECHILD;
|
||||||
} else
|
} else {
|
||||||
|
parent = dget_parent(dentry);
|
||||||
|
ret = nfs_do_lookup_revalidate(d_inode(parent), dentry, flags);
|
||||||
dput(parent);
|
dput(parent);
|
||||||
dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is valid\n",
|
|
||||||
__func__, dentry);
|
|
||||||
return 1;
|
|
||||||
out_zap_parent:
|
|
||||||
nfs_zap_caches(dir);
|
|
||||||
out_bad:
|
|
||||||
WARN_ON(flags & LOOKUP_RCU);
|
|
||||||
nfs_free_fattr(fattr);
|
|
||||||
nfs_free_fhandle(fhandle);
|
|
||||||
nfs4_label_free(label);
|
|
||||||
nfs_mark_for_revalidate(dir);
|
|
||||||
if (inode && S_ISDIR(inode->i_mode)) {
|
|
||||||
/* Purge readdir caches. */
|
|
||||||
nfs_zap_caches(inode);
|
|
||||||
/*
|
|
||||||
* We can't d_drop the root of a disconnected tree:
|
|
||||||
* its d_hash is on the s_anon list and d_drop() would hide
|
|
||||||
* it from shrink_dcache_for_unmount(), leading to busy
|
|
||||||
* inodes on unmount and further oopses.
|
|
||||||
*/
|
|
||||||
if (IS_ROOT(dentry))
|
|
||||||
goto out_valid;
|
|
||||||
}
|
}
|
||||||
dput(parent);
|
return ret;
|
||||||
dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is invalid\n",
|
|
||||||
__func__, dentry);
|
|
||||||
return 0;
|
|
||||||
out_error:
|
|
||||||
WARN_ON(flags & LOOKUP_RCU);
|
|
||||||
nfs_free_fattr(fattr);
|
|
||||||
nfs_free_fhandle(fhandle);
|
|
||||||
nfs4_label_free(label);
|
|
||||||
dput(parent);
|
|
||||||
dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) lookup returned error %d\n",
|
|
||||||
__func__, dentry, error);
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue