]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
.38 patches
authorGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Mar 2011 23:42:30 +0000 (16:42 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Tue, 15 Mar 2011 23:42:30 +0000 (16:42 -0700)
queue-2.6.38/dcache.c-create-helper-function-for-duplicated-functionality.patch [new file with mode: 0644]
queue-2.6.38/series [new file with mode: 0644]
queue-2.6.38/vfs-fix-the-nfs-sillyrename-regression-in-kernel-2.6.38.patch [new file with mode: 0644]

diff --git a/queue-2.6.38/dcache.c-create-helper-function-for-duplicated-functionality.patch b/queue-2.6.38/dcache.c-create-helper-function-for-duplicated-functionality.patch
new file mode 100644 (file)
index 0000000..124978d
--- /dev/null
@@ -0,0 +1,146 @@
+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;
+       }
diff --git a/queue-2.6.38/series b/queue-2.6.38/series
new file mode 100644 (file)
index 0000000..bfc22de
--- /dev/null
@@ -0,0 +1,2 @@
+dcache.c-create-helper-function-for-duplicated-functionality.patch
+vfs-fix-the-nfs-sillyrename-regression-in-kernel-2.6.38.patch
diff --git a/queue-2.6.38/vfs-fix-the-nfs-sillyrename-regression-in-kernel-2.6.38.patch b/queue-2.6.38/vfs-fix-the-nfs-sillyrename-regression-in-kernel-2.6.38.patch
new file mode 100644 (file)
index 0000000..c66e6e7
--- /dev/null
@@ -0,0 +1,56 @@
+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;