This patch changes the semantics of d_revalidate so that it is always called with the parent i_sem lock held. This allows the autofs4 code to release the lock if it needs to pend. Without this patch the autofs has a race condition in which it pends in the revalidate code while holding the parent i_sem lock which prevents the mount from ever completing. There have been other patches proposed for this problem which check to see if the parent i_sem lock is held before releasing it but those solutions ignore the possibility that the lock may be held by another process. diff -ur linux-2.6.13.3/fs/autofs4/root.c linux-2.6.13.3-autofspatch/fs/autofs4/root.c --- linux-2.6.13.3/fs/autofs4/root.c 2005-10-03 16:27:35.000000000 -0700 +++ linux-2.6.13.3-autofspatch/fs/autofs4/root.c 2005-11-28 04:22:52.000000000 -0800 @@ -302,7 +302,9 @@ DPRINTK("waiting for expire %p name=%.*s", dentry, dentry->d_name.len, dentry->d_name.name); + up(&dentry->d_parent->d_inode->i_sem); status = autofs4_wait(sbi, dentry, NFY_NONE); + down(&dentry->d_parent->d_inode->i_sem); DPRINTK("expire done status=%d", status); @@ -324,7 +326,9 @@ DPRINTK("waiting for mount name=%.*s", dentry->d_name.len, dentry->d_name.name); + up(&dentry->d_parent->d_inode->i_sem); status = autofs4_wait(sbi, dentry, NFY_MOUNT); + down(&dentry->d_parent->d_inode->i_sem); DPRINTK("mount done status=%d", status); @@ -351,7 +355,9 @@ spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_AUTOFS_PENDING; spin_unlock(&dentry->d_lock); + up(&dentry->d_parent->d_inode->i_sem); status = autofs4_wait(sbi, dentry, NFY_MOUNT); + down(&dentry->d_parent->d_inode->i_sem); DPRINTK("mount done status=%d", status); diff -ur linux-2.6.13.3/fs/namei.c linux-2.6.13.3-autofspatch/fs/namei.c --- linux-2.6.13.3/fs/namei.c 2005-10-03 16:27:35.000000000 -0700 +++ linux-2.6.13.3-autofspatch/fs/namei.c 2005-11-28 04:22:52.000000000 -0800 @@ -393,7 +393,6 @@ struct dentry * result; struct inode *dir = parent->d_inode; - down(&dir->i_sem); /* * First re-do the cached lookup just in case it was created * while we waited for the directory semaphore.. @@ -419,7 +418,6 @@ else result = dentry; } - up(&dir->i_sem); return result; } @@ -427,7 +425,6 @@ * Uhhuh! Nasty case: the cache was re-populated while * we waited on the semaphore. Need to revalidate. */ - up(&dir->i_sem); if (result->d_op && result->d_op->d_revalidate) { if (!result->d_op->d_revalidate(result, nd) && !d_invalidate(result)) { dput(result); @@ -676,13 +673,16 @@ struct path *path) { struct vfsmount *mnt = nd->mnt; + struct inode *parent = nd->dentry->d_inode; struct dentry *dentry = __d_lookup(nd->dentry, name); + down(&parent->i_sem); if (!dentry) goto need_lookup; if (dentry->d_op && dentry->d_op->d_revalidate) goto need_revalidate; done: + up(&parent->i_sem); path->mnt = mnt; path->dentry = dentry; __follow_mount(path); @@ -703,6 +703,7 @@ goto need_lookup; fail: + up(&parent->i_sem); return PTR_ERR(dentry); } @@ -718,7 +719,7 @@ { struct path next; struct inode *inode; - int err; + int err, reval; unsigned int lookup_flags = nd->flags; while (*name=='/') @@ -893,9 +894,17 @@ */ if (nd->dentry && nd->dentry->d_sb && (nd->dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) { + struct dentry *nparent; + err = -ESTALE; /* Note: we do not d_invalidate() */ - if (!nd->dentry->d_op->d_revalidate(nd->dentry, nd)) + /* Revalidate requires us to lock the parent. + */ + nparent = nd->dentry->d_parent; + down(&nparent->d_inode->i_sem); + reval = nd->dentry->d_op->d_revalidate(nd->dentry, nd); + up(&nparent->d_inode->i_sem); + if (!reval) break; } return_base: