Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1702322 > unrolled thread

Re: perf: bug in rdpmc/mmap accounting after exec

Started byPeter Zijlstra <peterz@infradead.org>
First post2017-08-02 19:40 +0200
Last post2017-08-10 14:10 +0200
Articles 6 — 4 participants

Back to article view | Back to linux.kernel

This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by below is the oldest one visible, not the original post.


Contents

  Re: perf: bug in rdpmc/mmap accounting after exec Peter Zijlstra <peterz@infradead.org> - 2017-08-02 19:40 +0200
    Re: perf: bug in rdpmc/mmap accounting after exec Vince Weaver <vincent.weaver@maine.edu> - 2017-08-03 20:10 +0200
    Re: perf: bug in rdpmc/mmap accounting after exec Andy Lutomirski <luto@kernel.org> - 2017-08-04 02:40 +0200
      Re: perf: bug in rdpmc/mmap accounting after exec Peter Zijlstra <peterz@infradead.org> - 2017-08-04 11:20 +0200
        Re: perf: bug in rdpmc/mmap accounting after exec Peter Zijlstra <peterz@infradead.org> - 2017-08-04 13:20 +0200
    [tip:perf/core] perf/x86: Fix RDPMC vs. mm_struct tracking tip-bot for Peter Zijlstra <tipbot@zytor.com> - 2017-08-10 14:10 +0200

#1702322 — Re: perf: bug in rdpmc/mmap accounting after exec

FromPeter Zijlstra <peterz@infradead.org>
Date2017-08-02 19:40 +0200
SubjectRe: perf: bug in rdpmc/mmap accounting after exec
Message-ID<ua5MK-4w6-13@gated-at.bofh.it>
On Wed, Jul 26, 2017 at 03:39:01PM -0400, Vince Weaver wrote:
> Hello
> 
> so one last bug found by the PAPI testsuite.
> 
> This one involves the rdpmc auto-disable on last unmap of an event 
> feature.
> 
> Failing test case:
> 
> 	fd=perf_event_open();
> 	addr=mmap(fd);
> 	exec()  // without closing or unmapping the event
> 	fd=perf_event_open();
> 	addr=mmap(fd);
> 	rdpmc()	// GPFs due to rdpmc being disabled
> 
> I won't pretend to be able to follow the rdpmc disabling code, but if I 
> add some printks it looks like 
> 	current->mm->context.perf_rdpmc_allowed
> isn't properly being reset on exec?
> 
> In fact, current->mm->context.perf_rdpmc_allowed goes negative which seems
> like it shouldn't happen?
> 
> Anyway, a test case for this can be found in the perf_event_tests,
> 	tests/rdpmc/rdpmc_exec_papi

Good find that...

The below seems to fix that for me.

---
diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 8e3db8f642a7..3186f6e081c4 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2114,11 +2114,14 @@ static void refresh_pce(void *ignored)
 	load_mm_cr4(this_cpu_read(cpu_tlbstate.loaded_mm));
 }
 
-static void x86_pmu_event_mapped(struct perf_event *event)
+static void x86_pmu_event_mapped(struct perf_event *event, struct vm_area_struct *vma)
 {
+	struct mm_struct *mm;
+
 	if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
 		return;
 
+	mm = vma->vm_mm;
 	/*
 	 * This function relies on not being called concurrently in two
 	 * tasks in the same mm.  Otherwise one task could observe
@@ -2129,22 +2132,21 @@ static void x86_pmu_event_mapped(struct perf_event *event)
 	 * For now, this can't happen because all callers hold mmap_sem
 	 * for write.  If this changes, we'll need a different solution.
 	 */
-	lockdep_assert_held_exclusive(&current->mm->mmap_sem);
+	lockdep_assert_held_exclusive(&mm->mmap_sem);
 
-	if (atomic_inc_return(&current->mm->context.perf_rdpmc_allowed) == 1)
-		on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
+	if (atomic_inc_return(&mm->context.perf_rdpmc_allowed) == 1)
+		on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
 }
 
-static void x86_pmu_event_unmapped(struct perf_event *event)
+static void x86_pmu_event_unmapped(struct perf_event *event, struct vm_area_struct *vma)
 {
-	if (!current->mm)
-		return;
+	struct mm_struct *mm = vma->vm_mm;
 
 	if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
 		return;
 
-	if (atomic_dec_and_test(&current->mm->context.perf_rdpmc_allowed))
-		on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
+	if (atomic_dec_and_test(&mm->context.perf_rdpmc_allowed))
+		on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
 }
 
 static int x86_pmu_event_idx(struct perf_event *event)
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index a3b873fc59e4..271a32a7e3e1 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -310,8 +310,8 @@ struct pmu {
 	 * Notification that the event was mapped or unmapped.  Called
 	 * in the context of the mapping task.
 	 */
-	void (*event_mapped)		(struct perf_event *event); /*optional*/
-	void (*event_unmapped)		(struct perf_event *event); /*optional*/
+	void (*event_mapped)		(struct perf_event *event, struct vm_area_struct *vma); /*optional*/
+	void (*event_unmapped)		(struct perf_event *event, struct vm_area_struct *vma); /*optional*/
 
 	/*
 	 * Flags for ->add()/->del()/ ->start()/->stop(). There are
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 426c2ffba16d..f724348bba08 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5090,7 +5105,7 @@ static void perf_mmap_open(struct vm_area_struct *vma)
 		atomic_inc(&event->rb->aux_mmap_count);
 
 	if (event->pmu->event_mapped)
-		event->pmu->event_mapped(event);
+		event->pmu->event_mapped(event, vma);
 }
 
 static void perf_pmu_output_stop(struct perf_event *event);
@@ -5113,7 +5128,7 @@ static void perf_mmap_close(struct vm_area_struct *vma)
 	unsigned long size = perf_data_size(rb);
 
 	if (event->pmu->event_unmapped)
-		event->pmu->event_unmapped(event);
+		event->pmu->event_unmapped(event, vma);
 
 	/*
 	 * rb->aux_mmap_count will always drop before rb->mmap_count and
@@ -5411,7 +5426,7 @@ static int perf_mmap(struct file *file, struct vm_area_struct *vma)
 	vma->vm_ops = &perf_mmap_vmops;
 
 	if (event->pmu->event_mapped)
-		event->pmu->event_mapped(event);
+		event->pmu->event_mapped(event, vma);
 
 	return ret;
 }

[toc] | [next] | [standalone]


#1703325

FromVince Weaver <vincent.weaver@maine.edu>
Date2017-08-03 20:10 +0200
Message-ID<uasJk-3EH-25@gated-at.bofh.it>
In reply to#1702322
On Wed, 2 Aug 2017, Peter Zijlstra wrote:

> On Wed, Jul 26, 2017 at 03:39:01PM -0400, Vince Weaver wrote:
> > In fact, current->mm->context.perf_rdpmc_allowed goes negative which seems
> > like it shouldn't happen?

>
> Good find that...
> 
> The below seems to fix that for me.

yes, I can confirm that with this patch all of the various PAPI and 
perf_event_tests pass on my test system.

Thanks,

Vince

[toc] | [prev] | [next] | [standalone]


#1703564

FromAndy Lutomirski <luto@kernel.org>
Date2017-08-04 02:40 +0200
Message-ID<uayOK-7Oe-3@gated-at.bofh.it>
In reply to#1702322
On Wed, Aug 2, 2017 at 10:39 AM, Peter Zijlstra <peterz@infradead.org> wrote:
> On Wed, Jul 26, 2017 at 03:39:01PM -0400, Vince Weaver wrote:
>> Hello
>>
>> so one last bug found by the PAPI testsuite.
>>
>> This one involves the rdpmc auto-disable on last unmap of an event
>> feature.
>>
>> Failing test case:
>>
>>       fd=perf_event_open();
>>       addr=mmap(fd);
>>       exec()  // without closing or unmapping the event
>>       fd=perf_event_open();
>>       addr=mmap(fd);
>>       rdpmc() // GPFs due to rdpmc being disabled
>>
>> I won't pretend to be able to follow the rdpmc disabling code, but if I
>> add some printks it looks like
>>       current->mm->context.perf_rdpmc_allowed
>> isn't properly being reset on exec?
>>
>> In fact, current->mm->context.perf_rdpmc_allowed goes negative which seems
>> like it shouldn't happen?
>>
>> Anyway, a test case for this can be found in the perf_event_tests,
>>       tests/rdpmc/rdpmc_exec_papi
>
> Good find that...
>
> The below seems to fix that for me.
>

...because execve plays funny games with non-current mms.  Whoops.

Reviewed-by: Andy Lutomirski <luto@kernel.org>

Can you send it upstream and tag it for stable?

[toc] | [prev] | [next] | [standalone]


#1703815

FromPeter Zijlstra <peterz@infradead.org>
Date2017-08-04 11:20 +0200
Message-ID<uaGVX-56P-7@gated-at.bofh.it>
In reply to#1703564
On Thu, Aug 03, 2017 at 05:37:22PM -0700, Andy Lutomirski wrote:
> Reviewed-by: Andy Lutomirski <luto@kernel.org>

Thanks!

> Can you send it upstream and tag it for stable?

Yeah, I'll make it pretty, write a changelog and things like that :-)

[toc] | [prev] | [next] | [standalone]


#1703878

FromPeter Zijlstra <peterz@infradead.org>
Date2017-08-04 13:20 +0200
Message-ID<uaIO5-6h5-3@gated-at.bofh.it>
In reply to#1703815
On Fri, Aug 04, 2017 at 11:17:02AM +0200, Peter Zijlstra wrote:
> Yeah, I'll make it pretty, write a changelog and things like that :-)

Slightly different from the earlier one in that I now pass @mm along
instead of @vma. Same thing otherwise.

---
Subject: perf,x86: Fix RDPMC vs mm_struct tracking
From: Peter Zijlstra <peterz@infradead.org>
Date: Wed, 2 Aug 2017 19:39:30 +0200

Vince reported:

> Failing test case:
>
> 	fd=perf_event_open();
> 	addr=mmap(fd);
> 	exec()  // without closing or unmapping the event
> 	fd=perf_event_open();
> 	addr=mmap(fd);
> 	rdpmc()	// GPFs due to rdpmc being disabled

The problem is of course that exec() plays tricks with what is
current->mm, only destroying the old mappings after having installed
the new mm.

Fix this confusion by passing along vma->vm_mm instead of relying on
current->mm.

Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Stephane Eranian <eranian@gmail.com>
Cc: stable@vger.kernel.org
Reported-by: Vince Weaver <vincent.weaver@maine.edu>
Tested-by: Vince Weaver <vincent.weaver@maine.edu>
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Fixes: 1e0fb9ec679c ("perf: Add pmu callbacks to track event mapping and unmapping")
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: http://lkml.kernel.org/r/20170802173930.cstykcqefmqt7jau@hirez.programming.kicks-ass.net
---
 arch/x86/events/core.c     |   16 +++++++---------
 include/linux/perf_event.h |    4 ++--
 kernel/events/core.c       |    6 +++---
 3 files changed, 12 insertions(+), 14 deletions(-)

--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2114,7 +2114,7 @@ static void refresh_pce(void *ignored)
 	load_mm_cr4(this_cpu_read(cpu_tlbstate.loaded_mm));
 }
 
-static void x86_pmu_event_mapped(struct perf_event *event)
+static void x86_pmu_event_mapped(struct perf_event *event, struct mm_struct *mm)
 {
 	if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
 		return;
@@ -2129,22 +2129,20 @@ static void x86_pmu_event_mapped(struct
 	 * For now, this can't happen because all callers hold mmap_sem
 	 * for write.  If this changes, we'll need a different solution.
 	 */
-	lockdep_assert_held_exclusive(&current->mm->mmap_sem);
+	lockdep_assert_held_exclusive(&mm->mmap_sem);
 
-	if (atomic_inc_return(&current->mm->context.perf_rdpmc_allowed) == 1)
-		on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
+	if (atomic_inc_return(&mm->context.perf_rdpmc_allowed) == 1)
+		on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
 }
 
-static void x86_pmu_event_unmapped(struct perf_event *event)
+static void x86_pmu_event_unmapped(struct perf_event *event, struct mm_struct *mm)
 {
-	if (!current->mm)
-		return;
 
 	if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
 		return;
 
-	if (atomic_dec_and_test(&current->mm->context.perf_rdpmc_allowed))
-		on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
+	if (atomic_dec_and_test(&mm->context.perf_rdpmc_allowed))
+		on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
 }
 
 static int x86_pmu_event_idx(struct perf_event *event)
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -310,8 +310,8 @@ struct pmu {
 	 * Notification that the event was mapped or unmapped.  Called
 	 * in the context of the mapping task.
 	 */
-	void (*event_mapped)		(struct perf_event *event); /*optional*/
-	void (*event_unmapped)		(struct perf_event *event); /*optional*/
+	void (*event_mapped)		(struct perf_event *event, struct mm_struct *mm); /*optional*/
+	void (*event_unmapped)		(struct perf_event *event, struct mm_struct *mm); /*optional*/
 
 	/*
 	 * Flags for ->add()/->del()/ ->start()/->stop(). There are
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5090,7 +5090,7 @@ static void perf_mmap_open(struct vm_are
 		atomic_inc(&event->rb->aux_mmap_count);
 
 	if (event->pmu->event_mapped)
-		event->pmu->event_mapped(event);
+		event->pmu->event_mapped(event, vma->vm_mm);
 }
 
 static void perf_pmu_output_stop(struct perf_event *event);
@@ -5113,7 +5113,7 @@ static void perf_mmap_close(struct vm_ar
 	unsigned long size = perf_data_size(rb);
 
 	if (event->pmu->event_unmapped)
-		event->pmu->event_unmapped(event);
+		event->pmu->event_unmapped(event, vma->vm_mm);
 
 	/*
 	 * rb->aux_mmap_count will always drop before rb->mmap_count and
@@ -5411,7 +5411,7 @@ static int perf_mmap(struct file *file,
 	vma->vm_ops = &perf_mmap_vmops;
 
 	if (event->pmu->event_mapped)
-		event->pmu->event_mapped(event);
+		event->pmu->event_mapped(event, vma->vm_mm);
 
 	return ret;
 }

[toc] | [prev] | [next] | [standalone]


#1708496 — [tip:perf/core] perf/x86: Fix RDPMC vs. mm_struct tracking

Fromtip-bot for Peter Zijlstra <tipbot@zytor.com>
Date2017-08-10 14:10 +0200
Subject[tip:perf/core] perf/x86: Fix RDPMC vs. mm_struct tracking
Message-ID<ucUrN-5pA-21@gated-at.bofh.it>
In reply to#1702322
Commit-ID:  bfe334924ccd9f4a53f30240c03cf2f43f5b2df1
Gitweb:     http://git.kernel.org/tip/bfe334924ccd9f4a53f30240c03cf2f43f5b2df1
Author:     Peter Zijlstra <peterz@infradead.org>
AuthorDate: Wed, 2 Aug 2017 19:39:30 +0200
Committer:  Ingo Molnar <mingo@kernel.org>
CommitDate: Thu, 10 Aug 2017 12:01:08 +0200

perf/x86: Fix RDPMC vs. mm_struct tracking

Vince reported the following rdpmc() testcase failure:

 > Failing test case:
 >
 >	fd=perf_event_open();
 >	addr=mmap(fd);
 >	exec()  // without closing or unmapping the event
 >	fd=perf_event_open();
 >	addr=mmap(fd);
 >	rdpmc()	// GPFs due to rdpmc being disabled

The problem is of course that exec() plays tricks with what is
current->mm, only destroying the old mappings after having
installed the new mm.

Fix this confusion by passing along vma->vm_mm instead of relying on
current->mm.

Reported-by: Vince Weaver <vincent.weaver@maine.edu>
Tested-by: Vince Weaver <vincent.weaver@maine.edu>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@gmail.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
Fixes: 1e0fb9ec679c ("perf: Add pmu callbacks to track event mapping and unmapping")
Link: http://lkml.kernel.org/r/20170802173930.cstykcqefmqt7jau@hirez.programming.kicks-ass.net
[ Minor cleanups. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 arch/x86/events/core.c     | 16 +++++++---------
 include/linux/perf_event.h |  4 ++--
 kernel/events/core.c       |  6 +++---
 3 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c
index 8e3db8f..af12e29 100644
--- a/arch/x86/events/core.c
+++ b/arch/x86/events/core.c
@@ -2114,7 +2114,7 @@ static void refresh_pce(void *ignored)
 	load_mm_cr4(this_cpu_read(cpu_tlbstate.loaded_mm));
 }
 
-static void x86_pmu_event_mapped(struct perf_event *event)
+static void x86_pmu_event_mapped(struct perf_event *event, struct mm_struct *mm)
 {
 	if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
 		return;
@@ -2129,22 +2129,20 @@ static void x86_pmu_event_mapped(struct perf_event *event)
 	 * For now, this can't happen because all callers hold mmap_sem
 	 * for write.  If this changes, we'll need a different solution.
 	 */
-	lockdep_assert_held_exclusive(&current->mm->mmap_sem);
+	lockdep_assert_held_exclusive(&mm->mmap_sem);
 
-	if (atomic_inc_return(&current->mm->context.perf_rdpmc_allowed) == 1)
-		on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
+	if (atomic_inc_return(&mm->context.perf_rdpmc_allowed) == 1)
+		on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
 }
 
-static void x86_pmu_event_unmapped(struct perf_event *event)
+static void x86_pmu_event_unmapped(struct perf_event *event, struct mm_struct *mm)
 {
-	if (!current->mm)
-		return;
 
 	if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
 		return;
 
-	if (atomic_dec_and_test(&current->mm->context.perf_rdpmc_allowed))
-		on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
+	if (atomic_dec_and_test(&mm->context.perf_rdpmc_allowed))
+		on_each_cpu_mask(mm_cpumask(mm), refresh_pce, NULL, 1);
 }
 
 static int x86_pmu_event_idx(struct perf_event *event)
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index a3b873f..b14095b 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -310,8 +310,8 @@ struct pmu {
 	 * Notification that the event was mapped or unmapped.  Called
 	 * in the context of the mapping task.
 	 */
-	void (*event_mapped)		(struct perf_event *event); /*optional*/
-	void (*event_unmapped)		(struct perf_event *event); /*optional*/
+	void (*event_mapped)		(struct perf_event *event, struct mm_struct *mm); /* optional */
+	void (*event_unmapped)		(struct perf_event *event, struct mm_struct *mm); /* optional */
 
 	/*
 	 * Flags for ->add()/->del()/ ->start()/->stop(). There are
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 426c2ff..a654b8a 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -5090,7 +5090,7 @@ static void perf_mmap_open(struct vm_area_struct *vma)
 		atomic_inc(&event->rb->aux_mmap_count);
 
 	if (event->pmu->event_mapped)
-		event->pmu->event_mapped(event);
+		event->pmu->event_mapped(event, vma->vm_mm);
 }
 
 static void perf_pmu_output_stop(struct perf_event *event);
@@ -5113,7 +5113,7 @@ static void perf_mmap_close(struct vm_area_struct *vma)
 	unsigned long size = perf_data_size(rb);
 
 	if (event->pmu->event_unmapped)
-		event->pmu->event_unmapped(event);
+		event->pmu->event_unmapped(event, vma->vm_mm);
 
 	/*
 	 * rb->aux_mmap_count will always drop before rb->mmap_count and
@@ -5411,7 +5411,7 @@ aux_unlock:
 	vma->vm_ops = &perf_mmap_vmops;
 
 	if (event->pmu->event_mapped)
-		event->pmu->event_mapped(event);
+		event->pmu->event_mapped(event, vma->vm_mm);
 
 	return ret;
 }

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web