powerpc/perf: Fix MMCR2 handling for EBB
[cascardo/linux.git] / arch / powerpc / perf / core-book3s.c
index 4520c93..fe52db2 100644 (file)
@@ -485,7 +485,7 @@ static bool is_ebb_event(struct perf_event *event)
         * check that the PMU supports EBB, meaning those that don't can still
         * use bit 63 of the event code for something else if they wish.
         */
-       return (ppmu->flags & PPMU_EBB) &&
+       return (ppmu->flags & PPMU_ARCH_207S) &&
               ((event->attr.config >> PERF_EVENT_CONFIG_EBB_SHIFT) & 1);
 }
 
@@ -777,7 +777,7 @@ void perf_event_print_debug(void)
        if (ppmu->flags & PPMU_HAS_SIER)
                sier = mfspr(SPRN_SIER);
 
-       if (ppmu->flags & PPMU_EBB) {
+       if (ppmu->flags & PPMU_ARCH_207S) {
                pr_info("MMCR2: %016lx EBBHR: %016lx\n",
                        mfspr(SPRN_MMCR2), mfspr(SPRN_EBBHR));
                pr_info("EBBRR: %016lx BESCR: %016lx\n",
@@ -996,7 +996,22 @@ static void power_pmu_read(struct perf_event *event)
        } while (local64_cmpxchg(&event->hw.prev_count, prev, val) != prev);
 
        local64_add(delta, &event->count);
-       local64_sub(delta, &event->hw.period_left);
+
+       /*
+        * A number of places program the PMC with (0x80000000 - period_left).
+        * We never want period_left to be less than 1 because we will program
+        * the PMC with a value >= 0x800000000 and an edge detected PMC will
+        * roll around to 0 before taking an exception. We have seen this
+        * on POWER8.
+        *
+        * To fix this, clamp the minimum value of period_left to 1.
+        */
+       do {
+               prev = local64_read(&event->hw.period_left);
+               val = prev - delta;
+               if (val < 1)
+                       val = 1;
+       } while (local64_cmpxchg(&event->hw.period_left, prev, val) != prev);
 }
 
 /*
@@ -1292,6 +1307,9 @@ static void power_pmu_enable(struct pmu *pmu)
  out_enable:
        pmao_restore_workaround(ebb);
 
+       if (ppmu->flags & PPMU_ARCH_207S)
+               mtspr(SPRN_MMCR2, 0);
+
        mmcr0 = ebb_switch_in(ebb, cpuhw->mmcr[0]);
 
        mb();
@@ -1696,7 +1714,7 @@ static int power_pmu_event_init(struct perf_event *event)
 
        if (has_branch_stack(event)) {
                /* PMU has BHRB enabled */
-               if (!(ppmu->flags & PPMU_BHRB))
+               if (!(ppmu->flags & PPMU_ARCH_207S))
                        return -EOPNOTSUPP;
        }