Commit 1864f7bd authored by Ian Kent's avatar Ian Kent Committed by Linus Torvalds

autofs4: deadlock during create

Due to inconsistent locking in the VFS between calls to lookup and
revalidate deadlock can occur in the automounter.

The inconsistency is that the directory inode mutex is held for both lookup
and revalidate calls when called via lookup_hash whereas it is held only
for lookup during a path walk.  Consequently, if the mutex is held during a
call to revalidate autofs4 can't release the mutex to callback the daemon
as it can't know whether it owns the mutex.

This situation happens when a process tries to create a directory within an
automount and a second process also tries to create the same directory
between the lookup and the mkdir.  Since the first process has dropped the
mutex for the daemon callback, the second process takes it during
revalidate leading to deadlock between the autofs daemon and the second
process when the daemon tries to create the mount point directory.

After spending quite a bit of time trying to resolve this on more than one
occassion, using rather complex and ulgy approaches, it turns out that just
delaying the hashing of the dentry until the create operation works fine.
Signed-off-by: default avatarIan Kent <raven@themaw.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent f4768ffd
...@@ -587,19 +587,20 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s ...@@ -587,19 +587,20 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name); unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name);
if (!unhashed) { if (!unhashed) {
/* /*
* Mark the dentry incomplete, but add it. This is needed so * Mark the dentry incomplete but don't hash it. We do this
* that the VFS layer knows about the dentry, and we can count * to serialize our inode creation operations (symlink and
* on catching any lookups through the revalidate. * mkdir) which prevents deadlock during the callback to
* * the daemon. Subsequent user space lookups for the same
* Let all the hard work be done by the revalidate function that * dentry are placed on the wait queue while the daemon
* needs to be able to do this anyway.. * itself is allowed passage unresticted so the create
* * operation itself can then hash the dentry. Finally,
* We need to do this before we release the directory semaphore. * we check for the hashed dentry and return the newly
* hashed dentry.
*/ */
dentry->d_op = &autofs4_root_dentry_operations; dentry->d_op = &autofs4_root_dentry_operations;
dentry->d_fsdata = NULL; dentry->d_fsdata = NULL;
d_add(dentry, NULL); d_instantiate(dentry, NULL);
} else { } else {
struct autofs_info *ino = autofs4_dentry_ino(unhashed); struct autofs_info *ino = autofs4_dentry_ino(unhashed);
DPRINTK("rehash %p with %p", dentry, unhashed); DPRINTK("rehash %p with %p", dentry, unhashed);
...@@ -607,15 +608,17 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s ...@@ -607,15 +608,17 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
* If we are racing with expire the request might not * If we are racing with expire the request might not
* be quite complete but the directory has been removed * be quite complete but the directory has been removed
* so it must have been successful, so just wait for it. * so it must have been successful, so just wait for it.
* We need to ensure the AUTOFS_INF_EXPIRING flag is clear
* before continuing as revalidate may fail when calling
* try_to_fill_dentry (returning EAGAIN) if we don't.
*/ */
if (ino && (ino->flags & AUTOFS_INF_EXPIRING)) { while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) {
DPRINTK("wait for incomplete expire %p name=%.*s", DPRINTK("wait for incomplete expire %p name=%.*s",
unhashed, unhashed->d_name.len, unhashed, unhashed->d_name.len,
unhashed->d_name.name); unhashed->d_name.name);
autofs4_wait(sbi, unhashed, NFY_NONE); autofs4_wait(sbi, unhashed, NFY_NONE);
DPRINTK("request completed"); DPRINTK("request completed");
} }
d_rehash(unhashed);
dentry = unhashed; dentry = unhashed;
} }
...@@ -658,7 +661,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s ...@@ -658,7 +661,7 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
* for all system calls, but it should be OK for the operations * for all system calls, but it should be OK for the operations
* we permit from an autofs. * we permit from an autofs.
*/ */
if (dentry->d_inode && d_unhashed(dentry)) { if (!oz_mode && d_unhashed(dentry)) {
/* /*
* A user space application can (and has done in the past) * A user space application can (and has done in the past)
* remove and re-create this directory during the callback. * remove and re-create this directory during the callback.
...@@ -716,7 +719,7 @@ static int autofs4_dir_symlink(struct inode *dir, ...@@ -716,7 +719,7 @@ static int autofs4_dir_symlink(struct inode *dir,
strcpy(cp, symname); strcpy(cp, symname);
inode = autofs4_get_inode(dir->i_sb, ino); inode = autofs4_get_inode(dir->i_sb, ino);
d_instantiate(dentry, inode); d_add(dentry, inode);
if (dir == dir->i_sb->s_root->d_inode) if (dir == dir->i_sb->s_root->d_inode)
dentry->d_op = &autofs4_root_dentry_operations; dentry->d_op = &autofs4_root_dentry_operations;
...@@ -844,7 +847,7 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode) ...@@ -844,7 +847,7 @@ static int autofs4_dir_mkdir(struct inode *dir, struct dentry *dentry, int mode)
return -ENOSPC; return -ENOSPC;
inode = autofs4_get_inode(dir->i_sb, ino); inode = autofs4_get_inode(dir->i_sb, ino);
d_instantiate(dentry, inode); d_add(dentry, inode);
if (dir == dir->i_sb->s_root->d_inode) if (dir == dir->i_sb->s_root->d_inode)
dentry->d_op = &autofs4_root_dentry_operations; dentry->d_op = &autofs4_root_dentry_operations;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment