]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
3.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 14 Aug 2015 17:03:18 +0000 (10:03 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 14 Aug 2015 17:03:18 +0000 (10:03 -0700)
added patches:
dcache-don-t-need-rcu-in-shrink_dentry_list.patch
dentry_kill-don-t-try-to-remove-from-shrink-list.patch
don-t-remove-from-shrink-list-in-select_collect.patch
kvm-x86-fix-kvm_apic_has_events-to-check-for-null-pointer.patch
more-graceful-recovery-in-umount_collect.patch
path_openat-fix-double-fput.patch

queue-3.14/dcache-don-t-need-rcu-in-shrink_dentry_list.patch [new file with mode: 0644]
queue-3.14/dentry_kill-don-t-try-to-remove-from-shrink-list.patch [new file with mode: 0644]
queue-3.14/don-t-remove-from-shrink-list-in-select_collect.patch [new file with mode: 0644]
queue-3.14/kvm-x86-fix-kvm_apic_has_events-to-check-for-null-pointer.patch [new file with mode: 0644]
queue-3.14/more-graceful-recovery-in-umount_collect.patch [new file with mode: 0644]
queue-3.14/path_openat-fix-double-fput.patch [new file with mode: 0644]
queue-3.14/series

diff --git a/queue-3.14/dcache-don-t-need-rcu-in-shrink_dentry_list.patch b/queue-3.14/dcache-don-t-need-rcu-in-shrink_dentry_list.patch
new file mode 100644 (file)
index 0000000..e4fc561
--- /dev/null
@@ -0,0 +1,85 @@
+From 60942f2f235ce7b817166cdf355eed729094834d Mon Sep 17 00:00:00 2001
+From: Miklos Szeredi <mszeredi@suse.cz>
+Date: Fri, 2 May 2014 15:38:39 -0400
+Subject: dcache: don't need rcu in shrink_dentry_list()
+
+From: Miklos Szeredi <mszeredi@suse.cz>
+
+commit 60942f2f235ce7b817166cdf355eed729094834d upstream.
+
+Since now the shrink list is private and nobody can free the dentry while
+it is on the shrink list, we can remove RCU protection from this.
+
+Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Cc: "Nicholas A. Bellinger" <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/dcache.c |   27 ++++-----------------------
+ 1 file changed, 4 insertions(+), 23 deletions(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -797,23 +797,9 @@ static void shrink_dentry_list(struct li
+ {
+       struct dentry *dentry, *parent;
+-      rcu_read_lock();
+-      for (;;) {
+-              dentry = list_entry_rcu(list->prev, struct dentry, d_lru);
+-              if (&dentry->d_lru == list)
+-                      break; /* empty */
+-
+-              /*
+-               * Get the dentry lock, and re-verify that the dentry is
+-               * this on the shrinking list. If it is, we know that
+-               * DCACHE_SHRINK_LIST and DCACHE_LRU_LIST are set.
+-               */
++      while (!list_empty(list)) {
++              dentry = list_entry(list->prev, struct dentry, d_lru);
+               spin_lock(&dentry->d_lock);
+-              if (dentry != list_entry(list->prev, struct dentry, d_lru)) {
+-                      spin_unlock(&dentry->d_lock);
+-                      continue;
+-              }
+-
+               /*
+                * The dispose list is isolated and dentries are not accounted
+                * to the LRU here, so we can simply remove it from the list
+@@ -829,23 +815,20 @@ static void shrink_dentry_list(struct li
+                       spin_unlock(&dentry->d_lock);
+                       continue;
+               }
+-              rcu_read_unlock();
+               parent = dentry_kill(dentry, 0);
+               /*
+                * If dentry_kill returns NULL, we have nothing more to do.
+                */
+-              if (!parent) {
+-                      rcu_read_lock();
++              if (!parent)
+                       continue;
+-              }
++
+               if (unlikely(parent == dentry)) {
+                       /*
+                        * trylocks have failed and d_lock has been held the
+                        * whole time, so it could not have been added to any
+                        * other lists. Just add it back to the shrink list.
+                        */
+-                      rcu_read_lock();
+                       d_shrink_add(dentry, list);
+                       spin_unlock(&dentry->d_lock);
+                       continue;
+@@ -859,9 +842,7 @@ static void shrink_dentry_list(struct li
+               dentry = parent;
+               while (dentry && !lockref_put_or_lock(&dentry->d_lockref))
+                       dentry = dentry_kill(dentry, 1);
+-              rcu_read_lock();
+       }
+-      rcu_read_unlock();
+ }
+ static enum lru_status
diff --git a/queue-3.14/dentry_kill-don-t-try-to-remove-from-shrink-list.patch b/queue-3.14/dentry_kill-don-t-try-to-remove-from-shrink-list.patch
new file mode 100644 (file)
index 0000000..a50163f
--- /dev/null
@@ -0,0 +1,108 @@
+From 41edf278fc2f042f4e22a12ed87d19c5201210e1 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Thu, 1 May 2014 10:30:00 -0400
+Subject: dentry_kill(): don't try to remove from shrink list
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+commit 41edf278fc2f042f4e22a12ed87d19c5201210e1 upstream.
+
+If the victim in on the shrink list, don't remove it from there.
+If shrink_dentry_list() manages to remove it from the list before
+we are done - fine, we'll just free it as usual.  If not - mark
+it with new flag (DCACHE_MAY_FREE) and leave it there.
+
+Eventually, shrink_dentry_list() will get to it, remove the sucker
+from shrink list and call dentry_kill(dentry, 0).  Which is where
+we'll deal with freeing.
+
+Since now dentry_kill(dentry, 0) may happen after or during
+dentry_kill(dentry, 1), we need to recognize that (by seeing
+DCACHE_DENTRY_KILLED already set), unlock everything
+and either free the sucker (in case DCACHE_MAY_FREE has been
+set) or leave it for ongoing dentry_kill(dentry, 1) to deal with.
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Cc: "Nicholas A. Bellinger" <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/dcache.c            |   27 +++++++++++++++++++--------
+ include/linux/dcache.h |    2 ++
+ 2 files changed, 21 insertions(+), 8 deletions(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -466,7 +466,14 @@ dentry_kill(struct dentry *dentry, int u
+       __releases(dentry->d_lock)
+ {
+       struct inode *inode;
+-      struct dentry *parent;
++      struct dentry *parent = NULL;
++      bool can_free = true;
++
++      if (unlikely(dentry->d_flags & DCACHE_DENTRY_KILLED)) {
++              can_free = dentry->d_flags & DCACHE_MAY_FREE;
++              spin_unlock(&dentry->d_lock);
++              goto out;
++      }
+       inode = dentry->d_inode;
+       if (inode && !spin_trylock(&inode->i_lock)) {
+@@ -477,9 +484,7 @@ relock:
+               }
+               return dentry; /* try again with same dentry */
+       }
+-      if (IS_ROOT(dentry))
+-              parent = NULL;
+-      else
++      if (!IS_ROOT(dentry))
+               parent = dentry->d_parent;
+       if (parent && !spin_trylock(&parent->d_lock)) {
+               if (inode)
+@@ -502,8 +507,6 @@ relock:
+       if (dentry->d_flags & DCACHE_LRU_LIST) {
+               if (!(dentry->d_flags & DCACHE_SHRINK_LIST))
+                       d_lru_del(dentry);
+-              else
+-                      d_shrink_del(dentry);
+       }
+       /* if it was on the hash then remove it */
+       __d_drop(dentry);
+@@ -525,7 +528,15 @@ relock:
+       if (dentry->d_op && dentry->d_op->d_release)
+               dentry->d_op->d_release(dentry);
+-      dentry_free(dentry);
++      spin_lock(&dentry->d_lock);
++      if (dentry->d_flags & DCACHE_SHRINK_LIST) {
++              dentry->d_flags |= DCACHE_MAY_FREE;
++              can_free = false;
++      }
++      spin_unlock(&dentry->d_lock);
++out:
++      if (likely(can_free))
++              dentry_free(dentry);
+       return parent;
+ }
+@@ -830,7 +841,7 @@ static void shrink_dentry_list(struct li
+                * We found an inuse dentry which was not removed from
+                * the LRU because of laziness during lookup. Do not free it.
+                */
+-              if (dentry->d_lockref.count) {
++              if ((int)dentry->d_lockref.count > 0) {
+                       spin_unlock(&dentry->d_lock);
+                       continue;
+               }
+--- a/include/linux/dcache.h
++++ b/include/linux/dcache.h
+@@ -221,6 +221,8 @@ struct dentry_operations {
+ #define DCACHE_SYMLINK_TYPE           0x00300000 /* Symlink */
+ #define DCACHE_FILE_TYPE              0x00400000 /* Other file type */
++#define DCACHE_MAY_FREE                       0x00800000
++
+ extern seqlock_t rename_lock;
+ static inline int dname_external(const struct dentry *dentry)
diff --git a/queue-3.14/don-t-remove-from-shrink-list-in-select_collect.patch b/queue-3.14/don-t-remove-from-shrink-list-in-select_collect.patch
new file mode 100644 (file)
index 0000000..1075602
--- /dev/null
@@ -0,0 +1,71 @@
+From fe91522a7ba82ca1a51b07e19954b3825e4aaa22 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Sat, 3 May 2014 00:02:25 -0400
+Subject: don't remove from shrink list in select_collect()
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+commit fe91522a7ba82ca1a51b07e19954b3825e4aaa22 upstream.
+
+       If we find something already on a shrink list, just increment
+data->found and do nothing else.  Loops in shrink_dcache_parent() and
+check_submounts_and_drop() will do the right thing - everything we
+did put into our list will be evicted and if there had been nothing,
+but data->found got non-zero, well, we have somebody else shrinking
+those guys; just try again.
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Cc: "Nicholas A. Bellinger" <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/dcache.c |   31 ++++++++++---------------------
+ 1 file changed, 10 insertions(+), 21 deletions(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -1231,34 +1231,23 @@ static enum d_walk_ret select_collect(vo
+       if (data->start == dentry)
+               goto out;
+-      /*
+-       * move only zero ref count dentries to the dispose list.
+-       *
+-       * Those which are presently on the shrink list, being processed
+-       * by shrink_dentry_list(), shouldn't be moved.  Otherwise the
+-       * loop in shrink_dcache_parent() might not make any progress
+-       * and loop forever.
+-       */
+-      if (dentry->d_lockref.count) {
+-              dentry_lru_del(dentry);
+-      } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
+-              /*
+-               * We can't use d_lru_shrink_move() because we
+-               * need to get the global LRU lock and do the
+-               * LRU accounting.
+-               */
+-              d_lru_del(dentry);
+-              d_shrink_add(dentry, &data->dispose);
++      if (dentry->d_flags & DCACHE_SHRINK_LIST) {
+               data->found++;
+-              ret = D_WALK_NORETRY;
++      } else {
++              if (dentry->d_flags & DCACHE_LRU_LIST)
++                      d_lru_del(dentry);
++              if (!dentry->d_lockref.count) {
++                      d_shrink_add(dentry, &data->dispose);
++                      data->found++;
++              }
+       }
+       /*
+        * We can return to the caller if we have found some (this
+        * ensures forward progress). We'll be coming back to find
+        * the rest.
+        */
+-      if (data->found && need_resched())
+-              ret = D_WALK_QUIT;
++      if (!list_empty(&data->dispose))
++              ret = need_resched() ? D_WALK_QUIT : D_WALK_NORETRY;
+ out:
+       return ret;
+ }
diff --git a/queue-3.14/kvm-x86-fix-kvm_apic_has_events-to-check-for-null-pointer.patch b/queue-3.14/kvm-x86-fix-kvm_apic_has_events-to-check-for-null-pointer.patch
new file mode 100644 (file)
index 0000000..e264826
--- /dev/null
@@ -0,0 +1,31 @@
+From ce40cd3fc7fa40a6119e5fe6c0f2bc0eb4541009 Mon Sep 17 00:00:00 2001
+From: Paolo Bonzini <pbonzini@redhat.com>
+Date: Sat, 30 May 2015 14:31:24 +0200
+Subject: kvm: x86: fix kvm_apic_has_events to check for NULL pointer
+
+From: Paolo Bonzini <pbonzini@redhat.com>
+
+commit ce40cd3fc7fa40a6119e5fe6c0f2bc0eb4541009 upstream.
+
+Malicious (or egregiously buggy) userspace can trigger it, but it
+should never happen in normal operation.
+
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+Signed-off-by: Wang Kai <morgan.wang@huawei.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ arch/x86/kvm/lapic.h |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/x86/kvm/lapic.h
++++ b/arch/x86/kvm/lapic.h
+@@ -165,7 +165,7 @@ static inline u16 apic_logical_id(struct
+ static inline bool kvm_apic_has_events(struct kvm_vcpu *vcpu)
+ {
+-      return vcpu->arch.apic->pending_events;
++      return kvm_vcpu_has_lapic(vcpu) && vcpu->arch.apic->pending_events;
+ }
+ bool kvm_apic_pending_eoi(struct kvm_vcpu *vcpu, int vector);
diff --git a/queue-3.14/more-graceful-recovery-in-umount_collect.patch b/queue-3.14/more-graceful-recovery-in-umount_collect.patch
new file mode 100644 (file)
index 0000000..a381a3e
--- /dev/null
@@ -0,0 +1,165 @@
+From 9c8c10e262e0f62cb2530f1b076de979123183dd Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Fri, 2 May 2014 20:36:10 -0400
+Subject: more graceful recovery in umount_collect()
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+commit 9c8c10e262e0f62cb2530f1b076de979123183dd upstream.
+
+Start with shrink_dcache_parent(), then scan what remains.
+
+First of all, BUG() is very much an overkill here; we are holding
+->s_umount, and hitting BUG() means that a lot of interesting stuff
+will be hanging after that point (sync(2), for example).  Moreover,
+in cases when there had been more than one leak, we'll be better
+off reporting all of them.  And more than just the last component
+of pathname - %pd is there for just such uses...
+
+That was the last user of dentry_lru_del(), so kill it off...
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Cc: "Nicholas A. Bellinger" <nab@linux-iscsi.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/dcache.c |  101 ++++++++++++++----------------------------------------------
+ 1 file changed, 25 insertions(+), 76 deletions(-)
+
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -393,22 +393,6 @@ static void dentry_lru_add(struct dentry
+               d_lru_add(dentry);
+ }
+-/*
+- * Remove a dentry with references from the LRU.
+- *
+- * If we are on the shrink list, then we can get to try_prune_one_dentry() and
+- * lose our last reference through the parent walk. In this case, we need to
+- * remove ourselves from the shrink list, not the LRU.
+- */
+-static void dentry_lru_del(struct dentry *dentry)
+-{
+-      if (dentry->d_flags & DCACHE_LRU_LIST) {
+-              if (dentry->d_flags & DCACHE_SHRINK_LIST)
+-                      return d_shrink_del(dentry);
+-              d_lru_del(dentry);
+-      }
+-}
+-
+ /**
+  * d_drop - drop a dentry
+  * @dentry: dentry to drop
+@@ -1277,45 +1261,35 @@ void shrink_dcache_parent(struct dentry
+ }
+ EXPORT_SYMBOL(shrink_dcache_parent);
+-static enum d_walk_ret umount_collect(void *_data, struct dentry *dentry)
++static enum d_walk_ret umount_check(void *_data, struct dentry *dentry)
+ {
+-      struct select_data *data = _data;
+-      enum d_walk_ret ret = D_WALK_CONTINUE;
++      /* it has busy descendents; complain about those instead */
++      if (!list_empty(&dentry->d_subdirs))
++              return D_WALK_CONTINUE;
++
++      /* root with refcount 1 is fine */
++      if (dentry == _data && dentry->d_lockref.count == 1)
++              return D_WALK_CONTINUE;
+-      if (dentry->d_lockref.count) {
+-              dentry_lru_del(dentry);
+-              if (likely(!list_empty(&dentry->d_subdirs)))
+-                      goto out;
+-              if (dentry == data->start && dentry->d_lockref.count == 1)
+-                      goto out;
+-              printk(KERN_ERR
+-                     "BUG: Dentry %p{i=%lx,n=%s}"
+-                     " still in use (%d)"
+-                     " [unmount of %s %s]\n",
++      printk(KERN_ERR "BUG: Dentry %p{i=%lx,n=%pd} "
++                      " still in use (%d) [unmount of %s %s]\n",
+                      dentry,
+                      dentry->d_inode ?
+                      dentry->d_inode->i_ino : 0UL,
+-                     dentry->d_name.name,
++                     dentry,
+                      dentry->d_lockref.count,
+                      dentry->d_sb->s_type->name,
+                      dentry->d_sb->s_id);
+-              BUG();
+-      } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) {
+-              /*
+-               * We can't use d_lru_shrink_move() because we
+-               * need to get the global LRU lock and do the
+-               * LRU accounting.
+-               */
+-              if (dentry->d_flags & DCACHE_LRU_LIST)
+-                      d_lru_del(dentry);
+-              d_shrink_add(dentry, &data->dispose);
+-              data->found++;
+-              ret = D_WALK_NORETRY;
+-      }
+-out:
+-      if (data->found && need_resched())
+-              ret = D_WALK_QUIT;
+-      return ret;
++      WARN_ON(1);
++      return D_WALK_CONTINUE;
++}
++
++static void do_one_tree(struct dentry *dentry)
++{
++      shrink_dcache_parent(dentry);
++      d_walk(dentry, dentry, umount_check, NULL);
++      d_drop(dentry);
++      dput(dentry);
+ }
+ /*
+@@ -1325,40 +1299,15 @@ void shrink_dcache_for_umount(struct sup
+ {
+       struct dentry *dentry;
+-      if (down_read_trylock(&sb->s_umount))
+-              BUG();
++      WARN(down_read_trylock(&sb->s_umount), "s_umount should've been locked");
+       dentry = sb->s_root;
+       sb->s_root = NULL;
+-      for (;;) {
+-              struct select_data data;
+-
+-              INIT_LIST_HEAD(&data.dispose);
+-              data.start = dentry;
+-              data.found = 0;
+-
+-              d_walk(dentry, &data, umount_collect, NULL);
+-              if (!data.found)
+-                      break;
+-
+-              shrink_dentry_list(&data.dispose);
+-              cond_resched();
+-      }
+-      d_drop(dentry);
+-      dput(dentry);
++      do_one_tree(dentry);
+       while (!hlist_bl_empty(&sb->s_anon)) {
+-              struct select_data data;
+-              dentry = hlist_bl_entry(hlist_bl_first(&sb->s_anon), struct dentry, d_hash);
+-
+-              INIT_LIST_HEAD(&data.dispose);
+-              data.start = NULL;
+-              data.found = 0;
+-
+-              d_walk(dentry, &data, umount_collect, NULL);
+-              if (data.found)
+-                      shrink_dentry_list(&data.dispose);
+-              cond_resched();
++              dentry = dget(hlist_bl_entry(hlist_bl_first(&sb->s_anon), struct dentry, d_hash));
++              do_one_tree(dentry);
+       }
+ }
diff --git a/queue-3.14/path_openat-fix-double-fput.patch b/queue-3.14/path_openat-fix-double-fput.patch
new file mode 100644 (file)
index 0000000..dc13599
--- /dev/null
@@ -0,0 +1,39 @@
+From f15133df088ecadd141ea1907f2c96df67c729f0 Mon Sep 17 00:00:00 2001
+From: Al Viro <viro@zeniv.linux.org.uk>
+Date: Fri, 8 May 2015 22:53:15 -0400
+Subject: path_openat(): fix double fput()
+
+From: Al Viro <viro@zeniv.linux.org.uk>
+
+commit f15133df088ecadd141ea1907f2c96df67c729f0 upstream.
+
+path_openat() jumps to the wrong place after do_tmpfile() - it has
+already done path_cleanup() (as part of path_lookupat() called by
+do_tmpfile()), so doing that again can lead to double fput().
+
+Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ fs/namei.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/fs/namei.c
++++ b/fs/namei.c
+@@ -3171,7 +3171,7 @@ static struct file *path_openat(int dfd,
+       if (unlikely(file->f_flags & __O_TMPFILE)) {
+               error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened);
+-              goto out;
++              goto out2;
+       }
+       error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base);
+@@ -3209,6 +3209,7 @@ out:
+               path_put(&nd->root);
+       if (base)
+               fput(base);
++out2:
+       if (!(opened & FILE_OPENED)) {
+               BUG_ON(!error);
+               put_filp(file);
index 89bcb3b3d17b468ecad33622b34ea0b6d14ad619..fa9cb49292ba05e8323ed3496317d105dd86b6d9 100644 (file)
@@ -34,3 +34,9 @@ fold-d_kill-and-d_free.patch
 fold-try_prune_one_dentry.patch
 new-helper-dentry_free.patch
 expand-the-call-of-dentry_lru_del-in-dentry_kill.patch
+dentry_kill-don-t-try-to-remove-from-shrink-list.patch
+don-t-remove-from-shrink-list-in-select_collect.patch
+more-graceful-recovery-in-umount_collect.patch
+dcache-don-t-need-rcu-in-shrink_dentry_list.patch
+kvm-x86-fix-kvm_apic_has_events-to-check-for-null-pointer.patch
+path_openat-fix-double-fput.patch