Skip to contentSkip to conversation

x86/perf: A CR4.PCE bugfix and clarification

PATCH New Andy Lutomirski 2 participants 5 messages Mar 16, 2017 – Mar 17, 2017

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

cover x86/perf: A CR4.PCE bugfix and clarification

Hi all-

Vince found a bug in our CR4.PCE handling. While investigating, I found what looked like another bug but actually wasn't.

Here's a fix for the bug (cc'd to stable) and a comment and lockdep annotation for why the other non-bug isn't actually a bug.

Andy Lutomirski (2):
x86/perf: Fix CR4.PCE propagation to use active_mm instead of mm x86/perf: Clarify why x86_pmu_event_mapped() isn't racy

 arch/x86/events/core.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)
Signature · 1 line
2.9.3
#1 lore raw

PATCH 1/2 x86/perf: Fix CR4.PCE propagation to use active_mm instead of mm

If one thread mmaps a perf event while another thread in the same mm is in some context where active_mm != mm (which can happen in the scheduler, for example), refresh_pce() would write the wrong value to CR4.PCE. This broke some PAPI tests.

Fixes 7911d3f7af14 ("perf/x86: Only allow rdpmc if a perf_event is mapped")
Reported-and-tested-by Vince Weaver <[email protected]>
---
 arch/x86/events/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/events/core.c +2 -2
index 349d4d17aa7f..4f564df73b8f 100644
@@ -2101,8 +2101,8 @@ static int x86_pmu_event_init(struct perf_event *event)
 
 static void refresh_pce(void *ignored)
 {
-	if (current->mm)
-		load_mm_cr4(current->mm);
+	if (current->active_mm)
+		load_mm_cr4(current->active_mm);
 }
 
 static void x86_pmu_event_mapped(struct perf_event *event)
Signature · 1 line
2.9.3
#2 lore raw

[tip:perf/urgent] x86/perf: Fix CR4.PCE propagation to use active_mm instead of mm

x86/perf: Fix CR4.PCE propagation to use active_mm instead of mm
Commit-ID:  5dc855d44c2ad960a86f593c60461f1ae1566b6d
Gitweb:     http://git.kernel.org/tip/5dc855d44c2ad960a86f593c60461f1ae1566b6d
Author:     Andy Lutomirski <[email protected]>
AuthorDate: Thu, 16 Mar 2017 12:59:39 -0700
Committer:  Ingo Molnar <[email protected]>
CommitDate: Fri, 17 Mar 2017 08:28:26 +0100

x86/perf: Fix CR4.PCE propagation to use active_mm instead of mm

If one thread mmaps a perf event while another thread in the same mm is in some context where active_mm != mm (which can happen in the scheduler, for example), refresh_pce() would write the wrong value to CR4.PCE. This broke some PAPI tests.

Reported-and-tested-by Vince Weaver <[email protected]>
Cc Alexander Shishkin <[email protected]>
Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc H. Peter Anvin <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Stephane Eranian <[email protected]>
Cc Thomas Gleixner <[email protected]>
Fixes 7911d3f7af14 ("perf/x86: Only allow rdpmc if a perf_event is mapped")
---
 arch/x86/events/core.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
arch/x86/events/core.c +2 -2
index 1635c0c..e07b36c 100644
@@ -2100,8 +2100,8 @@ static int x86_pmu_event_init(struct perf_event *event)
 
 static void refresh_pce(void *ignored)
 {
-	if (current->mm)
-		load_mm_cr4(current->mm);
+	if (current->active_mm)
+		load_mm_cr4(current->active_mm);
 }
 
 static void x86_pmu_event_mapped(struct perf_event *event)
#3 lore raw

PATCH 2/2 x86/perf: Clarify why x86_pmu_event_mapped() isn't racy

In reply to Andy Lutomirski

Naively, it looks racy, but mmap_sem saves it. Add a comment and a lockdep assertion.

---
 arch/x86/events/core.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/x86/events/core.c +12 -0
index 4f564df73b8f..2aa1ad194db2 100644
@@ -2110,6 +2110,18 @@ static void x86_pmu_event_mapped(struct perf_event *event)
 	if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
 		return;
 
+	/*
+	 * This function relies on not being called concurrently in two
+	 * tasks in the same mm.  Otherwise one task could observe
+	 * perf_rdpmc_allowed > 1 and return all the way back to
+	 * userspace with CR4.PCE clear while another task is still
+	 * doing on_each_cpu_mask() to propagate CR4.PCE.
+	 *
+	 * 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);
+
 	if (atomic_inc_return(&current->mm->context.perf_rdpmc_allowed) == 1)
 		on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
 }
Signature · 1 line
2.9.3
#4 lore raw

[tip:perf/urgent] x86/perf: Clarify why x86_pmu_event_mapped() isn't racy

x86/perf: Clarify why x86_pmu_event_mapped() isn't racy
Commit-ID:  4b07372a32c0c1505a7634ad7e607d83340ef645
Gitweb:     http://git.kernel.org/tip/4b07372a32c0c1505a7634ad7e607d83340ef645
Author:     Andy Lutomirski <[email protected]>
AuthorDate: Thu, 16 Mar 2017 12:59:40 -0700
Committer:  Ingo Molnar <[email protected]>
CommitDate: Fri, 17 Mar 2017 08:28:26 +0100

x86/perf: Clarify why x86_pmu_event_mapped() isn't racy

Naively, it looks racy, but ->mmap_sem saves it. Add a comment and a lockdep assertion.

Cc Alexander Shishkin <[email protected]>
Cc Arnaldo Carvalho de Melo <[email protected]>
Cc Borislav Petkov <[email protected]>
Cc H. Peter Anvin <[email protected]>
Cc Jiri Olsa <[email protected]>
Cc Linus Torvalds <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Stephane Eranian <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Vince Weaver <[email protected]>
---
 arch/x86/events/core.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)
arch/x86/events/core.c +12 -0
index e07b36c..183a972 100644
@@ -2109,6 +2109,18 @@ static void x86_pmu_event_mapped(struct perf_event *event)
 	if (!(event->hw.flags & PERF_X86_EVENT_RDPMC_ALLOWED))
 		return;
 
+	/*
+	 * This function relies on not being called concurrently in two
+	 * tasks in the same mm.  Otherwise one task could observe
+	 * perf_rdpmc_allowed > 1 and return all the way back to
+	 * userspace with CR4.PCE clear while another task is still
+	 * doing on_each_cpu_mask() to propagate CR4.PCE.
+	 *
+	 * 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);
+
 	if (atomic_inc_return(&current->mm->context.perf_rdpmc_allowed) == 1)
 		on_each_cpu_mask(mm_cpumask(current->mm), refresh_pce, NULL, 1);
 }