untangle do_lookup()

That thing has devolved into rats nest of gotos; sane use of unlikely()
gets rid of that horror and gives much more readable structure:
	* make a fast attempt to find a dentry; false negatives are OK.
In RCU mode if everything went fine, we are done, otherwise just drop
out of RCU.  If we'd done (RCU) ->d_revalidate() and it had not refused
outright (i.e. didn't give us -ECHILD), remember its result.
	* now we are not in RCU mode and hopefully have a dentry.  If we
do not, lock parent, do full d_lookup() and if that has not found anything,
allocate and call ->lookup().  If we'd done that ->lookup(), remember that
dentry is good and we don't need to revalidate it.
	* now we have a dentry.  If it has ->d_revalidate() and we can't
skip it, call it.
	* hopefully dentry is good; if not, either fail (in case of error)
or try to invalidate it.  If d_invalidate() has succeeded, drop it and
retry everything as if original attempt had not found a dentry.
	* now we can finish it up - deal with mountpoint crossing and
automount.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2011-03-11 04:44:53 -05:00
parent 40b39136f0
commit 5a18fff209
1 changed files with 56 additions and 85 deletions

View File

@ -589,29 +589,6 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
return dentry;
}
static inline struct dentry *
do_revalidate_rcu(struct dentry *dentry, struct nameidata *nd)
{
int status = d_revalidate(dentry, nd);
if (likely(status > 0))
return dentry;
if (status == -ECHILD) {
if (nameidata_dentry_drop_rcu(nd, dentry))
return ERR_PTR(-ECHILD);
return do_revalidate(dentry, nd);
}
if (status < 0)
return ERR_PTR(status);
/* Don't d_invalidate in rcu-walk mode */
if (nameidata_dentry_drop_rcu(nd, dentry))
return ERR_PTR(-ECHILD);
if (!d_invalidate(dentry)) {
dput(dentry);
dentry = NULL;
}
return dentry;
}
/*
* handle_reval_path - force revalidation of a dentry
*
@ -1213,7 +1190,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
{
struct vfsmount *mnt = nd->path.mnt;
struct dentry *dentry, *parent = nd->path.dentry;
struct inode *dir;
int need_reval = 1;
int status = 1;
int err;
/*
@ -1223,48 +1201,74 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
*/
if (nd->flags & LOOKUP_RCU) {
unsigned seq;
*inode = nd->inode;
dentry = __d_lookup_rcu(parent, name, &seq, inode);
if (!dentry) {
if (nameidata_drop_rcu(nd))
return -ECHILD;
goto need_lookup;
}
if (!dentry)
goto unlazy;
/* Memory barrier in read_seqcount_begin of child is enough */
if (__read_seqcount_retry(&parent->d_seq, nd->seq))
return -ECHILD;
nd->seq = seq;
if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {
dentry = do_revalidate_rcu(dentry, nd);
if (!dentry)
goto need_lookup;
if (IS_ERR(dentry))
goto fail;
if (!(nd->flags & LOOKUP_RCU))
goto done;
status = d_revalidate(dentry, nd);
if (unlikely(status <= 0)) {
if (status != -ECHILD)
need_reval = 0;
goto unlazy;
}
}
path->mnt = mnt;
path->dentry = dentry;
if (likely(__follow_mount_rcu(nd, path, inode, false)))
return 0;
if (nameidata_drop_rcu(nd))
return -ECHILD;
/* fallthru */
unlazy:
if (dentry) {
if (nameidata_dentry_drop_rcu(nd, dentry))
return -ECHILD;
} else {
if (nameidata_drop_rcu(nd))
return -ECHILD;
}
} else {
dentry = __d_lookup(parent, name);
}
dentry = __d_lookup(parent, name);
if (!dentry)
goto need_lookup;
found:
if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {
dentry = do_revalidate(dentry, nd);
if (!dentry)
goto need_lookup;
if (IS_ERR(dentry))
goto fail;
retry:
if (unlikely(!dentry)) {
struct inode *dir = parent->d_inode;
BUG_ON(nd->inode != dir);
mutex_lock(&dir->i_mutex);
dentry = d_lookup(parent, name);
if (likely(!dentry)) {
dentry = d_alloc_and_lookup(parent, name, nd);
if (IS_ERR(dentry)) {
mutex_unlock(&dir->i_mutex);
return PTR_ERR(dentry);
}
/* known good */
need_reval = 0;
status = 1;
}
mutex_unlock(&dir->i_mutex);
}
done:
if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE) && need_reval)
status = d_revalidate(dentry, nd);
if (unlikely(status <= 0)) {
if (status < 0) {
dput(dentry);
return status;
}
if (!d_invalidate(dentry)) {
dput(dentry);
dentry = NULL;
need_reval = 1;
goto retry;
}
}
path->mnt = mnt;
path->dentry = dentry;
err = follow_managed(path, nd->flags);
@ -1274,39 +1278,6 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
}
*inode = path->dentry->d_inode;
return 0;
need_lookup:
dir = parent->d_inode;
BUG_ON(nd->inode != dir);
mutex_lock(&dir->i_mutex);
/*
* First re-do the cached lookup just in case it was created
* while we waited for the directory semaphore, or the first
* lookup failed due to an unrelated rename.
*
* This could use version numbering or similar to avoid unnecessary
* cache lookups, but then we'd have to do the first lookup in the
* non-racy way. However in the common case here, everything should
* be hot in cache, so would it be a big win?
*/
dentry = d_lookup(parent, name);
if (likely(!dentry)) {
dentry = d_alloc_and_lookup(parent, name, nd);
mutex_unlock(&dir->i_mutex);
if (IS_ERR(dentry))
goto fail;
goto done;
}
/*
* Uhhuh! Nasty case: the cache was re-populated while
* we waited on the semaphore. Need to revalidate.
*/
mutex_unlock(&dir->i_mutex);
goto found;
fail:
return PTR_ERR(dentry);
}
static inline int may_lookup(struct nameidata *nd)