Commit 5a18fff2 authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 40b39136
...@@ -589,29 +589,6 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd) ...@@ -589,29 +589,6 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
return dentry; 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 * handle_reval_path - force revalidation of a dentry
* *
...@@ -1213,7 +1190,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, ...@@ -1213,7 +1190,8 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
{ {
struct vfsmount *mnt = nd->path.mnt; struct vfsmount *mnt = nd->path.mnt;
struct dentry *dentry, *parent = nd->path.dentry; struct dentry *dentry, *parent = nd->path.dentry;
struct inode *dir; int need_reval = 1;
int status = 1;
int err; int err;
/* /*
...@@ -1223,90 +1201,83 @@ static int do_lookup(struct nameidata *nd, struct qstr *name, ...@@ -1223,90 +1201,83 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
*/ */
if (nd->flags & LOOKUP_RCU) { if (nd->flags & LOOKUP_RCU) {
unsigned seq; unsigned seq;
*inode = nd->inode; *inode = nd->inode;
dentry = __d_lookup_rcu(parent, name, &seq, inode); dentry = __d_lookup_rcu(parent, name, &seq, inode);
if (!dentry) { if (!dentry)
if (nameidata_drop_rcu(nd)) goto unlazy;
return -ECHILD;
goto need_lookup;
}
/* Memory barrier in read_seqcount_begin of child is enough */ /* Memory barrier in read_seqcount_begin of child is enough */
if (__read_seqcount_retry(&parent->d_seq, nd->seq)) if (__read_seqcount_retry(&parent->d_seq, nd->seq))
return -ECHILD; return -ECHILD;
nd->seq = seq; nd->seq = seq;
if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) { if (unlikely(dentry->d_flags & DCACHE_OP_REVALIDATE)) {
dentry = do_revalidate_rcu(dentry, nd); status = d_revalidate(dentry, nd);
if (!dentry) if (unlikely(status <= 0)) {
goto need_lookup; if (status != -ECHILD)
if (IS_ERR(dentry)) need_reval = 0;
goto fail; goto unlazy;
if (!(nd->flags & LOOKUP_RCU)) }
goto done;
} }
path->mnt = mnt; path->mnt = mnt;
path->dentry = dentry; path->dentry = dentry;
if (likely(__follow_mount_rcu(nd, path, inode, false))) if (likely(__follow_mount_rcu(nd, path, inode, false)))
return 0; return 0;
unlazy:
if (dentry) {
if (nameidata_dentry_drop_rcu(nd, dentry))
return -ECHILD;
} else {
if (nameidata_drop_rcu(nd)) if (nameidata_drop_rcu(nd))
return -ECHILD; return -ECHILD;
/* fallthru */
} }
} 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;
} }
done:
path->mnt = mnt;
path->dentry = dentry;
err = follow_managed(path, nd->flags);
if (unlikely(err < 0)) {
path_put_conditional(path, nd);
return err;
}
*inode = path->dentry->d_inode;
return 0;
need_lookup: retry:
dir = parent->d_inode; if (unlikely(!dentry)) {
struct inode *dir = parent->d_inode;
BUG_ON(nd->inode != dir); BUG_ON(nd->inode != dir);
mutex_lock(&dir->i_mutex); 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); dentry = d_lookup(parent, name);
if (likely(!dentry)) { if (likely(!dentry)) {
dentry = d_alloc_and_lookup(parent, name, nd); dentry = d_alloc_and_lookup(parent, name, nd);
if (IS_ERR(dentry)) {
mutex_unlock(&dir->i_mutex); mutex_unlock(&dir->i_mutex);
if (IS_ERR(dentry)) return PTR_ERR(dentry);
goto fail; }
goto done; /* known good */
need_reval = 0;
status = 1;
} }
/*
* Uhhuh! Nasty case: the cache was re-populated while
* we waited on the semaphore. Need to revalidate.
*/
mutex_unlock(&dir->i_mutex); mutex_unlock(&dir->i_mutex);
goto found; }
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;
}
}
fail: path->mnt = mnt;
return PTR_ERR(dentry); path->dentry = dentry;
err = follow_managed(path, nd->flags);
if (unlikely(err < 0)) {
path_put_conditional(path, nd);
return err;
}
*inode = path->dentry->d_inode;
return 0;
} }
static inline int may_lookup(struct nameidata *nd) static inline int may_lookup(struct nameidata *nd)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment