From: Carl Love <cel@us.ibm.com>
Date: Wed, 24 Oct 2012 18:19:43 -0500
Subject: powerpc/oprofile: Fix marked events support on Power7+ not set.
Git-commit: adbf115d66649b3a52e6875f93c9f131ba690cce
Patch-mainline: 3.7
References: bnc#788294,fate#314052

This patch was backported from the kernel.org mainline kernel by
Carl Love <cel@us.ibm.com>.  The log message from the commit in
the mainline tree at kernel.org is as follows:

    commit adbf115d66649b3a52e6875f93c9f131ba690cce
    Author: Carl E. Love <cel@linux.vnet.ibm.com>
    Date:   Fri Aug 3 03:02:17 2012 +0000

    powerpc/oprofile: Fix marked events support on Power7+ not set.

    Starting with Power 7+ we need to check for marked events if the SIAR
    register is valid, i.e. it contains the correct address of the instruction
    at the time the performance counter overflowed.  The mmcra register on
    Power 7+, contains a new bit to indicate that the contents of the SIAR
    is valid. If the event is not marked, then the sample is recorded
    independently of the SIAR valid bit setting.  For older processors, there
    is no SIAR valid bit to check so the samples are always recorded.  This is
    done by forcing the cntr_marked_events bit mask to zero.  The code will
    always record the sample in this case since the bit mask says the event is
    not a marked event even if it really is a marked event.

    Signed-off-by: Carl Love <cel@us.ibm.com>
    Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>

Signed-off-by: Carl Love <cel@us.ibm.com>
Acked-by: Torsten Duwe <duwe@suse.de>

---
 arch/powerpc/oprofile/op_model_power4.c | 100 +++++++++++++++++++++++++++++++-
 1 file changed, 99 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/oprofile/op_model_power4.c b/arch/powerpc/oprofile/op_model_power4.c
index e6bec74..c964bd7 100644
--- a/arch/powerpc/oprofile/op_model_power4.c
+++ b/arch/powerpc/oprofile/op_model_power4.c
@@ -22,6 +22,13 @@
 #include <asm/reg.h>
 
 #define dbg(args...)
+#define OPROFILE_PM_PMCSEL_MSK      0xffULL
+#define OPROFILE_PM_UNIT_SHIFT      60
+#define OPROFILE_PM_UNIT_MSK        0xfULL
+#define OPROFILE_MAX_PMC_NUM        3
+#define OPROFILE_PMSEL_FIELD_WIDTH  8
+#define OPROFILE_UNIT_FIELD_WIDTH   4
+#define MMCRA_SIAR_VALID_MASK       0x10000000ULL
 
 static unsigned long reset_value[OP_MAX_COUNTER];
 
@@ -32,6 +39,61 @@ static int use_slot_nums;
 static u32 mmcr0_val;
 static u64 mmcr1_val;
 static u64 mmcra_val;
+static u32 cntr_marked_events;
+
+static int power7_marked_instr_event(u64 mmcr1)
+{
+	u64 psel, unit;
+	int pmc, cntr_marked_events = 0;
+
+	/* Given the MMCR1 value, look at the field for each counter to
+	 * determine if it is a marked event.  Code based on the function
+	 * power7_marked_instr_event() in file arch/powerpc/perf/power7-pmu.c.
+	 */
+	for (pmc = 0; pmc < 4; pmc++) {
+		psel = mmcr1 & (OPROFILE_PM_PMCSEL_MSK
+				<< (OPROFILE_MAX_PMC_NUM - pmc)
+				* OPROFILE_MAX_PMC_NUM);
+		psel = (psel >> ((OPROFILE_MAX_PMC_NUM - pmc)
+				 * OPROFILE_PMSEL_FIELD_WIDTH)) & ~1ULL;
+		unit = mmcr1 & (OPROFILE_PM_UNIT_MSK
+				<< (OPROFILE_PM_UNIT_SHIFT
+				    - (pmc * OPROFILE_PMSEL_FIELD_WIDTH )));
+		unit = unit >> (OPROFILE_PM_UNIT_SHIFT
+				- (pmc * OPROFILE_PMSEL_FIELD_WIDTH));
+
+		switch (psel >> 4) {
+		case 2:
+			cntr_marked_events |= (pmc == 1 || pmc == 3) << pmc;
+			break;
+		case 3:
+			if (psel == 0x3c) {
+				cntr_marked_events |= (pmc == 0) << pmc;
+				break;
+			}
+
+			if (psel == 0x3e) {
+				cntr_marked_events |= (pmc != 1) << pmc;
+				break;
+			}
+
+			cntr_marked_events |= 1 << pmc;
+			break;
+		case 4:
+		case 5:
+			cntr_marked_events |= (unit == 0xd) << pmc;
+			break;
+		case 6:
+			if (psel == 0x64)
+				cntr_marked_events |= (pmc >= 2) << pmc;
+			break;
+		case 8:
+			cntr_marked_events |= (unit == 0xd) << pmc;
+			break;
+		}
+	}
+	return cntr_marked_events;
+}
 
 static int power4_reg_setup(struct op_counter_config *ctr,
 			     struct op_system_config *sys,
@@ -48,6 +110,23 @@ static int power4_reg_setup(struct op_counter_config *ctr,
 	mmcr1_val = sys->mmcr1;
 	mmcra_val = sys->mmcra;
 
+	/* Power 7+ and newer architectures:
+	 * Determine which counter events in the group (the group of events is
+	 * specified by the bit settings in the MMCR1 register) are marked
+	 * events for use in the interrupt handler.  Do the calculation once
+	 * before OProfile starts.  Information is used in the interrupt
+	 * handler.  Starting with Power 7+ we only record the sample for
+	 * marked events if the SIAR valid bit is set.  For non marked events
+	 * the sample is always recorded.
+	 */
+	if (__is_processor(PVR_POWER7p))
+		cntr_marked_events = power7_marked_instr_event(mmcr1_val);
+	else
+		cntr_marked_events = 0; /* For older processors, set the bit map
+					 * to zero so the sample will always be
+					 * be recorded.
+					 */
+
 	for (i = 0; i < cur_cpu_spec->num_pmcs; ++i)
 		reset_value[i] = 0x80000000UL - ctr[i].count;
 
@@ -292,6 +371,7 @@ static void power4_handle_interrupt(struct pt_regs *regs,
 	int i;
 	unsigned int mmcr0;
 	unsigned long mmcra;
+	bool siar_valid = false;
 
 	mmcra = mfspr(SPRN_MMCRA);
 
@@ -301,11 +381,29 @@ static void power4_handle_interrupt(struct pt_regs *regs,
 	/* set the PMM bit (see comment below) */
 	mtmsrd(mfmsr() | MSR_PMM);
 
+	/* Check that the SIAR  valid bit in MMCRA is set to 1. */
+	if ((mmcra & MMCRA_SIAR_VALID_MASK) == MMCRA_SIAR_VALID_MASK)
+		siar_valid = true;
+
 	for (i = 0; i < cur_cpu_spec->num_pmcs; ++i) {
 		val = classic_ctr_read(i);
 		if (pmc_overflow(val)) {
 			if (oprofile_running && ctr[i].enabled) {
-				oprofile_add_ext_sample(pc, regs, i, is_kernel);
+				/* Power 7+ and newer architectures:
+				 * If the event is a marked event, then only
+				 * save the sample if the SIAR valid bit is
+				 * set.  If the event is not marked, then
+				 * always save the sample.
+				 * Note, the Sample enable bit in the MMCRA
+				 * register must be set to 1 if the group
+				 * contains a marked event.
+				 */
+				if ((siar_valid &&
+				     (cntr_marked_events & (1 << i)))
+				    || !(cntr_marked_events & (1 << i)))
+					oprofile_add_ext_sample(pc, regs, i,
+								is_kernel);
+
 				classic_ctr_write(i, reset_value[i]);
 			} else {
 				classic_ctr_write(i, 0);
-- 
1.7.12.rc1.22.gbfbf4d4

