[kernel] r14190 - in dists/lenny/linux-2.6/debian: . patches/bugfix/all patches/series

Dann Frazier dannf at alioth.debian.org
Tue Aug 25 16:12:49 UTC 2009


Author: dannf
Date: Tue Aug 25 16:12:47 2009
New Revision: 14190

Log:
autofs4: don't make expiring dentry negative, avoiding an oops
(Closes: #530636)

Added:
   dists/lenny/linux-2.6/debian/patches/bugfix/all/autofs4-dont-make-expiring-dentry-negative.patch
Modified:
   dists/lenny/linux-2.6/debian/changelog
   dists/lenny/linux-2.6/debian/patches/series/20

Modified: dists/lenny/linux-2.6/debian/changelog
==============================================================================
--- dists/lenny/linux-2.6/debian/changelog	Tue Aug 25 05:19:14 2009	(r14189)
+++ dists/lenny/linux-2.6/debian/changelog	Tue Aug 25 16:12:47 2009	(r14190)
@@ -4,6 +4,10 @@
   * xen: Fix crash in xen_spin_wait() on busy multiprocessor domain
     (Closes: #542250), thanks to Nikita V. Youshchenko <yoush at debian.org>
 
+  [ dann frazier ]
+  * autofs4: don't make expiring dentry negative, avoiding an oops
+    (Closes: #530636)
+
  -- Ben Hutchings <ben at decadent.org.uk>  Fri, 21 Aug 2009 00:11:55 +0100
 
 linux-2.6 (2.6.26-19) stable; urgency=high

Added: dists/lenny/linux-2.6/debian/patches/bugfix/all/autofs4-dont-make-expiring-dentry-negative.patch
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ dists/lenny/linux-2.6/debian/patches/bugfix/all/autofs4-dont-make-expiring-dentry-negative.patch	Tue Aug 25 16:12:47 2009	(r14190)
@@ -0,0 +1,297 @@
+commit 5f6f4f28b6ba543beef8bad91aa6f69c7ffeee51
+Author: Ian Kent <raven at themaw.net>
+Date:   Wed Jul 23 21:30:09 2008 -0700
+
+    autofs4: don't make expiring dentry negative
+    
+    Correct the error of making a positive dentry negative after it has been
+    instantiated.
+    
+    The code that makes this error attempts to re-use the dentry from a
+    concurrent expire and mount to resolve a race and the dentry used for the
+    lookup must be negative for mounts to trigger in the required cases.  The
+    fact is that the dentry doesn't need to be re-used because all that is
+    needed is to preserve the flag that indicates an expire is still
+    incomplete at the time of the mount request.
+    
+    This change uses the the dentry to check the flag and wait for the expire
+    to complete then discards it instead of attempting to re-use it.
+    
+    Signed-off-by: Ian Kent <raven at themaw.net>
+    Signed-off-by: Andrew Morton <akpm at linux-foundation.org>
+    Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
+
+diff --git a/fs/autofs4/autofs_i.h b/fs/autofs4/autofs_i.h
+index c3d352d..69b1497 100644
+--- a/fs/autofs4/autofs_i.h
++++ b/fs/autofs4/autofs_i.h
+@@ -52,7 +52,7 @@ struct autofs_info {
+ 
+ 	int		flags;
+ 
+-	struct list_head rehash;
++	struct list_head expiring;
+ 
+ 	struct autofs_sb_info *sbi;
+ 	unsigned long last_used;
+@@ -112,8 +112,8 @@ struct autofs_sb_info {
+ 	struct mutex wq_mutex;
+ 	spinlock_t fs_lock;
+ 	struct autofs_wait_queue *queues; /* Wait queue pointer */
+-	spinlock_t rehash_lock;
+-	struct list_head rehash_list;
++	spinlock_t lookup_lock;
++	struct list_head expiring_list;
+ };
+ 
+ static inline struct autofs_sb_info *autofs4_sbi(struct super_block *sb)
+diff --git a/fs/autofs4/inode.c b/fs/autofs4/inode.c
+index 2fdcf5e..94bfc15 100644
+--- a/fs/autofs4/inode.c
++++ b/fs/autofs4/inode.c
+@@ -47,7 +47,7 @@ struct autofs_info *autofs4_init_ino(struct autofs_info *ino,
+ 	ino->dentry = NULL;
+ 	ino->size = 0;
+ 
+-	INIT_LIST_HEAD(&ino->rehash);
++	INIT_LIST_HEAD(&ino->expiring);
+ 
+ 	ino->last_used = jiffies;
+ 	atomic_set(&ino->count, 0);
+@@ -338,8 +338,8 @@ int autofs4_fill_super(struct super_block *s, void *data, int silent)
+ 	mutex_init(&sbi->wq_mutex);
+ 	spin_lock_init(&sbi->fs_lock);
+ 	sbi->queues = NULL;
+-	spin_lock_init(&sbi->rehash_lock);
+-	INIT_LIST_HEAD(&sbi->rehash_list);
++	spin_lock_init(&sbi->lookup_lock);
++	INIT_LIST_HEAD(&sbi->expiring_list);
+ 	s->s_blocksize = 1024;
+ 	s->s_blocksize_bits = 10;
+ 	s->s_magic = AUTOFS_SUPER_MAGIC;
+diff --git a/fs/autofs4/root.c b/fs/autofs4/root.c
+index edf5b6b..9ead227 100644
+--- a/fs/autofs4/root.c
++++ b/fs/autofs4/root.c
+@@ -493,10 +493,10 @@ void autofs4_dentry_release(struct dentry *de)
+ 		struct autofs_sb_info *sbi = autofs4_sbi(de->d_sb);
+ 
+ 		if (sbi) {
+-			spin_lock(&sbi->rehash_lock);
+-			if (!list_empty(&inf->rehash))
+-				list_del(&inf->rehash);
+-			spin_unlock(&sbi->rehash_lock);
++			spin_lock(&sbi->lookup_lock);
++			if (!list_empty(&inf->expiring))
++				list_del(&inf->expiring);
++			spin_unlock(&sbi->lookup_lock);
+ 		}
+ 
+ 		inf->dentry = NULL;
+@@ -518,7 +518,7 @@ static struct dentry_operations autofs4_dentry_operations = {
+ 	.d_release	= autofs4_dentry_release,
+ };
+ 
+-static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name)
++static struct dentry *autofs4_lookup_expiring(struct autofs_sb_info *sbi, struct dentry *parent, struct qstr *name)
+ {
+ 	unsigned int len = name->len;
+ 	unsigned int hash = name->hash;
+@@ -526,14 +526,14 @@ static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct
+ 	struct list_head *p, *head;
+ 
+ 	spin_lock(&dcache_lock);
+-	spin_lock(&sbi->rehash_lock);
+-	head = &sbi->rehash_list;
++	spin_lock(&sbi->lookup_lock);
++	head = &sbi->expiring_list;
+ 	list_for_each(p, head) {
+ 		struct autofs_info *ino;
+ 		struct dentry *dentry;
+ 		struct qstr *qstr;
+ 
+-		ino = list_entry(p, struct autofs_info, rehash);
++		ino = list_entry(p, struct autofs_info, expiring);
+ 		dentry = ino->dentry;
+ 
+ 		spin_lock(&dentry->d_lock);
+@@ -555,33 +555,16 @@ static struct dentry *autofs4_lookup_unhashed(struct autofs_sb_info *sbi, struct
+ 			goto next;
+ 
+ 		if (d_unhashed(dentry)) {
+-			struct inode *inode = dentry->d_inode;
+-
+-			ino = autofs4_dentry_ino(dentry);
+-			list_del_init(&ino->rehash);
+ 			dget(dentry);
+-			/*
+-			 * Make the rehashed dentry negative so the VFS
+-			 * behaves as it should.
+-			 */
+-			if (inode) {
+-				dentry->d_inode = NULL;
+-				list_del_init(&dentry->d_alias);
+-				spin_unlock(&dentry->d_lock);
+-				spin_unlock(&sbi->rehash_lock);
+-				spin_unlock(&dcache_lock);
+-				iput(inode);
+-				return dentry;
+-			}
+ 			spin_unlock(&dentry->d_lock);
+-			spin_unlock(&sbi->rehash_lock);
++			spin_unlock(&sbi->lookup_lock);
+ 			spin_unlock(&dcache_lock);
+ 			return dentry;
+ 		}
+ next:
+ 		spin_unlock(&dentry->d_lock);
+ 	}
+-	spin_unlock(&sbi->rehash_lock);
++	spin_unlock(&sbi->lookup_lock);
+ 	spin_unlock(&dcache_lock);
+ 
+ 	return NULL;
+@@ -591,7 +574,7 @@ next:
+ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
+ {
+ 	struct autofs_sb_info *sbi;
+-	struct dentry *unhashed;
++	struct dentry *expiring;
+ 	int oz_mode;
+ 
+ 	DPRINTK("name = %.*s",
+@@ -607,44 +590,44 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
+ 	DPRINTK("pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d",
+ 		 current->pid, task_pgrp_nr(current), sbi->catatonic, oz_mode);
+ 
+-	unhashed = autofs4_lookup_unhashed(sbi, dentry->d_parent, &dentry->d_name);
+-	if (!unhashed) {
+-		/*
+-		 * Mark the dentry incomplete but don't hash it. We do this
+-		 * to serialize our inode creation operations (symlink and
+-		 * mkdir) which prevents deadlock during the callback to
+-		 * the daemon. Subsequent user space lookups for the same
+-		 * dentry are placed on the wait queue while the daemon
+-		 * itself is allowed passage unresticted so the create
+-		 * operation itself can then hash the dentry. Finally,
+-		 * we check for the hashed dentry and return the newly
+-		 * hashed dentry.
+-		 */
+-		dentry->d_op = &autofs4_root_dentry_operations;
+-
+-		dentry->d_fsdata = NULL;
+-		d_instantiate(dentry, NULL);
+-	} else {
+-		struct autofs_info *ino = autofs4_dentry_ino(unhashed);
+-		DPRINTK("rehash %p with %p", dentry, unhashed);
++	expiring = autofs4_lookup_expiring(sbi, dentry->d_parent, &dentry->d_name);
++	if (expiring) {
++		struct autofs_info *ino = autofs4_dentry_ino(expiring);
+ 		/*
+ 		 * If we are racing with expire the request might not
+ 		 * be quite complete but the directory has been removed
+ 		 * 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.
+ 		 */
+ 		while (ino && (ino->flags & AUTOFS_INF_EXPIRING)) {
+ 			DPRINTK("wait for incomplete expire %p name=%.*s",
+-				unhashed, unhashed->d_name.len,
+-				unhashed->d_name.name);
+-			autofs4_wait(sbi, unhashed, NFY_NONE);
++				expiring, expiring->d_name.len,
++				expiring->d_name.name);
++			autofs4_wait(sbi, expiring, NFY_NONE);
+ 			DPRINTK("request completed");
+ 		}
+-		dentry = unhashed;
++		spin_lock(&sbi->lookup_lock);
++		if (!list_empty(&ino->expiring))
++			list_del_init(&ino->expiring);
++		spin_unlock(&sbi->lookup_lock);
++		dput(expiring);
+ 	}
+ 
++	/*
++	 * Mark the dentry incomplete but don't hash it. We do this
++	 * to serialize our inode creation operations (symlink and
++	 * mkdir) which prevents deadlock during the callback to
++	 * the daemon. Subsequent user space lookups for the same
++	 * dentry are placed on the wait queue while the daemon
++	 * itself is allowed passage unresticted so the create
++	 * operation itself can then hash the dentry. Finally,
++	 * we check for the hashed dentry and return the newly
++	 * hashed dentry.
++	 */
++	dentry->d_op = &autofs4_root_dentry_operations;
++
++	dentry->d_fsdata = NULL;
++	d_instantiate(dentry, NULL);
++
+ 	if (!oz_mode) {
+ 		spin_lock(&dentry->d_lock);
+ 		dentry->d_flags |= DCACHE_AUTOFS_PENDING;
+@@ -668,8 +651,6 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
+ 			if (sigismember (sigset, SIGKILL) ||
+ 			    sigismember (sigset, SIGQUIT) ||
+ 			    sigismember (sigset, SIGINT)) {
+-			    if (unhashed)
+-				dput(unhashed);
+ 			    return ERR_PTR(-ERESTARTNOINTR);
+ 			}
+ 		}
+@@ -699,15 +680,9 @@ static struct dentry *autofs4_lookup(struct inode *dir, struct dentry *dentry, s
+ 		else
+ 			dentry = ERR_PTR(-ENOENT);
+ 
+-		if (unhashed)
+-			dput(unhashed);
+-
+ 		return dentry;
+ 	}
+ 
+-	if (unhashed)
+-		return dentry;
+-
+ 	return NULL;
+ }
+ 
+@@ -769,9 +744,8 @@ static int autofs4_dir_symlink(struct inode *dir,
+  * that the file no longer exists. However, doing that means that the
+  * VFS layer can turn the dentry into a negative dentry.  We don't want
+  * this, because the unlink is probably the result of an expire.
+- * We simply d_drop it and add it to a rehash candidates list in the
+- * super block, which allows the dentry lookup to reuse it retaining
+- * the flags, such as expire in progress, in case we're racing with expire.
++ * We simply d_drop it and add it to a expiring list in the super block,
++ * which allows the dentry lookup to check for an incomplete expire.
+  *
+  * If a process is blocked on the dentry waiting for the expire to finish,
+  * it will invalidate the dentry and try to mount with a new one.
+@@ -801,9 +775,9 @@ static int autofs4_dir_unlink(struct inode *dir, struct dentry *dentry)
+ 	dir->i_mtime = CURRENT_TIME;
+ 
+ 	spin_lock(&dcache_lock);
+-	spin_lock(&sbi->rehash_lock);
+-	list_add(&ino->rehash, &sbi->rehash_list);
+-	spin_unlock(&sbi->rehash_lock);
++	spin_lock(&sbi->lookup_lock);
++	list_add(&ino->expiring, &sbi->expiring_list);
++	spin_unlock(&sbi->lookup_lock);
+ 	spin_lock(&dentry->d_lock);
+ 	__d_drop(dentry);
+ 	spin_unlock(&dentry->d_lock);
+@@ -829,9 +803,9 @@ static int autofs4_dir_rmdir(struct inode *dir, struct dentry *dentry)
+ 		spin_unlock(&dcache_lock);
+ 		return -ENOTEMPTY;
+ 	}
+-	spin_lock(&sbi->rehash_lock);
+-	list_add(&ino->rehash, &sbi->rehash_list);
+-	spin_unlock(&sbi->rehash_lock);
++	spin_lock(&sbi->lookup_lock);
++	list_add(&ino->expiring, &sbi->expiring_list);
++	spin_unlock(&sbi->lookup_lock);
+ 	spin_lock(&dentry->d_lock);
+ 	__d_drop(dentry);
+ 	spin_unlock(&dentry->d_lock);

Modified: dists/lenny/linux-2.6/debian/patches/series/20
==============================================================================
--- dists/lenny/linux-2.6/debian/patches/series/20	Tue Aug 25 05:19:14 2009	(r14189)
+++ dists/lenny/linux-2.6/debian/patches/series/20	Tue Aug 25 16:12:47 2009	(r14190)
@@ -0,0 +1 @@
++ bugfix/all/autofs4-dont-make-expiring-dentry-negative.patch



More information about the Kernel-svn-changes mailing list