Skip to contentSkip to conversation

perf_counter: fix the group mess

PATCH In review Peter Zijlstra 4 participants 11 messages Aug 12, 2009 – Aug 13, 2009 perf events

lore
Share & tools
lore
#0 lore raw

cover perf_counter: fix the group mess

With these two patches the below proglet gives:

# ./test
EVNT: 0xffffffff811c0f4c scale: nan ID: 37 CNT: 1006180 ID: 38 CNT: 1010230 ID: 39 CNT: 1010332 ID: 40 CNT: 1010420

I can't seem to catch the SIGIO thing, but at least all the counters report more or less the same value. Also, that scale stuff doesn't seem to quite work yet.. I'll be poking at that too.

---
#include "perf.h"

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>

void work(void)

{
	int i;

	for (i = 0; i < 1000000000; i++) {
		asm("nop");
		asm("nop");
		asm("nop");
		asm("nop");
		asm("nop");
		asm("nop");
		asm("nop");
	}
}

unsigned long page_size;
int fd = -1, fd1 = 0;
pid_t me;
void *output;

void handle_sigio(int sig)

{
	printf("signal\n"); // not signal safe
	fflush(stdout);

	ioctl(fd, PERF_COUNTER_IOC_REFRESH, 1);
}

static unsigned long mmap_read_head(void)

{
	struct perf_counter_mmap_page *pc = output;
	long head;

	head = pc->data_head;
	rmb();

	return head;
}

static void *mmap_read_base(void)

{
	return output + page_size;
}
struct event {
	struct perf_event_header header;

	u64 ip;
	u64 nr;
	u64 time_enabled;
	u64 time_running;
	struct {
		u64 val;
		u64 id;
	} cnt[0];
};

int main(int argc, char **argv)

{
	struct perf_counter_attr attr;
	unsigned long offset = 0, head;
	int err, i;

	page_size = sysconf(_SC_PAGE_SIZE);
	me = getpid();

	memset(&attr, 0, sizeof(attr));

	attr.type = PERF_TYPE_HARDWARE;
	attr.config = PERF_COUNT_HW_CPU_CYCLES;
	attr.sample_period = 1000000;
	attr.sample_type = PERF_SAMPLE_IP |
			   PERF_SAMPLE_READ;
	attr.read_format = PERF_FORMAT_TOTAL_TIME_RUNNING |
			   PERF_FORMAT_TOTAL_TIME_ENABLED |
			   PERF_FORMAT_ID |
			   PERF_FORMAT_GROUP;
	attr.disabled = 1;
	attr.wakeup_events = 1;

	fd  = sys_perf_counter_open(&attr, me, -1, fd, 0);
	if (fd <= 0) {
		perror("FAIL fd: ");
		exit(-1);
	}

	attr.sample_period = 0;
	attr.disabled = 0;

	for (i = 0; i < 3; i++) {
		fd1 = sys_perf_counter_open(&attr, me, -1, fd, 0);
		if (fd1 <= 0) {
			perror("FAIL fd1: ");
			exit(-1);
		}
	}

	signal(SIGIO, handle_sigio);
	err = fcntl(fd, F_SETOWN, me);
	if (err == -1) {
		perror("FAIL fcntl: ");
		exit(-1);
	}

	output = mmap(NULL, page_size * 3, PROT_READ, MAP_SHARED, fd, 0);
	if (output == ((void *)-1)) {
		perror("FAIL mmap:");
		exit(-1);
	}

	ioctl(fd, PERF_COUNTER_IOC_REFRESH, 1);

	work();

	ioctl(fd, PERF_COUNTER_IOC_DISABLE, 0);

	head = mmap_read_head();

	for (; offset < head; ) {
		struct event *evnt = mmap_read_base() + offset;

		offset += evnt->header.size;

		printf("EVNT: %p scale: %f ", (void *)evnt->ip,
				((double)evnt->time_running)/evnt->time_enabled
				);
		for (i = 0; i < evnt->nr; i++) {
			printf("ID: %Lu CNT: %Lu ", 
					evnt->cnt[i].id, evnt->cnt[i].val);
		}
		printf("\n");
	}

	return 0;
}
#1 lore raw

PATCH 1/2 perf: rework the whole read vs group stuff

Replace PERF_SAMPLE_GROUP with PERF_SAMPLE_READ and introduce PERF_FORMAT_GROUP to deal with group reads in a more generic way.

This allows you to get group reads out of read() as well.

---
 include/linux/perf_counter.h |   36 +++++--
 kernel/perf_counter.c        |  213 +++++++++++++++++++++++++++++--------------
 2 files changed, 172 insertions(+), 77 deletions(-)
include/linux/perf_counter.h +28 -8
Index: linux-2.6/include/linux/perf_counter.h
===================================================================
@@ -115,7 +115,7 @@ enum perf_counter_sample_format {
 	PERF_SAMPLE_TID				= 1U << 1,
 	PERF_SAMPLE_TIME			= 1U << 2,
 	PERF_SAMPLE_ADDR			= 1U << 3,
-	PERF_SAMPLE_GROUP			= 1U << 4,
+	PERF_SAMPLE_READ			= 1U << 4,
 	PERF_SAMPLE_CALLCHAIN			= 1U << 5,
 	PERF_SAMPLE_ID				= 1U << 6,
 	PERF_SAMPLE_CPU				= 1U << 7,
@@ -130,13 +130,25 @@ enum perf_counter_sample_format {
  * Bits that can be set in attr.read_format to request that
  * reads on the counter should return the indicated quantities,
  * in increasing order of bit value, after the counter value.
+ *
+ * struct {
+ * 	{ u64		nr; 		} && PERF_FORMAT_GROUP
+ * 	{ u64		time_enabled;	} && PERF_FORMAT_ENABLED
+ * 	{ u64		time_running;	} && PERF_FORMAT_RUNNING
+ * 	{ u64		value;
+ * 	  { u64		id;		} && PERF_FORMAT_ID
+ * 	} 		cntr[nr];
+ * };
+ *
+ * Where 'nr' defaults to 1 when !PERF_FORMAT_GROUP
  */
 enum perf_counter_read_format {
 	PERF_FORMAT_TOTAL_TIME_ENABLED		= 1U << 0,
 	PERF_FORMAT_TOTAL_TIME_RUNNING		= 1U << 1,
 	PERF_FORMAT_ID				= 1U << 2,
+	PERF_FORMAT_GROUP			= 1U << 3,
 
-	PERF_FORMAT_MAX = 1U << 3, 		/* non-ABI */
+	PERF_FORMAT_MAX = 1U << 4, 		/* non-ABI */
 };
 
 #define PERF_ATTR_SIZE_VER0	64	/* sizeof first published struct */
@@ -343,10 +355,13 @@ enum perf_event_type {
 	 * struct {
 	 * 	struct perf_event_header	header;
 	 * 	u32				pid, tid;
-	 * 	u64				value;
-	 * 	{ u64		time_enabled; 	} && PERF_FORMAT_ENABLED
-	 * 	{ u64		time_running; 	} && PERF_FORMAT_RUNNING
-	 * 	{ u64		parent_id;	} && PERF_FORMAT_ID
+	 *
+	 * 	{ u64		nr;		} && PERF_FORMAT_GROUP
+	 * 	{ u64		value;
+	 * 	  { u64		time_enabled; 	} && PERF_FORMAT_ENABLED
+	 * 	  { u64		time_running; 	} && PERF_FORMAT_RUNNING
+	 * 	  { u64		parent_id;	} && PERF_FORMAT_ID
+	 * 	} 				cntr[nr];
 	 * };
 	 */
 	PERF_EVENT_READ			= 8,
@@ -364,11 +379,16 @@ enum perf_event_type {
 	 *	{ u32			cpu, res; } && PERF_SAMPLE_CPU
 	 * 	{ u64			period;   } && PERF_SAMPLE_PERIOD
 	 *
-	 *	{ u64			nr;
-	 *	  { u64 id, val; }	cnt[nr];  } && PERF_SAMPLE_GROUP
+	 *	{ { u64			nr;   } && PERF_FORMAT_GROUP
+	 *	  { u64		value;
+	 *	    { u64	time_enabled; } && PERF_FORMAT_ENABLED
+	 *	    { u64	time_running; } && PERF_FORMAT_RUNNING
+	 *	    { u64	id;           } && PERF_FORMAT_ID
+	 *	  }			cntr[nr]; } && PERF_SAMPLE_READ
 	 *
 	 *	{ u64			nr,
 	 *	  u64			ips[nr];  } && PERF_SAMPLE_CALLCHAIN
+	 *
 	 *	{ u32			size;
 	 *	  char                  data[size];}&& PERF_SAMPLE_RAW
 	 * };
kernel/perf_counter.c +144 -69

295 lines not on this page · raw message

#2 lore raw

PATCH 2/2 perf_counter: Fix an ipi-deadlock

In reply to Peter Zijlstra

perf_pending_counter() is called from IRQ context and will call perf_counter_disable(), however perf_counter_disable() uses smp_call_function_single() which doesn't fancy being used with IRQs disabled due to IPI deadlocks.

Fix this by making it use the local __perf_counter_disable() call and teaching the counter_sched_out() code about pending disables as well.

This should cover the case where a counter migrates before the pending queue gets processed.

---
 kernel/perf_counter.c |    6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)
kernel/perf_counter.c +5 -1
Index: linux-2.6/kernel/perf_counter.c
===================================================================
@@ -307,6 +307,10 @@ counter_sched_out(struct perf_counter *c
 		return;
 
 	counter->state = PERF_COUNTER_STATE_INACTIVE;
+	if (counter->pending_disable) {
+		counter->pending_disable = 0;
+		counter->state = PERF_COUNTER_STATE_OFF;
+	}
 	counter->tstamp_stopped = ctx->time;
 	counter->pmu->disable(counter);
 	counter->oncpu = -1;
@@ -2315,7 +2319,7 @@ static void perf_pending_counter(struct 
 
 	if (counter->pending_disable) {
 		counter->pending_disable = 0;
-		perf_counter_disable(counter);
+		__perf_counter_disable(counter);
 	}
 
 	if (counter->pending_wakeup) {
#3 lore raw
In reply to Peter Zijlstra
Quoted 7 lines · Peter Zijlstra

With these two patches the below proglet gives:

# ./test
EVNT: 0xffffffff811c0f4c scale: nan ID: 37 CNT: 1006180 ID: 38 CNT: 1010230 ID: 39 CNT: 1010332 ID: 40 CNT: 1010420

I can't seem to catch the SIGIO thing, but at least all the counters report

I think it's because you are missing this fcntl() call to get async notification:

       ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_ASYNC);
        if (ret == -1)
                err(1, "cannot set ASYNC");
#4 lore raw
Quoted 7 lines · stephane eranian
Quoted 6 lines · Peter Zijlstra

With these two patches the below proglet gives:

# ./test
EVNT: 0xffffffff811c0f4c scale: nan ID: 37 CNT: 1006180 ID: 38 CNT: 1010230 ID: 39 CNT: 1010332 ID: 40 CNT: 1010420
Quoted 8 lines
Quoted 1 line

I can't seem to catch the SIGIO thing, but at least all the counters report

I think it's because you are missing this fcntl() call to get async notification:

       ret = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_ASYNC);
        if (ret == -1)
                err(1, "cannot set ASYNC");

Ah, indeed, thanks!

(with a reduced work function, or increased period)

# gcc -o test test.c; ./test
signal
signal
signal
signal
signal
signal
EVNT: 0x40091c scale: nan ID: 49 CNT: 1006676 ID: 50 CNT: 1010796 ID: 51 CNT: 1010960 ID: 52 CNT: 1010937
EVNT: 0x400928 scale: 1.000000 ID: 49 CNT: 2003019 ID: 50 CNT: 2010882 ID: 51 CNT: 2011475 ID: 52 CNT: 2012032
EVNT: 0x400928 scale: 1.000000 ID: 49 CNT: 3002707 ID: 50 CNT: 3015571 ID: 51 CNT: 3016714 ID: 52 CNT: 3017830
EVNT: 0x400916 scale: 1.000000 ID: 49 CNT: 4002677 ID: 50 CNT: 4020137 ID: 51 CNT: 4021880 ID: 52 CNT: 4023639
EVNT: 0x40091c scale: 1.000000 ID: 49 CNT: 5002749 ID: 50 CNT: 5024707 ID: 51 CNT: 5027078 ID: 52 CNT: 5029378
EVNT: 0x40091c scale: 1.000000 ID: 49 CNT: 6002799 ID: 50 CNT: 6029452 ID: 51 CNT: 6032339 ID: 52 CNT: 6035277
maintainer
#5 lore raw

[PATCH 3/2] perf tools: Fixup read ABI breakage

In reply to Peter Zijlstra

Subject: perf tools: Fixup read ABI breakage
From: Peter Zijlstra <[email protected]>
Date: Thu Aug 13 09:03:33 CEST 2009

Update the perf tool to the new read output format.

---
 tools/perf/builtin-record.c |    2 +-
 tools/perf/builtin-stat.c   |    8 +++++---
 tools/perf/util/event.h     |   10 ++++++++--
 3 files changed, 14 insertions(+), 6 deletions(-)
tools/perf/builtin-record.c +1 -1
Index: linux-2.6/tools/perf/builtin-record.c
===================================================================
@@ -370,9 +370,9 @@ static void create_counter(int counter, 
 	struct perf_header_attr *h_attr;
 	int track = !counter; /* only the first counter needs these */
 	struct {
-		u64 count;
 		u64 time_enabled;
 		u64 time_running;
+		u64 count;
 		u64 id;
 	} read_data;
 
tools/perf/builtin-stat.c +5 -3
Index: linux-2.6/tools/perf/builtin-stat.c
===================================================================
@@ -168,10 +168,12 @@ static void read_counter(int counter)
 		close(fd[cpu][counter]);
 		fd[cpu][counter] = -1;
 
-		count[0] += single_count[0];
 		if (scale) {
-			count[1] += single_count[1];
-			count[2] += single_count[2];
+			count[0] += single_count[2]; /* value   */
+			count[1] += single_count[0]; /* enabled */
+			count[2] += single_count[1]; /* running */
+		} else {
+			count[0] += single_count[0]; /* value   */
 		}
 	}
 
tools/perf/util/event.h +8 -2
Index: linux-2.6/tools/perf/util/event.h
===================================================================
@@ -4,6 +4,9 @@
 #include "util.h"
 #include <linux/list.h>
 
+/*
+ * PERF_SAMPLE_IP | PERF_SAMPLE_TID | *
+ */
 struct ip_event {
 	struct perf_event_header header;
 	u64 ip;
@@ -38,12 +41,15 @@ struct lost_event {
 	u64 lost;
 };
 
-struct read_event {
+/*
+ * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
+ */
+struct read_event {
 	struct perf_event_header header;
 	u32 pid,tid;
-	u64 value;
 	u64 time_enabled;
 	u64 time_running;
+	u64 value;
 	u64 id;
 };
 
maintainer
#6 lore raw

[PATCH 4/2] perf_counter: Fix swcounter context invariance

In reply to Peter Zijlstra

Not really related to this topic, but it needs posting anyway.

---

Subject: perf_counter: Fix swcounter context invariance
From: Peter Zijlstra <[email protected]>
Date: Fri Aug 07 13:29:13 CEST 2009

perf_swcounter_is_counting() uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.

The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in perf_swcounter_ctx_event().

---
 kernel/perf_counter.c |   44 ++++++++++++++++++--------------------------
 1 file changed, 18 insertions(+), 26 deletions(-)
kernel/perf_counter.c +18 -26
Index: linux-2.6/kernel/perf_counter.c
===================================================================
@@ -3519,40 +3519,32 @@ static void perf_swcounter_add(struct pe
 
 static int perf_swcounter_is_counting(struct perf_counter *counter)
 {
-	struct perf_counter_context *ctx;
-	unsigned long flags;
-	int count;
-
+	/*
+	 * The counter is active, we're good!
+	 */
 	if (counter->state == PERF_COUNTER_STATE_ACTIVE)
 		return 1;
 
+	/*
+	 * The counter is off/error, not counting.
+	 */
 	if (counter->state != PERF_COUNTER_STATE_INACTIVE)
 		return 0;
 
 	/*
-	 * If the counter is inactive, it could be just because
-	 * its task is scheduled out, or because it's in a group
-	 * which could not go on the PMU.  We want to count in
-	 * the first case but not the second.  If the context is
-	 * currently active then an inactive software counter must
-	 * be the second case.  If it's not currently active then
-	 * we need to know whether the counter was active when the
-	 * context was last active, which we can determine by
-	 * comparing counter->tstamp_stopped with ctx->time.
-	 *
-	 * We are within an RCU read-side critical section,
-	 * which protects the existence of *ctx.
+	 * The counter is inactive, if the context is active
+	 * we're part of a group that didn't make it on the 'pmu',
+	 * not counting.
 	 */
-	ctx = counter->ctx;
-	spin_lock_irqsave(&ctx->lock, flags);
-	count = 1;
-	/* Re-check state now we have the lock */
-	if (counter->state < PERF_COUNTER_STATE_INACTIVE ||
-	    counter->ctx->is_active ||
-	    counter->tstamp_stopped < ctx->time)
-		count = 0;
-	spin_unlock_irqrestore(&ctx->lock, flags);
-	return count;
+	if (counter->ctx->is_active)
+		return 0;
+
+	/*
+	 * We're inactive and the context is too, this means the
+	 * task is scheduled out, we're counting events that happen
+	 * to us, like migration events.
+	 */
+	return 1;
 }
 
 static int perf_swcounter_match(struct perf_counter *counter,
#7 lore raw

Re: [PATCH 4/2] perf_counter: Fix swcounter context invariance

Quoted 12 lines · Peter Zijlstra

Not really related to this topic, but it needs posting anyway.

---

Subject: perf_counter: Fix swcounter context invariance
From: Peter Zijlstra <[email protected]>
Date: Fri Aug 07 13:29:13 CEST 2009

perf_swcounter_is_counting() uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.

Quoted 6 lines

The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in perf_swcounter_ctx_event().

As a side effect, it's possible this also fixes the hard lockups while opening a lockdep tracepoint counter.

Quoted 73 lines
---
 kernel/perf_counter.c |   44 ++++++++++++++++++--------------------------
 1 file changed, 18 insertions(+), 26 deletions(-)

Index: linux-2.6/kernel/perf_counter.c
===================================================================

kernel/perf_counter.c +18 -27
@@ -3519,40 +3519,32 @@ static void perf_swcounter_add(struct pe
 
 static int perf_swcounter_is_counting(struct perf_counter *counter)
 {
-	struct perf_counter_context *ctx;
-	unsigned long flags;
-	int count;
-
+	/*
+	 * The counter is active, we're good!
+	 */
 	if (counter->state == PERF_COUNTER_STATE_ACTIVE)
 		return 1;
 
+	/*
+	 * The counter is off/error, not counting.
+	 */
 	if (counter->state != PERF_COUNTER_STATE_INACTIVE)
 		return 0;
 
 	/*
-	 * If the counter is inactive, it could be just because
-	 * its task is scheduled out, or because it's in a group
-	 * which could not go on the PMU.  We want to count in
-	 * the first case but not the second.  If the context is
-	 * currently active then an inactive software counter must
-	 * be the second case.  If it's not currently active then
-	 * we need to know whether the counter was active when the
-	 * context was last active, which we can determine by
-	 * comparing counter->tstamp_stopped with ctx->time.
-	 *
-	 * We are within an RCU read-side critical section,
-	 * which protects the existence of *ctx.
+	 * The counter is inactive, if the context is active
+	 * we're part of a group that didn't make it on the 'pmu',
+	 * not counting.
 	 */
-	ctx = counter->ctx;
-	spin_lock_irqsave(&ctx->lock, flags);
-	count = 1;
-	/* Re-check state now we have the lock */
-	if (counter->state < PERF_COUNTER_STATE_INACTIVE ||
-	    counter->ctx->is_active ||
-	    counter->tstamp_stopped < ctx->time)
-		count = 0;
-	spin_unlock_irqrestore(&ctx->lock, flags);
-	return count;
+	if (counter->ctx->is_active)
+		return 0;
+
+	/*
+	 * We're inactive and the context is too, this means the
+	 * task is scheduled out, we're counting events that happen
+	 * to us, like migration events.
+	 */
+	return 1;
 }
 
 static int perf_swcounter_match(struct perf_counter *counter,
 
--

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

maintainer
#8 lore raw

Re: [PATCH 4/2] perf_counter: Fix swcounter context invariance

Quoted 17 lines · Frederic Weisbecker
Quoted 16 lines · Peter Zijlstra

Not really related to this topic, but it needs posting anyway.

---

Subject: perf_counter: Fix swcounter context invariance
From: Peter Zijlstra <[email protected]>
Date: Fri Aug 07 13:29:13 CEST 2009

perf_swcounter_is_counting() uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.

The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in perf_swcounter_ctx_event().

Quoted 7 lines
Quoted 2 lines

As a side effect, it's possible this also fixes the hard lockups while opening a lockdep tracepoint counter.

It will -- not a side-effect at all, intended consequence :-)

#9 lore raw

Re: [PATCH 4/2] perf_counter: Fix swcounter context invariance

Quoted 23 lines · Peter Zijlstra
Quoted 22 lines · Frederic Weisbecker
Quoted 18 lines · Peter Zijlstra

Not really related to this topic, but it needs posting anyway.

---

Subject: perf_counter: Fix swcounter context invariance
From: Peter Zijlstra <[email protected]>
Date: Fri Aug 07 13:29:13 CEST 2009

perf_swcounter_is_counting() uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.

The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in perf_swcounter_ctx_event().

Quoted 4 lines
Quoted 2 lines

As a side effect, it's possible this also fixes the hard lockups while opening a lockdep tracepoint counter.

It will -- not a side-effect at all, intended consequence :-)

Great then! Important fix for .31 :-)

#10 lore raw

[tip:perfcounters/urgent] perf_counter: Fix swcounter context invariance

In reply to Peter Zijlstra
Commit-ID:  bcfc2602e8541ac13b1def38e2591dca072cff7a
Gitweb:     http://git.kernel.org/tip/bcfc2602e8541ac13b1def38e2591dca072cff7a
Author:     Peter Zijlstra <[email protected]>
AuthorDate: Thu, 13 Aug 2009 09:51:55 +0200
Committer:  Ingo Molnar <[email protected]>
CommitDate: Thu, 13 Aug 2009 12:18:43 +0200

perf_counter: Fix swcounter context invariance

perf_swcounter_is_counting() uses a lock, which means we cannot use swcounters from NMI or when holding that particular lock, this is unintended.

The below removes the lock, this opens up race window, but not worse than the swcounters already experience due to RCU traversal of the context in perf_swcounter_ctx_event().

This also fixes the hard lockups while opening a lockdep tracepoint counter.

Cc Paul Mackerras <[email protected]>
Cc stephane eranian <[email protected]>
Cc Corey J Ashford <[email protected]>

LKML-Reference: <1250149915.10001.66.camel@twins>

---
 kernel/perf_counter.c |   44 ++++++++++++++++++--------------------------
 1 files changed, 18 insertions(+), 26 deletions(-)
kernel/perf_counter.c +18 -26
index e26d2fc..3dd4339 100644
@@ -3444,40 +3444,32 @@ static void perf_swcounter_add(struct perf_counter *counter, u64 nr,
 
 static int perf_swcounter_is_counting(struct perf_counter *counter)
 {
-	struct perf_counter_context *ctx;
-	unsigned long flags;
-	int count;
-
+	/*
+	 * The counter is active, we're good!
+	 */
 	if (counter->state == PERF_COUNTER_STATE_ACTIVE)
 		return 1;
 
+	/*
+	 * The counter is off/error, not counting.
+	 */
 	if (counter->state != PERF_COUNTER_STATE_INACTIVE)
 		return 0;
 
 	/*
-	 * If the counter is inactive, it could be just because
-	 * its task is scheduled out, or because it's in a group
-	 * which could not go on the PMU.  We want to count in
-	 * the first case but not the second.  If the context is
-	 * currently active then an inactive software counter must
-	 * be the second case.  If it's not currently active then
-	 * we need to know whether the counter was active when the
-	 * context was last active, which we can determine by
-	 * comparing counter->tstamp_stopped with ctx->time.
-	 *
-	 * We are within an RCU read-side critical section,
-	 * which protects the existence of *ctx.
+	 * The counter is inactive, if the context is active
+	 * we're part of a group that didn't make it on the 'pmu',
+	 * not counting.
 	 */
-	ctx = counter->ctx;
-	spin_lock_irqsave(&ctx->lock, flags);
-	count = 1;
-	/* Re-check state now we have the lock */
-	if (counter->state < PERF_COUNTER_STATE_INACTIVE ||
-	    counter->ctx->is_active ||
-	    counter->tstamp_stopped < ctx->time)
-		count = 0;
-	spin_unlock_irqrestore(&ctx->lock, flags);
-	return count;
+	if (counter->ctx->is_active)
+		return 0;
+
+	/*
+	 * We're inactive and the context is too, this means the
+	 * task is scheduled out, we're counting events that happen
+	 * to us, like migration events.
+	 */
+	return 1;
 }
 
 static int perf_swcounter_match(struct perf_counter *counter,