Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 209637
b: refs/heads/master
c: b04f784
h: refs/heads/master
i:
  209635: 7c0f5ce
v: v3
  • Loading branch information
Nick Piggin authored and Al Viro committed Aug 18, 2010
1 parent 7704990 commit 2a835cb
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 42 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 2a4419b5b2a77f3f4537c14f7ad7df95770655dd
refs/heads/master: b04f784e5d19ed58892833dae845738972cea260
60 changes: 35 additions & 25 deletions trunk/fs/dcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -1332,31 +1332,13 @@ EXPORT_SYMBOL(d_add_ci);
* d_lookup - search for a dentry
* @parent: parent dentry
* @name: qstr of name we wish to find
* Returns: dentry, or NULL
*
* Searches the children of the parent dentry for the name in question. If
* the dentry is found its reference count is incremented and the dentry
* is returned. The caller must use dput to free the entry when it has
* finished using it. %NULL is returned on failure.
*
* __d_lookup is dcache_lock free. The hash list is protected using RCU.
* Memory barriers are used while updating and doing lockless traversal.
* To avoid races with d_move while rename is happening, d_lock is used.
*
* Overflows in memcmp(), while d_move, are avoided by keeping the length
* and name pointer in one structure pointed by d_qstr.
*
* rcu_read_lock() and rcu_read_unlock() are used to disable preemption while
* lookup is going on.
*
* The dentry unused LRU is not updated even if lookup finds the required dentry
* in there. It is updated in places such as prune_dcache, shrink_dcache_sb,
* select_parent and __dget_locked. This laziness saves lookup from dcache_lock
* acquisition.
*
* d_lookup() is protected against the concurrent renames in some unrelated
* directory using the seqlockt_t rename_lock.
* d_lookup searches the children of the parent dentry for the name in
* question. If the dentry is found its reference count is incremented and the
* dentry is returned. The caller must use dput to free the entry when it has
* finished using it. %NULL is returned if the dentry does not exist.
*/

struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
{
struct dentry * dentry = NULL;
Expand All @@ -1372,6 +1354,21 @@ struct dentry * d_lookup(struct dentry * parent, struct qstr * name)
}
EXPORT_SYMBOL(d_lookup);

/*
* __d_lookup - search for a dentry (racy)
* @parent: parent dentry
* @name: qstr of name we wish to find
* Returns: dentry, or NULL
*
* __d_lookup is like d_lookup, however it may (rarely) return a
* false-negative result due to unrelated rename activity.
*
* __d_lookup is slightly faster by avoiding rename_lock read seqlock,
* however it must be used carefully, eg. with a following d_lookup in
* the case of failure.
*
* __d_lookup callers must be commented.
*/
struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
{
unsigned int len = name->len;
Expand All @@ -1382,6 +1379,19 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)
struct hlist_node *node;
struct dentry *dentry;

/*
* The hash list is protected using RCU.
*
* Take d_lock when comparing a candidate dentry, to avoid races
* with d_move().
*
* It is possible that concurrent renames can mess up our list
* walk here and result in missing our dentry, resulting in the
* false-negative result. d_lookup() protects against concurrent
* renames using rename_lock seqlock.
*
* See Documentation/vfs/dcache-locking.txt for more details.
*/
rcu_read_lock();

hlist_for_each_entry_rcu(dentry, node, head, d_hash) {
Expand All @@ -1396,8 +1406,8 @@ struct dentry * __d_lookup(struct dentry * parent, struct qstr * name)

/*
* Recheck the dentry after taking the lock - d_move may have
* changed things. Don't bother checking the hash because we're
* about to compare the whole name anyway.
* changed things. Don't bother checking the hash because
* we're about to compare the whole name anyway.
*/
if (dentry->d_parent != parent)
goto next;
Expand Down
32 changes: 16 additions & 16 deletions trunk/fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -735,6 +735,11 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
return err;
}

/*
* Rename seqlock is not required here because in the off chance
* of a false negative due to a concurrent rename, we're going to
* do the non-racy lookup, below.
*/
dentry = __d_lookup(nd->path.dentry, name);
if (!dentry)
goto need_lookup;
Expand All @@ -754,17 +759,13 @@ static int do_lookup(struct nameidata *nd, struct qstr *name,
mutex_lock(&dir->i_mutex);
/*
* First re-do the cached lookup just in case it was created
* while we waited for the directory semaphore..
*
* FIXME! This could use version numbering or similar to
* avoid unnecessary cache lookups.
*
* The "dcache_lock" is purely to protect the RCU list walker
* from concurrent renames at this point (we mustn't get false
* negatives from the RCU list walk here, unlike the optimistic
* fast walk).
* while we waited for the directory semaphore, or the first
* lookup failed due to an unrelated rename.
*
* so doing d_lookup() (with seqlock), instead of lockfree __d_lookup
* 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)) {
Expand Down Expand Up @@ -1136,13 +1137,12 @@ static struct dentry *__lookup_hash(struct qstr *name,
goto out;
}

dentry = __d_lookup(base, name);

/* lockess __d_lookup may fail due to concurrent d_move()
* in some unrelated directory, so try with d_lookup
/*
* Don't bother with __d_lookup: callers are for creat as
* well as unlink, so a lot of the time it would cost
* a double lookup.
*/
if (!dentry)
dentry = d_lookup(base, name);
dentry = d_lookup(base, name);

if (dentry && dentry->d_op && dentry->d_op->d_revalidate)
dentry = do_revalidate(dentry, nd);
Expand Down

0 comments on commit 2a835cb

Please sign in to comment.