powerpc/perf: Use Instruction Counter value
authorMadhavan Srinivasan <maddy@linux.vnet.ibm.com>
Sun, 12 Feb 2017 17:03:14 +0000 (22:33 +0530)
committerMichael Ellerman <mpe@ellerman.id.au>
Fri, 17 Feb 2017 10:48:54 +0000 (21:48 +1100)
Since PM_INST_DISP include speculative instruction,
based on the workload the dispatch count could vary
considerably. Hence as an alternative, for completed
instruction counting, program the PM_INST_DISP event
to the MMCR* but use Instruction Counter register value.

Signed-off-by: Madhavan Srinivasan <maddy@linux.vnet.ibm.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
arch/powerpc/perf/core-book3s.c

index 270eb9b74e2e13eff5f37b441f76a37a019ff73e..87d17a1f71688753cf24372676e4e76b4cf7b92a 100644 (file)
@@ -57,6 +57,7 @@ struct cpu_hw_events {
        void                            *bhrb_context;
        struct  perf_branch_stack       bhrb_stack;
        struct  perf_branch_entry       bhrb_entries[BHRB_MAX_ENTRIES];
+       u64                             ic_init;
 };
 
 static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events);
@@ -127,6 +128,10 @@ static inline void power_pmu_bhrb_disable(struct perf_event *event) {}
 static void power_pmu_sched_task(struct perf_event_context *ctx, bool sched_in) {}
 static inline void power_pmu_bhrb_read(struct cpu_hw_events *cpuhw) {}
 static void pmao_restore_workaround(bool ebb) { }
+static bool use_ic(u64 event)
+{
+       return false;
+}
 #endif /* CONFIG_PPC32 */
 
 static bool regs_use_siar(struct pt_regs *regs)
@@ -688,6 +693,15 @@ static void pmao_restore_workaround(bool ebb)
        mtspr(SPRN_PMC5, pmcs[4]);
        mtspr(SPRN_PMC6, pmcs[5]);
 }
+
+static bool use_ic(u64 event)
+{
+       if (cpu_has_feature(CPU_FTR_POWER9_DD1) &&
+                       (event == 0x200f2 || event == 0x300f2))
+               return true;
+
+       return false;
+}
 #endif /* CONFIG_PPC64 */
 
 static void perf_event_interrupt(struct pt_regs *regs);
@@ -1007,6 +1021,7 @@ static u64 check_and_compute_delta(u64 prev, u64 val)
 static void power_pmu_read(struct perf_event *event)
 {
        s64 val, delta, prev;
+       struct cpu_hw_events *cpuhw = this_cpu_ptr(&cpu_hw_events);
 
        if (event->hw.state & PERF_HES_STOPPED)
                return;
@@ -1016,6 +1031,13 @@ static void power_pmu_read(struct perf_event *event)
 
        if (is_ebb_event(event)) {
                val = read_pmc(event->hw.idx);
+               if (use_ic(event->attr.config)) {
+                       val = mfspr(SPRN_IC);
+                       if (val > cpuhw->ic_init)
+                               val = val - cpuhw->ic_init;
+                       else
+                               val = val + (0 - cpuhw->ic_init);
+               }
                local64_set(&event->hw.prev_count, val);
                return;
        }
@@ -1029,6 +1051,13 @@ static void power_pmu_read(struct perf_event *event)
                prev = local64_read(&event->hw.prev_count);
                barrier();
                val = read_pmc(event->hw.idx);
+               if (use_ic(event->attr.config)) {
+                       val = mfspr(SPRN_IC);
+                       if (val > cpuhw->ic_init)
+                               val = val - cpuhw->ic_init;
+                       else
+                               val = val + (0 - cpuhw->ic_init);
+               }
                delta = check_and_compute_delta(prev, val);
                if (!delta)
                        return;
@@ -1466,6 +1495,13 @@ nocheck:
                                        event->attr.branch_sample_type);
        }
 
+       /*
+        * Workaround for POWER9 DD1 to use the Instruction Counter
+        * register value for instruction counting
+        */
+       if (use_ic(event->attr.config))
+               cpuhw->ic_init = mfspr(SPRN_IC);
+
        perf_pmu_enable(event->pmu);
        local_irq_restore(flags);
        return ret;