From: Greg Kroah-Hartman Date: Fri, 14 Aug 2015 17:03:18 +0000 (-0700) Subject: 3.14-stable patches X-Git-Tag: v3.10.87~9 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=99aaff700f2a1801215b68d05cb8137b73496c23;p=thirdparty%2Fkernel%2Fstable-queue.git 3.14-stable patches 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 --- 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 index 00000000000..e4fc5619cd7 --- /dev/null +++ b/queue-3.14/dcache-don-t-need-rcu-in-shrink_dentry_list.patch @@ -0,0 +1,85 @@ +From 60942f2f235ce7b817166cdf355eed729094834d Mon Sep 17 00:00:00 2001 +From: Miklos Szeredi +Date: Fri, 2 May 2014 15:38:39 -0400 +Subject: dcache: don't need rcu in shrink_dentry_list() + +From: Miklos Szeredi + +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 +Signed-off-by: Al Viro +Cc: "Nicholas A. Bellinger" +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..a50163fdf86 --- /dev/null +++ b/queue-3.14/dentry_kill-don-t-try-to-remove-from-shrink-list.patch @@ -0,0 +1,108 @@ +From 41edf278fc2f042f4e22a12ed87d19c5201210e1 Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Thu, 1 May 2014 10:30:00 -0400 +Subject: dentry_kill(): don't try to remove from shrink list + +From: Al Viro + +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 +Cc: "Nicholas A. Bellinger" +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..10756025600 --- /dev/null +++ b/queue-3.14/don-t-remove-from-shrink-list-in-select_collect.patch @@ -0,0 +1,71 @@ +From fe91522a7ba82ca1a51b07e19954b3825e4aaa22 Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Sat, 3 May 2014 00:02:25 -0400 +Subject: don't remove from shrink list in select_collect() + +From: Al Viro + +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 +Cc: "Nicholas A. Bellinger" +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..e2648262049 --- /dev/null +++ b/queue-3.14/kvm-x86-fix-kvm_apic_has_events-to-check-for-null-pointer.patch @@ -0,0 +1,31 @@ +From ce40cd3fc7fa40a6119e5fe6c0f2bc0eb4541009 Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +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 + +commit ce40cd3fc7fa40a6119e5fe6c0f2bc0eb4541009 upstream. + +Malicious (or egregiously buggy) userspace can trigger it, but it +should never happen in normal operation. + +Signed-off-by: Paolo Bonzini +Signed-off-by: Wang Kai +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..a381a3e03c5 --- /dev/null +++ b/queue-3.14/more-graceful-recovery-in-umount_collect.patch @@ -0,0 +1,165 @@ +From 9c8c10e262e0f62cb2530f1b076de979123183dd Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Fri, 2 May 2014 20:36:10 -0400 +Subject: more graceful recovery in umount_collect() + +From: Al Viro + +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 +Cc: "Nicholas A. Bellinger" +Signed-off-by: Greg Kroah-Hartman + +--- + 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 index 00000000000..dc135991c59 --- /dev/null +++ b/queue-3.14/path_openat-fix-double-fput.patch @@ -0,0 +1,39 @@ +From f15133df088ecadd141ea1907f2c96df67c729f0 Mon Sep 17 00:00:00 2001 +From: Al Viro +Date: Fri, 8 May 2015 22:53:15 -0400 +Subject: path_openat(): fix double fput() + +From: Al Viro + +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 +Signed-off-by: Greg Kroah-Hartman + +--- + 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); diff --git a/queue-3.14/series b/queue-3.14/series index 89bcb3b3d17..fa9cb49292b 100644 --- a/queue-3.14/series +++ b/queue-3.14/series @@ -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