msm: perf: clean up duplicate constraint events

Events with a duplicate constraint are set to state=OFF when detected,
so that their duplicate counts are not read. However, they were not being
cleaned up because the core code only cleaned up ACTIVE events.
This resulted in counters not being freed and eventually running out
of resources.

Clean up the events with state==OFF that were marked that way because of
constraint duplication.
Ensure counts are not updated for OFF events.

Change-Id: If532801c79e6ad6809869eb0a3063774f00c92c3
Signed-off-by: Neil Leeder <nleeder@codeaurora.org>
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 4c0aac5..9fbe2d5 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -194,6 +194,9 @@
 	struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
 	u64 delta, prev_raw_count, new_raw_count;
 
+	if (event->state <= PERF_EVENT_STATE_OFF)
+		return 0;
+
 again:
 	prev_raw_count = local64_read(&hwc->prev_count);
 	new_raw_count = armpmu->read_counter(idx);
diff --git a/arch/arm/mach-msm/perf_debug.c b/arch/arm/mach-msm/perf_debug.c
index a072878..67bf05d 100644
--- a/arch/arm/mach-msm/perf_debug.c
+++ b/arch/arm/mach-msm/perf_debug.c
@@ -35,6 +35,7 @@
 	"15 Perf: bring CPU online if needed when disabling irq\n"
 	"16 Perf: Support sw events across hotplug\n"
 	"17 msm: perf: initialise krait perf L2 counter enables\n"
+	"18 msm: perf: clean up duplicate constraint events\n"
 ;
 
 static ssize_t desc_read(struct file *fp, char __user *buf,
diff --git a/arch/arm/mach-msm/perf_event_msm_krait_l2.c b/arch/arm/mach-msm/perf_event_msm_krait_l2.c
index a7c4287..a49c7b1 100644
--- a/arch/arm/mach-msm/perf_event_msm_krait_l2.c
+++ b/arch/arm/mach-msm/perf_event_msm_krait_l2.c
@@ -457,8 +457,10 @@
 			 * This sets the event OFF on all but one
 			 * CPU.
 			 */
-			if (!(event->cpu < 0))
+			if (!(event->cpu < 0)) {
 				event->state = PERF_EVENT_STATE_OFF;
+				event->attr.constraint_duplicate = 1;
+			}
 	}
 out:
 	raw_spin_unlock_irqrestore(&l2_pmu_constraints.lock, flags);
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index bf99faa..289754e 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -254,8 +254,9 @@
 
 				exclude_host   :  1, /* don't count in host   */
 				exclude_guest  :  1, /* don't count in guest  */
+				constraint_duplicate : 1,
 
-				__reserved_1   : 43;
+				__reserved_1   : 42;
 
 	union {
 		__u32		wakeup_events;	  /* wakeup every n events */
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 5ece9dd..7dd822b 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -2959,6 +2959,14 @@
 	struct perf_event *event = file->private_data;
 	struct task_struct *owner;
 
+	/*
+	 * Event can be in state OFF because of a constraint check.
+	 * Change to ACTIVE so that it gets cleaned up correctly.
+	 */
+	if ((event->state == PERF_EVENT_STATE_OFF) &&
+	    event->attr.constraint_duplicate)
+		event->state = PERF_EVENT_STATE_ACTIVE;
+
 	file->private_data = NULL;
 
 	rcu_read_lock();