diff options
Diffstat (limited to 'fs/dcache.c')
| -rw-r--r-- | fs/dcache.c | 71 | 
1 files changed, 41 insertions, 30 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 4d13bf50b7b1..83293be48149 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -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; @@ -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; @@ -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) { @@ -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; @@ -1925,7 +1935,7 @@ static int prepend_path(const struct path *path, struct path *root,  	bool slash = false;  	int error = 0; -	spin_lock(&vfsmount_lock); +	br_read_lock(vfsmount_lock);  	while (dentry != root->dentry || vfsmnt != root->mnt) {  		struct dentry * parent; @@ -1954,7 +1964,7 @@ out:  	if (!error && !slash)  		error = prepend(buffer, buflen, "/", 1); -	spin_unlock(&vfsmount_lock); +	br_read_unlock(vfsmount_lock);  	return error;  global_root: @@ -2292,11 +2302,12 @@ int path_is_under(struct path *path1, struct path *path2)  	struct vfsmount *mnt = path1->mnt;  	struct dentry *dentry = path1->dentry;  	int res; -	spin_lock(&vfsmount_lock); + +	br_read_lock(vfsmount_lock);  	if (mnt != path2->mnt) {  		for (;;) {  			if (mnt->mnt_parent == mnt) { -				spin_unlock(&vfsmount_lock); +				br_read_unlock(vfsmount_lock);  				return 0;  			}  			if (mnt->mnt_parent == path2->mnt) @@ -2306,7 +2317,7 @@ int path_is_under(struct path *path1, struct path *path2)  		dentry = mnt->mnt_mountpoint;  	}  	res = is_subdir(dentry, path2->dentry); -	spin_unlock(&vfsmount_lock); +	br_read_unlock(vfsmount_lock);  	return res;  }  EXPORT_SYMBOL(path_is_under);  | 
