diff --git a/debian/changelog b/debian/changelog index 4b14fa9c4..1ba99121b 100644 --- a/debian/changelog +++ b/debian/changelog @@ -90,6 +90,7 @@ linux (4.1.6-1) UNRELEASED; urgency=medium [ Ben Hutchings ] * [i386] udeb: Make gpio_keys_polled and leds-gpio optional in input-modules as they are not built for the 686-pae flavour (fixes FTBFS) + * perf: Fix AUX buffer refcounting -- Aurelien Jarno Tue, 18 Aug 2015 11:35:05 +0200 diff --git a/debian/patches/bugfix/all/perf-fix-aux-buffer-refcounting.patch b/debian/patches/bugfix/all/perf-fix-aux-buffer-refcounting.patch new file mode 100644 index 000000000..180add888 --- /dev/null +++ b/debian/patches/bugfix/all/perf-fix-aux-buffer-refcounting.patch @@ -0,0 +1,135 @@ +From: Peter Zijlstra +Date: Thu, 18 Jun 2015 12:32:49 +0200 +Subject: perf: Fix AUX buffer refcounting +Origin: https://git.kernel.org/linus/57ffc5ca679f499f4704fd9b6a372916f59930ee + +Its currently possible to drop the last refcount to the aux buffer +from NMI context, which results in the expected fireworks. + +The refcounting needs a bigger overhaul, but to cure the immediate +problem, delay the freeing by using an irq_work. + +Reviewed-and-tested-by: Alexander Shishkin +Reported-by: Vince Weaver +Signed-off-by: Peter Zijlstra (Intel) +Cc: Arnaldo Carvalho de Melo +Cc: Linus Torvalds +Cc: Peter Zijlstra +Cc: Stephane Eranian +Cc: Thomas Gleixner +Link: http://lkml.kernel.org/r/20150618103249.GK19282@twins.programming.kicks-ass.net +Signed-off-by: Ingo Molnar +--- + kernel/events/core.c | 8 -------- + kernel/events/internal.h | 10 ++++++++++ + kernel/events/ring_buffer.c | 27 +++++++++++++++++++++++++-- + 3 files changed, 35 insertions(+), 10 deletions(-) + +--- a/kernel/events/core.c ++++ b/kernel/events/core.c +@@ -4381,14 +4381,6 @@ static void ring_buffer_wakeup(struct pe + rcu_read_unlock(); + } + +-static void rb_free_rcu(struct rcu_head *rcu_head) +-{ +- struct ring_buffer *rb; +- +- rb = container_of(rcu_head, struct ring_buffer, rcu_head); +- rb_free(rb); +-} +- + struct ring_buffer *ring_buffer_get(struct perf_event *event) + { + struct ring_buffer *rb; +--- a/kernel/events/internal.h ++++ b/kernel/events/internal.h +@@ -11,6 +11,7 @@ + struct ring_buffer { + atomic_t refcount; + struct rcu_head rcu_head; ++ struct irq_work irq_work; + #ifdef CONFIG_PERF_USE_VMALLOC + struct work_struct work; + int page_order; /* allocation order */ +@@ -55,6 +56,15 @@ struct ring_buffer { + }; + + extern void rb_free(struct ring_buffer *rb); ++ ++static inline void rb_free_rcu(struct rcu_head *rcu_head) ++{ ++ struct ring_buffer *rb; ++ ++ rb = container_of(rcu_head, struct ring_buffer, rcu_head); ++ rb_free(rb); ++} ++ + extern struct ring_buffer * + rb_alloc(int nr_pages, long watermark, int cpu, int flags); + extern void perf_event_wakeup(struct perf_event *event); +--- a/kernel/events/ring_buffer.c ++++ b/kernel/events/ring_buffer.c +@@ -221,6 +221,8 @@ void perf_output_end(struct perf_output_ + rcu_read_unlock(); + } + ++static void rb_irq_work(struct irq_work *work); ++ + static void + ring_buffer_init(struct ring_buffer *rb, long watermark, int flags) + { +@@ -241,6 +243,16 @@ ring_buffer_init(struct ring_buffer *rb, + + INIT_LIST_HEAD(&rb->event_list); + spin_lock_init(&rb->event_lock); ++ init_irq_work(&rb->irq_work, rb_irq_work); ++} ++ ++static void ring_buffer_put_async(struct ring_buffer *rb) ++{ ++ if (!atomic_dec_and_test(&rb->refcount)) ++ return; ++ ++ rb->rcu_head.next = (void *)rb; ++ irq_work_queue(&rb->irq_work); + } + + /* +@@ -319,7 +331,7 @@ err_put: + rb_free_aux(rb); + + err: +- ring_buffer_put(rb); ++ ring_buffer_put_async(rb); + handle->event = NULL; + + return NULL; +@@ -370,7 +382,7 @@ void perf_aux_output_end(struct perf_out + + local_set(&rb->aux_nest, 0); + rb_free_aux(rb); +- ring_buffer_put(rb); ++ ring_buffer_put_async(rb); + } + + /* +@@ -557,7 +569,18 @@ static void __rb_free_aux(struct ring_bu + void rb_free_aux(struct ring_buffer *rb) + { + if (atomic_dec_and_test(&rb->aux_refcount)) ++ irq_work_queue(&rb->irq_work); ++} ++ ++static void rb_irq_work(struct irq_work *work) ++{ ++ struct ring_buffer *rb = container_of(work, struct ring_buffer, irq_work); ++ ++ if (!atomic_read(&rb->aux_refcount)) + __rb_free_aux(rb); ++ ++ if (rb->rcu_head.next == (void *)rb) ++ call_rcu(&rb->rcu_head, rb_free_rcu); + } + + #ifndef CONFIG_PERF_USE_VMALLOC diff --git a/debian/patches/bugfix/all/perf-fix-double-free-of-the-aux-buffer.patch b/debian/patches/bugfix/all/perf-fix-double-free-of-the-aux-buffer.patch index 32125aea6..74a1b63e2 100644 --- a/debian/patches/bugfix/all/perf-fix-double-free-of-the-aux-buffer.patch +++ b/debian/patches/bugfix/all/perf-fix-double-free-of-the-aux-buffer.patch @@ -24,7 +24,7 @@ Signed-off-by: Ingo Molnar --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c -@@ -547,11 +547,13 @@ static void __rb_free_aux(struct ring_bu +@@ -559,11 +559,13 @@ static void __rb_free_aux(struct ring_bu rb->aux_priv = NULL; } diff --git a/debian/patches/series b/debian/patches/series index 60a3950cd..e54bbb606 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -90,4 +90,5 @@ bugfix/all/virtio-net-drop-netif_f_fraglist.patch bugfix/all/-ovl-don-t-traverse-automount-points.patch bugfix/all/ovl-allow-distributed-fs-as-lower-layer.patch bugfix/all/fix-a-braino-in-ovl_d_select_inode.patch +bugfix/all/perf-fix-aux-buffer-refcounting.patch bugfix/all/perf-fix-double-free-of-the-aux-buffer.patch