From: NeilBrown <[email protected]> If afs is asked to lookup a name ending with @sys, it needs to look up a different name for which is allocates a dentry with d_alloc_parallel(). This is done while the parent lock is held which will be a problem in a future patch where the ordering of the parent lock and d_alloc_parallel() locking is reversed.
There is no actual need to hold the lock while d_alloc_parallel() is called, so with this patch we drop the lock and reclaim it. Signed-off-by: NeilBrown <[email protected]> --- fs/afs/dir.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 1e472768e1f1..c195ee851191 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -908,12 +908,14 @@ static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry) /* * Look up an entry in a directory with @sys substitution. */ -static struct dentry *afs_lookup_atsys(struct inode *dir, struct dentry *dentry) +static struct dentry *afs_lookup_atsys(struct inode *dir, struct dentry *dentry, + unsigned int flags) { struct afs_sysnames *subs; struct afs_net *net = afs_i2net(dir); struct dentry *ret; char *buf, *p, *name; + struct qstr nm; int len, i; _enter(""); @@ -933,6 +935,13 @@ static struct dentry *afs_lookup_atsys(struct inode *dir, struct dentry *dentry) refcount_inc(&subs->usage); read_unlock(&net->sysnames_lock); + /* Calling d_alloc_parallel() while holding parent locked is undesirable. + * We don't really need the lock any more. + */ + if (flags & LOOKUP_SHARED) + inode_unlock_shared(dir); + else + inode_unlock(dir); for (i = 0; i < subs->nr; i++) { name = subs->subs[i]; len = dentry->d_name.len - 4 + strlen(name); @@ -942,7 +951,10 @@ static struct dentry *afs_lookup_atsys(struct inode *dir, struct dentry *dentry) } strcpy(p, name); - ret = lookup_noperm(&QSTR(buf), dentry->d_parent); + nm = QSTR(buf); + ret = try_lookup_noperm(&nm, dentry->d_parent); + if (!ret) + ret = d_alloc_parallel(dentry->d_parent, &nm); if (IS_ERR(ret) || d_is_positive(ret)) goto out_s; dput(ret); @@ -953,6 +965,10 @@ static struct dentry *afs_lookup_atsys(struct inode *dir, struct dentry *dentry) */ ret = NULL; out_s: + if (flags & LOOKUP_SHARED) + inode_lock_shared(dir); + else + inode_lock_nested(dir, I_MUTEX_PARENT); afs_put_sysnames(subs); kfree(buf); out_p: @@ -998,7 +1014,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, dentry->d_name.name[dentry->d_name.len - 3] == 's' && dentry->d_name.name[dentry->d_name.len - 2] == 'y' && dentry->d_name.name[dentry->d_name.len - 1] == 's') - return afs_lookup_atsys(dir, dentry); + return afs_lookup_atsys(dir, dentry, flags); afs_stat_v(dvnode, n_lookup); inode = afs_do_lookup(dir, dentry); -- 2.50.0.107.gf914562f5916.dirty
