All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] perf,x86: Fix shared registers mutual exclusion bug
@ 2013-06-05 14:43 Stephane Eranian
  2013-06-18  9:11 ` Peter Zijlstra
  0 siblings, 1 reply; 4+ messages in thread
From: Stephane Eranian @ 2013-06-05 14:43 UTC (permalink / raw)
  To: linux-kernel; +Cc: peterz, mingo, jolsa, vincent.weaver, ak


This patch fixes a problem with the shared registers mutual
exclusion code and incremental event scheduling by the
generic perf_event code.

There was a bug whereby the mutual exclusion on the shared
registers was not enforced because of incremental scheduling
abort due to event constraints.

Example on Nehalem:
group1= ref-cycles,OFFCORE_RESPONSE_0:PF_RFO
group2= ref-cycles

The ref-cycles event can only be measured by 1 counter. Yet, there
are 2 instances here. The first group can be scheduled and is committed.
Then, the generic code tries to schedule group2 and this fails (because
there is no more counter to support the 2nd instance of ref-cycles).

But in x86_schedule_events() error path, put_event_contraints() is invoked
on ALL the events and not just the ones that just failed. That causes the
"lock" on the shared offcore_response MSR to be released. Yet the first group
is actually scheduled and is exposed to reprogramming of that shared msr by
the sibling HT thread (when they are shared by HT threads). In other words,
there is no guarantee on what is measured for the offcore_response event.

This patch fixes the problem by tagging committed events with the
PERF_X86_EVENT_COMMITTED tag. In the error path of x86_schedule_events(),
only the events NOT tagged have their constraint released. The tag
is eventually removed when the event in descheduled.

Example was given with offcore_response but also applies to LBR_SELECT
and LDLAT shared registers.

Signed-off-by: Stephane Eranian <eranian@google.com>
---
 arch/x86/kernel/cpu/perf_event.c       |   26 +++++++++++++++++++++++++-
 arch/x86/kernel/cpu/perf_event.h       |    9 +++++----
 arch/x86/kernel/cpu/perf_event_intel.c |    2 --
 include/linux/perf_event.h             |    3 ++-
 4 files changed, 32 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index 1025f3c..d2ff6b8 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -726,6 +726,7 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
 {
 	struct event_constraint *c, *constraints[X86_PMC_IDX_MAX];
 	unsigned long used_mask[BITS_TO_LONGS(X86_PMC_IDX_MAX)];
+	struct perf_event *e;
 	int i, wmin, wmax, num = 0;
 	struct hw_perf_event *hwc;
 
@@ -767,13 +768,31 @@ int x86_schedule_events(struct cpu_hw_events *cpuc, int n, int *assign)
 		num = perf_assign_events(constraints, n, wmin, wmax, assign);
 
 	/*
+	 * Mark the event as committed, so we do not put_contraint
+	 * in case new events are added and fail scheduling.
+	 */
+	if (!num && assign) {
+		for (i = 0; i < n; i++) {
+			e = cpuc->event_list[i];
+			e->hw.flags |= PERF_X86_EVENT_COMMITTED;
+		}
+	}
+	/*
 	 * scheduling failed or is just a simulation,
 	 * free resources if necessary
 	 */
 	if (!assign || num) {
 		for (i = 0; i < n; i++) {
+			e = cpuc->event_list[i];
+			/*
+			 * do not put_contraint() on comitted events,
+			 * because they are good to go
+			 */
+			if ((e->hw.flags & PERF_X86_EVENT_COMMITTED))
+				continue;
+
 			if (x86_pmu.put_event_constraints)
-				x86_pmu.put_event_constraints(cpuc, cpuc->event_list[i]);
+				x86_pmu.put_event_constraints(cpuc, e);
 		}
 	}
 	return num ? -EINVAL : 0;
@@ -1153,6 +1172,11 @@ static void x86_pmu_del(struct perf_event *event, int flags)
 	int i;
 
 	/*
+	 * event is descheduled
+	 */
+	event->hw.flags &= ~PERF_X86_EVENT_COMMITTED;
+
+	/*
 	 * If we're called during a txn, we don't need to do anything.
 	 * The events never got scheduled and ->cancel_txn will truncate
 	 * the event_list.
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
index ba9aadf..571fadb 100644
--- a/arch/x86/kernel/cpu/perf_event.h
+++ b/arch/x86/kernel/cpu/perf_event.h
@@ -60,13 +60,14 @@ struct event_constraint {
 	u64	cmask;
 	int	weight;
 	int	overlap;
-	int	flags;
+	int	flags; /* hw.flags from constraint */
 };
 /*
- * struct event_constraint flags
+ * struct hw_perf_event.flags flags
  */
-#define PERF_X86_EVENT_PEBS_LDLAT	0x1 /* ld+ldlat data address sampling */
-#define PERF_X86_EVENT_PEBS_ST		0x2 /* st data address sampling */
+#define PERF_X86_EVENT_COMMITTED	0x1 /* event passed commit_txn */
+#define PERF_X86_EVENT_PEBS_LDLAT	0x2 /* ld+ldlat data addr sampling */
+#define PERF_X86_EVENT_PEBS_ST		0x4 /* st data addr sampling */
 
 struct amd_nb {
 	int nb_id;  /* NorthBridge id */
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index f60d41f..8225c84 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -1425,7 +1425,6 @@ x86_get_event_constraints(struct cpu_hw_events *cpuc, struct perf_event *event)
 	if (x86_pmu.event_constraints) {
 		for_each_event_constraint(c, x86_pmu.event_constraints) {
 			if ((event->hw.config & c->cmask) == c->code) {
-				/* hw.flags zeroed at initialization */
 				event->hw.flags |= c->flags;
 				return c;
 			}
@@ -1473,7 +1472,6 @@ intel_put_shared_regs_event_constraints(struct cpu_hw_events *cpuc,
 static void intel_put_event_constraints(struct cpu_hw_events *cpuc,
 					struct perf_event *event)
 {
-	event->hw.flags = 0;
 	intel_put_shared_regs_event_constraints(cpuc, event);
 }
 
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 74a4e14..998b927 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -178,7 +178,8 @@ struct perf_event;
 /*
  * Common implementation detail of pmu::{start,commit,cancel}_txn
  */
-#define PERF_EVENT_TXN 0x1
+#define PERF_EVENT_TXN    0x1
+#define PERF_EVENT_COMMIT 0x2 /* event committed */
 
 /**
  * struct pmu - generic performance monitoring unit
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [PATCH] perf,x86: Fix shared registers mutual exclusion bug
  2013-06-05 14:43 [PATCH] perf,x86: Fix shared registers mutual exclusion bug Stephane Eranian
@ 2013-06-18  9:11 ` Peter Zijlstra
  2013-06-18 18:43   ` Stephane Eranian
  0 siblings, 1 reply; 4+ messages in thread
From: Peter Zijlstra @ 2013-06-18  9:11 UTC (permalink / raw)
  To: Stephane Eranian; +Cc: linux-kernel, mingo, jolsa, vincent.weaver, ak

On Wed, Jun 05, 2013 at 04:43:46PM +0200, Stephane Eranian wrote:
> 
> This patch fixes a problem with the shared registers mutual
> exclusion code and incremental event scheduling by the
> generic perf_event code.
> 
> There was a bug whereby the mutual exclusion on the shared
> registers was not enforced because of incremental scheduling
> abort due to event constraints.
> 
> Example on Nehalem:
> group1= ref-cycles,OFFCORE_RESPONSE_0:PF_RFO
> group2= ref-cycles
> 
> The ref-cycles event can only be measured by 1 counter. Yet, there
> are 2 instances here. The first group can be scheduled and is committed.
> Then, the generic code tries to schedule group2 and this fails (because
> there is no more counter to support the 2nd instance of ref-cycles).
> 
> But in x86_schedule_events() error path, put_event_contraints() is invoked
> on ALL the events and not just the ones that just failed. That causes the
> "lock" on the shared offcore_response MSR to be released. Yet the first group
> is actually scheduled and is exposed to reprogramming of that shared msr by
> the sibling HT thread (when they are shared by HT threads). In other words,
> there is no guarantee on what is measured for the offcore_response event.
> 
> This patch fixes the problem by tagging committed events with the
> PERF_X86_EVENT_COMMITTED tag. In the error path of x86_schedule_events(),
> only the events NOT tagged have their constraint released. The tag
> is eventually removed when the event in descheduled.
> 
> Example was given with offcore_response but also applies to LBR_SELECT
> and LDLAT shared registers.
> 
> Signed-off-by: Stephane Eranian <eranian@google.com>

I'm getting conflicts against other patches -- most notably I think the
contraints stack opt from Andrew Hunter.

I'll try and get Ingo to finally pick up my queued patches so we can
rebase.


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] perf,x86: Fix shared registers mutual exclusion bug
  2013-06-18  9:11 ` Peter Zijlstra
@ 2013-06-18 18:43   ` Stephane Eranian
  2013-06-20 12:07     ` Ingo Molnar
  0 siblings, 1 reply; 4+ messages in thread
From: Stephane Eranian @ 2013-06-18 18:43 UTC (permalink / raw)
  To: Peter Zijlstra; +Cc: LKML, mingo, Jiri Olsa, vincent.weaver, ak

On Tue, Jun 18, 2013 at 11:11 AM, Peter Zijlstra <peterz@infradead.org> wrote:
> On Wed, Jun 05, 2013 at 04:43:46PM +0200, Stephane Eranian wrote:
>>
>> This patch fixes a problem with the shared registers mutual
>> exclusion code and incremental event scheduling by the
>> generic perf_event code.
>>
>> There was a bug whereby the mutual exclusion on the shared
>> registers was not enforced because of incremental scheduling
>> abort due to event constraints.
>>
>> Example on Nehalem:
>> group1= ref-cycles,OFFCORE_RESPONSE_0:PF_RFO
>> group2= ref-cycles
>>
>> The ref-cycles event can only be measured by 1 counter. Yet, there
>> are 2 instances here. The first group can be scheduled and is committed.
>> Then, the generic code tries to schedule group2 and this fails (because
>> there is no more counter to support the 2nd instance of ref-cycles).
>>
>> But in x86_schedule_events() error path, put_event_contraints() is invoked
>> on ALL the events and not just the ones that just failed. That causes the
>> "lock" on the shared offcore_response MSR to be released. Yet the first group
>> is actually scheduled and is exposed to reprogramming of that shared msr by
>> the sibling HT thread (when they are shared by HT threads). In other words,
>> there is no guarantee on what is measured for the offcore_response event.
>>
>> This patch fixes the problem by tagging committed events with the
>> PERF_X86_EVENT_COMMITTED tag. In the error path of x86_schedule_events(),
>> only the events NOT tagged have their constraint released. The tag
>> is eventually removed when the event in descheduled.
>>
>> Example was given with offcore_response but also applies to LBR_SELECT
>> and LDLAT shared registers.
>>
>> Signed-off-by: Stephane Eranian <eranian@google.com>
>
> I'm getting conflicts against other patches -- most notably I think the
> contraints stack opt from Andrew Hunter.
>
Yes, that would not surprise me. I wrote this patch without assuming
Andrew's patch would be there. But we need to add it. Then we can fix
the shared_regs patch.

> I'll try and get Ingo to finally pick up my queued patches so we can
> rebase.

Ok, thanks.

>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] perf,x86: Fix shared registers mutual exclusion bug
  2013-06-18 18:43   ` Stephane Eranian
@ 2013-06-20 12:07     ` Ingo Molnar
  0 siblings, 0 replies; 4+ messages in thread
From: Ingo Molnar @ 2013-06-20 12:07 UTC (permalink / raw)
  To: Stephane Eranian
  Cc: Peter Zijlstra, LKML, mingo, Jiri Olsa, vincent.weaver, ak


* Stephane Eranian <eranian@google.com> wrote:

> On Tue, Jun 18, 2013 at 11:11 AM, Peter Zijlstra <peterz@infradead.org> wrote:
> > On Wed, Jun 05, 2013 at 04:43:46PM +0200, Stephane Eranian wrote:
> >>
> >> This patch fixes a problem with the shared registers mutual
> >> exclusion code and incremental event scheduling by the
> >> generic perf_event code.
> >>
> >> There was a bug whereby the mutual exclusion on the shared
> >> registers was not enforced because of incremental scheduling
> >> abort due to event constraints.
> >>
> >> Example on Nehalem:
> >> group1= ref-cycles,OFFCORE_RESPONSE_0:PF_RFO
> >> group2= ref-cycles
> >>
> >> The ref-cycles event can only be measured by 1 counter. Yet, there
> >> are 2 instances here. The first group can be scheduled and is committed.
> >> Then, the generic code tries to schedule group2 and this fails (because
> >> there is no more counter to support the 2nd instance of ref-cycles).
> >>
> >> But in x86_schedule_events() error path, put_event_contraints() is invoked
> >> on ALL the events and not just the ones that just failed. That causes the
> >> "lock" on the shared offcore_response MSR to be released. Yet the first group
> >> is actually scheduled and is exposed to reprogramming of that shared msr by
> >> the sibling HT thread (when they are shared by HT threads). In other words,
> >> there is no guarantee on what is measured for the offcore_response event.
> >>
> >> This patch fixes the problem by tagging committed events with the
> >> PERF_X86_EVENT_COMMITTED tag. In the error path of x86_schedule_events(),
> >> only the events NOT tagged have their constraint released. The tag
> >> is eventually removed when the event in descheduled.
> >>
> >> Example was given with offcore_response but also applies to LBR_SELECT
> >> and LDLAT shared registers.
> >>
> >> Signed-off-by: Stephane Eranian <eranian@google.com>
> >
> > I'm getting conflicts against other patches -- most notably I think the
> > contraints stack opt from Andrew Hunter.
> >
> Yes, that would not surprise me. I wrote this patch without assuming
> Andrew's patch would be there. But we need to add it. Then we can fix
> the shared_regs patch.
> 
> > I'll try and get Ingo to finally pick up my queued patches so we can
> > rebase.
> 
> Ok, thanks.

That happened yesterday, so latest -tip should be a good base to work on.

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2013-06-20 12:07 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-06-05 14:43 [PATCH] perf,x86: Fix shared registers mutual exclusion bug Stephane Eranian
2013-06-18  9:11 ` Peter Zijlstra
2013-06-18 18:43   ` Stephane Eranian
2013-06-20 12:07     ` Ingo Molnar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.