--- /dev/null
+From c826cb7dfce80512c26c984350077a25046bd215 Mon Sep 17 00:00:00 2001
+From: Linus Torvalds <torvalds@linux-foundation.org>
+Date: Tue, 15 Mar 2011 15:29:21 -0700
+Subject: dcache.c: create helper function for duplicated functionality
+
+From: Linus Torvalds <torvalds@linux-foundation.org>
+
+commit c826cb7dfce80512c26c984350077a25046bd215 upstream.
+
+This creates a helper function for he "try to ascend into the parent
+directory" case, which was written out in triplicate before. With all
+the locking and subtle sequence number stuff, we really don't want to
+duplicate that kind of code.
+
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/dcache.c | 88 +++++++++++++++++++++++++-----------------------------------
+ 1 file changed, 37 insertions(+), 51 deletions(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -1012,6 +1012,34 @@ void shrink_dcache_for_umount(struct sup
+ }
+
+ /*
++ * This tries to ascend one level of parenthood, but
++ * we can race with renaming, so we need to re-check
++ * the parenthood after dropping the lock and check
++ * that the sequence number still matches.
++ */
++static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq)
++{
++ struct dentry *new = old->d_parent;
++
++ rcu_read_lock();
++ spin_unlock(&old->d_lock);
++ spin_lock(&new->d_lock);
++
++ /*
++ * might go back up the wrong parent if we have had a rename
++ * or deletion
++ */
++ if (new != old->d_parent ||
++ (!locked && read_seqretry(&rename_lock, seq))) {
++ spin_unlock(&new->d_lock);
++ new = NULL;
++ }
++ rcu_read_unlock();
++ return new;
++}
++
++
++/*
+ * Search for at least 1 mount point in the dentry's subdirs.
+ * We descend to the next level whenever the d_subdirs
+ * list is non-empty and continue searching.
+@@ -1066,24 +1094,10 @@ resume:
+ * All done at this level ... ascend and resume the search.
+ */
+ if (this_parent != parent) {
+- struct dentry *tmp;
+- struct dentry *child;
+-
+- tmp = this_parent->d_parent;
+- rcu_read_lock();
+- spin_unlock(&this_parent->d_lock);
+- child = this_parent;
+- this_parent = tmp;
+- spin_lock(&this_parent->d_lock);
+- /* might go back up the wrong parent if we have had a rename
+- * or deletion */
+- if (this_parent != child->d_parent ||
+- (!locked && read_seqretry(&rename_lock, seq))) {
+- spin_unlock(&this_parent->d_lock);
+- rcu_read_unlock();
++ struct dentry *child = this_parent;
++ this_parent = try_to_ascend(this_parent, locked, seq);
++ if (!this_parent)
+ goto rename_retry;
+- }
+- rcu_read_unlock();
+ next = child->d_u.d_child.next;
+ goto resume;
+ }
+@@ -1181,24 +1195,10 @@ resume:
+ * All done at this level ... ascend and resume the search.
+ */
+ if (this_parent != parent) {
+- struct dentry *tmp;
+- struct dentry *child;
+-
+- tmp = this_parent->d_parent;
+- rcu_read_lock();
+- spin_unlock(&this_parent->d_lock);
+- child = this_parent;
+- this_parent = tmp;
+- spin_lock(&this_parent->d_lock);
+- /* might go back up the wrong parent if we have had a rename
+- * or deletion */
+- if (this_parent != child->d_parent ||
+- (!locked && read_seqretry(&rename_lock, seq))) {
+- spin_unlock(&this_parent->d_lock);
+- rcu_read_unlock();
++ struct dentry *child = this_parent;
++ this_parent = try_to_ascend(this_parent, locked, seq);
++ if (!this_parent)
+ goto rename_retry;
+- }
+- rcu_read_unlock();
+ next = child->d_u.d_child.next;
+ goto resume;
+ }
+@@ -2942,28 +2942,14 @@ resume:
+ spin_unlock(&dentry->d_lock);
+ }
+ if (this_parent != root) {
+- struct dentry *tmp;
+- struct dentry *child;
+-
+- tmp = this_parent->d_parent;
++ struct dentry *child = this_parent;
+ if (!(this_parent->d_flags & DCACHE_GENOCIDE)) {
+ this_parent->d_flags |= DCACHE_GENOCIDE;
+ this_parent->d_count--;
+ }
+- rcu_read_lock();
+- spin_unlock(&this_parent->d_lock);
+- child = this_parent;
+- this_parent = tmp;
+- spin_lock(&this_parent->d_lock);
+- /* might go back up the wrong parent if we have had a rename
+- * or deletion */
+- if (this_parent != child->d_parent ||
+- (!locked && read_seqretry(&rename_lock, seq))) {
+- spin_unlock(&this_parent->d_lock);
+- rcu_read_unlock();
++ this_parent = try_to_ascend(this_parent, locked, seq);
++ if (!this_parent)
+ goto rename_retry;
+- }
+- rcu_read_unlock();
+ next = child->d_u.d_child.next;
+ goto resume;
+ }
--- /dev/null
+From c83ce989cb5ff86575821992ea82c4df5c388ebc Mon Sep 17 00:00:00 2001
+From: Trond Myklebust <Trond.Myklebust@netapp.com>
+Date: Tue, 15 Mar 2011 13:36:43 -0400
+Subject: VFS: Fix the nfs sillyrename regression in kernel 2.6.38
+
+From: Trond Myklebust <Trond.Myklebust@netapp.com>
+
+commit c83ce989cb5ff86575821992ea82c4df5c388ebc upstream.
+
+The new vfs locking scheme introduced in 2.6.38 breaks NFS sillyrename
+because the latter relies on being able to determine the parent
+directory of the dentry in the ->iput() callback in order to send the
+appropriate unlink rpc call.
+
+Looking at the code that cares about races with dput(), there doesn't
+seem to be anything that specifically uses d_parent as a test for
+whether or not there is a race:
+ - __d_lookup_rcu(), __d_lookup() all test for d_hashed() after d_parent
+ - shrink_dcache_for_umount() is safe since nothing else can rearrange
+ the dentries in that super block.
+ - have_submount(), select_parent() and d_genocide() can test for a
+ deletion if we set the DCACHE_DISCONNECTED flag when the dentry
+ is removed from the parent's d_subdirs list.
+
+Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/dcache.c | 7 ++++++-
+ 1 file changed, 6 insertions(+), 1 deletion(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -296,8 +296,12 @@ static struct dentry *d_kill(struct dent
+ __releases(parent->d_lock)
+ __releases(dentry->d_inode->i_lock)
+ {
+- dentry->d_parent = NULL;
+ list_del(&dentry->d_u.d_child);
++ /*
++ * Inform try_to_ascend() that we are no longer attached to the
++ * dentry tree
++ */
++ dentry->d_flags |= DCACHE_DISCONNECTED;
+ if (parent)
+ spin_unlock(&parent->d_lock);
+ dentry_iput(dentry);
+@@ -1030,6 +1034,7 @@ static struct dentry *try_to_ascend(stru
+ * or deletion
+ */
+ if (new != old->d_parent ||
++ (old->d_flags & DCACHE_DISCONNECTED) ||
+ (!locked && read_seqretry(&rename_lock, seq))) {
+ spin_unlock(&new->d_lock);
+ new = NULL;