From: Carl Love <cel@us.ibm.com>
Subject: powerpc/perf: Sample only if SIAR-Valid bit is set in P7+
Git-commit: e6878835ac4794f25385522d29c634b7bbb7cca9
Patch-mainline: yes
References: fate#314047,bnc#788293

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 e6878835ac4794f25385522d29c634b7bbb7cca9
   Author: sukadev@linux.vnet.ibm.com <sukadev@linux.vnet.ibm.com>
   Date:   Tue Sep 18 20:56:11 2012 +0000

    powerpc/perf: Sample only if SIAR-Valid bit is set in P7+

    On POWER7+ two new bits (mmcra[35] and mmcra[36]) indicate whether the
    contents of SIAR and SDAR are valid.

    For marked instructions on P7+, we must save the contents of SIAR and
    SDAR registers only if these new bits are set.

    This code/check for the SIAR-Valid bit is specific to P7+, so rather than
    waste a CPU-feature bit use the PVR flag.

    Note that Carl Love proposed a similar change for oprofile:

            https://lkml.org/lkml/2012/6/22/309

    Signed-off-by: Sukadev Bhattiprolu <sukadev@linux.vnet.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/include/asm/perf_event_server.h |  2 +-
 arch/powerpc/include/asm/reg.h               |  4 +++
 arch/powerpc/kernel/perf_event.c             | 50 +++++++++++++++++++++++-----
 arch/powerpc/kernel/power7-pmu.c             |  3 ++
 4 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h
index 8f1df12..6dfd981 100644
--- a/arch/powerpc/include/asm/perf_event_server.h
+++ b/arch/powerpc/include/asm/perf_event_server.h
@@ -47,7 +47,7 @@ struct power_pmu {
  */
 #define PPMU_LIMITED_PMC5_6	1	/* PMC5/6 have limited function */
 #define PPMU_ALT_SIPR		2	/* uses alternate posn for SIPR/HV */
-
+#define PPMU_SIAR_VALID         16      /* Processor has SIAR Valid bit */
 /*
  * Values for flags to get_alternatives()
  */
diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h
index 32287b1..568f476 100644
--- a/arch/powerpc/include/asm/reg.h
+++ b/arch/powerpc/include/asm/reg.h
@@ -577,6 +577,10 @@
 #define   POWER6_MMCRA_SIPR   0x0000020000000000ULL
 #define   POWER6_MMCRA_THRM	0x00000020UL
 #define   POWER6_MMCRA_OTHER	0x0000000EUL
+
+#define   POWER7P_MMCRA_SIAR_VALID 0x10000000  /* P7+ SIAR contents valid */
+#define   POWER7P_MMCRA_SDAR_VALID 0x08000000  /* P7+ SDAR contents valid */
+
 #define SPRN_PMC1	787
 #define SPRN_PMC2	788
 #define SPRN_PMC3	789
diff --git a/arch/powerpc/kernel/perf_event.c b/arch/powerpc/kernel/perf_event.c
index 5793c4b..37ab189 100644
--- a/arch/powerpc/kernel/perf_event.c
+++ b/arch/powerpc/kernel/perf_event.c
@@ -79,6 +79,11 @@ static inline int perf_intr_is_nmi(struct pt_regs *regs)
 	return 0;
 }
 
+static inline int siar_valid(struct pt_regs *regs)
+{
+	return 1;
+}
+
 #endif /* CONFIG_PPC32 */
 
 /*
@@ -103,14 +108,20 @@ static inline unsigned long perf_ip_adjust(struct pt_regs *regs)
  * If we're not doing instruction sampling, give them the SDAR
  * (sampled data address).  If we are doing instruction sampling, then
  * only give them the SDAR if it corresponds to the instruction
- * pointed to by SIAR; this is indicated by the [POWER6_]MMCRA_SDSYNC
- * bit in MMCRA.
+ * pointed to by SIAR; this is indicated by the [POWER6_]MMCRA_SDSYNC or
+ * the [POWER7P_]MMCRA_SDAR_VALID bit in MMCRA.
  */
 static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp)
 {
 	unsigned long mmcra = regs->dsisr;
-	unsigned long sdsync = (ppmu->flags & PPMU_ALT_SIPR) ?
-		POWER6_MMCRA_SDSYNC : MMCRA_SDSYNC;
+	unsigned long sdsync;
+
+	if (ppmu->flags & PPMU_SIAR_VALID)
+		sdsync = POWER7P_MMCRA_SDAR_VALID;
+	else if (ppmu->flags & PPMU_ALT_SIPR)
+		sdsync = POWER6_MMCRA_SDSYNC;
+	else
+		sdsync = MMCRA_SDSYNC;
 
 	if (!(mmcra & MMCRA_SAMPLE_ENABLE) || (mmcra & sdsync))
 		*addrp = mfspr(SPRN_SDAR);
@@ -156,6 +167,24 @@ static inline int perf_intr_is_nmi(struct pt_regs *regs)
 	return !regs->softe;
 }
 
+/*
+ * On processors like P7+ that have the SIAR-Valid bit, marked instructions
+ * must be sampled only if the SIAR-valid bit is set.
+ *
+ * For unmarked instructions and for processors that don't have the SIAR-Valid
+ * bit, assume that SIAR is valid.
+ */
+static inline int siar_valid(struct pt_regs *regs)
+{
+	unsigned long mmcra = regs->dsisr;
+	int marked = mmcra & MMCRA_SAMPLE_ENABLE;
+
+	if ((ppmu->flags & PPMU_SIAR_VALID) && marked)
+		return mmcra & POWER7P_MMCRA_SIAR_VALID;
+
+	return 1;
+}
+
 #endif /* CONFIG_PPC64 */
 
 static void perf_event_interrupt(struct pt_regs *regs);
@@ -1240,7 +1269,7 @@ static void record_and_restart(struct perf_event *event, unsigned long val,
 			left += period;
 			if (left <= 0)
 				left = period;
-			record = 1;
+			record = siar_valid(regs);
 			event->hw.last_period = event->hw.sample_period;
 		}
 		if (left < 0x80000000LL)
@@ -1289,13 +1318,18 @@ unsigned long perf_misc_flags(struct pt_regs *regs)
  */
 unsigned long perf_instruction_pointer(struct pt_regs *regs)
 {
-	unsigned long ip;
+	unsigned long use_siar = regs->result;
 
 	if (TRAP(regs) != 0xf00)
 		return regs->nip;	/* not a PMU interrupt */
 
-	ip = mfspr(SPRN_SIAR) + perf_ip_adjust(regs);
-	return ip;
+	if (use_siar && siar_valid(regs))
+		return mfspr(SPRN_SIAR) + perf_ip_adjust(regs);
+	else if (use_siar)
+		return 0;               // no valid instruction pointer
+	else
+		return regs->nip;
+
 }
 
 static bool pmc_overflow(unsigned long val)
diff --git a/arch/powerpc/kernel/power7-pmu.c b/arch/powerpc/kernel/power7-pmu.c
index 593740f..268cbac 100644
--- a/arch/powerpc/kernel/power7-pmu.c
+++ b/arch/powerpc/kernel/power7-pmu.c
@@ -366,6 +366,9 @@ static int init_power7_pmu(void)
 	    strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power7"))
 		return -ENODEV;
 
+	if (__is_processor(PVR_POWER7p))
+		power7_pmu.flags |= PPMU_SIAR_VALID;
+
 	return register_power_pmu(&power7_pmu);
 }
 
-- 
1.7.12.rc1.22.gbfbf4d4

