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


Reply via email to