perf_counter: Rename perf_counter_hw_event => perf_counter_attr
[deliverable/linux.git] / arch / powerpc / kernel / perf_counter.c
index 15cdc8e67229614e915cd82f3088f62f9b7ba783..ea54686cb7878dae52a72496d6eff89fdfe639cb 100644 (file)
@@ -17,6 +17,7 @@
 #include <asm/pmc.h>
 #include <asm/machdep.h>
 #include <asm/firmware.h>
+#include <asm/ptrace.h>
 
 struct cpu_hw_counters {
        int n_counters;
@@ -26,7 +27,7 @@ struct cpu_hw_counters {
        int n_limited;
        u8  pmcs_enabled;
        struct perf_counter *counter[MAX_HWCOUNTERS];
-       unsigned int events[MAX_HWCOUNTERS];
+       u64 events[MAX_HWCOUNTERS];
        unsigned int flags[MAX_HWCOUNTERS];
        u64 mmcr[3];
        struct perf_counter *limited_counter[MAX_LIMITED_HWCOUNTERS];
@@ -131,11 +132,11 @@ static void write_pmc(int idx, unsigned long val)
  * and see if any combination of alternative codes is feasible.
  * The feasible set is returned in event[].
  */
-static int power_check_constraints(unsigned int event[], unsigned int cflags[],
+static int power_check_constraints(u64 event[], unsigned int cflags[],
                                   int n_ev)
 {
        u64 mask, value, nv;
-       unsigned int alternatives[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
+       u64 alternatives[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
        u64 amasks[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
        u64 avalues[MAX_HWCOUNTERS][MAX_EVENT_ALTERNATIVES];
        u64 smasks[MAX_HWCOUNTERS], svalues[MAX_HWCOUNTERS];
@@ -261,13 +262,13 @@ static int check_excludes(struct perf_counter **ctrs, unsigned int cflags[],
                }
                counter = ctrs[i];
                if (first) {
-                       eu = counter->hw_event.exclude_user;
-                       ek = counter->hw_event.exclude_kernel;
-                       eh = counter->hw_event.exclude_hv;
+                       eu = counter->attr.exclude_user;
+                       ek = counter->attr.exclude_kernel;
+                       eh = counter->attr.exclude_hv;
                        first = 0;
-               } else if (counter->hw_event.exclude_user != eu ||
-                          counter->hw_event.exclude_kernel != ek ||
-                          counter->hw_event.exclude_hv != eh) {
+               } else if (counter->attr.exclude_user != eu ||
+                          counter->attr.exclude_kernel != ek ||
+                          counter->attr.exclude_hv != eh) {
                        return -EAGAIN;
                }
        }
@@ -310,7 +311,8 @@ static void power_pmu_read(struct perf_counter *counter)
  */
 static int is_limited_pmc(int pmcnum)
 {
-       return ppmu->limited_pmc5_6 && (pmcnum == 5 || pmcnum == 6);
+       return (ppmu->flags & PPMU_LIMITED_PMC5_6)
+               && (pmcnum == 5 || pmcnum == 6);
 }
 
 static void freeze_limited_counters(struct cpu_hw_counters *cpuhw,
@@ -386,7 +388,7 @@ static void write_mmcr0(struct cpu_hw_counters *cpuhw, unsigned long mmcr0)
  * Disable all counters to prevent PMU interrupts and to allow
  * counters to be added or removed.
  */
-u64 hw_perf_save_disable(void)
+void hw_perf_disable(void)
 {
        struct cpu_hw_counters *cpuhw;
        unsigned long ret;
@@ -428,7 +430,6 @@ u64 hw_perf_save_disable(void)
                mb();
        }
        local_irq_restore(flags);
-       return ret;
 }
 
 /*
@@ -436,7 +437,7 @@ u64 hw_perf_save_disable(void)
  * If we were previously disabled and counters were added, then
  * put the new config on the PMU.
  */
-void hw_perf_restore(u64 disable)
+void hw_perf_enable(void)
 {
        struct perf_counter *counter;
        struct cpu_hw_counters *cpuhw;
@@ -448,10 +449,12 @@ void hw_perf_restore(u64 disable)
        int n_lim;
        int idx;
 
-       if (disable)
-               return;
        local_irq_save(flags);
        cpuhw = &__get_cpu_var(cpu_hw_counters);
+       if (!cpuhw->disabled) {
+               local_irq_restore(flags);
+               return;
+       }
        cpuhw->disabled = 0;
 
        /*
@@ -480,16 +483,16 @@ void hw_perf_restore(u64 disable)
 
        /*
         * Add in MMCR0 freeze bits corresponding to the
-        * hw_event.exclude_* bits for the first counter.
+        * attr.exclude_* bits for the first counter.
         * We have already checked that all counters have the
         * same values for these bits as the first counter.
         */
        counter = cpuhw->counter[0];
-       if (counter->hw_event.exclude_user)
+       if (counter->attr.exclude_user)
                cpuhw->mmcr[0] |= MMCR0_FCP;
-       if (counter->hw_event.exclude_kernel)
+       if (counter->attr.exclude_kernel)
                cpuhw->mmcr[0] |= freeze_counters_kernel;
-       if (counter->hw_event.exclude_hv)
+       if (counter->attr.exclude_hv)
                cpuhw->mmcr[0] |= MMCR0_FCHV;
 
        /*
@@ -532,7 +535,7 @@ void hw_perf_restore(u64 disable)
                        continue;
                }
                val = 0;
-               if (counter->hw_event.irq_period) {
+               if (counter->hw.sample_period) {
                        left = atomic64_read(&counter->hw.period_left);
                        if (left < 0x80000000L)
                                val = 0x80000000L - left;
@@ -562,7 +565,7 @@ void hw_perf_restore(u64 disable)
 }
 
 static int collect_events(struct perf_counter *group, int max_count,
-                         struct perf_counter *ctrs[], unsigned int *events,
+                         struct perf_counter *ctrs[], u64 *events,
                          unsigned int *flags)
 {
        int n = 0;
@@ -649,19 +652,18 @@ int hw_perf_group_sched_in(struct perf_counter *group_leader,
 /*
  * Add a counter to the PMU.
  * If all counters are not already frozen, then we disable and
- * re-enable the PMU in order to get hw_perf_restore to do the
+ * re-enable the PMU in order to get hw_perf_enable to do the
  * actual work of reconfiguring the PMU.
  */
 static int power_pmu_enable(struct perf_counter *counter)
 {
        struct cpu_hw_counters *cpuhw;
        unsigned long flags;
-       u64 pmudis;
        int n0;
        int ret = -EAGAIN;
 
        local_irq_save(flags);
-       pmudis = hw_perf_save_disable();
+       perf_disable();
 
        /*
         * Add the counter to the list (if there is room)
@@ -685,7 +687,7 @@ static int power_pmu_enable(struct perf_counter *counter)
 
        ret = 0;
  out:
-       hw_perf_restore(pmudis);
+       perf_enable();
        local_irq_restore(flags);
        return ret;
 }
@@ -697,11 +699,10 @@ static void power_pmu_disable(struct perf_counter *counter)
 {
        struct cpu_hw_counters *cpuhw;
        long i;
-       u64 pmudis;
        unsigned long flags;
 
        local_irq_save(flags);
-       pmudis = hw_perf_save_disable();
+       perf_disable();
 
        power_pmu_read(counter);
 
@@ -735,7 +736,33 @@ static void power_pmu_disable(struct perf_counter *counter)
                cpuhw->mmcr[0] &= ~(MMCR0_PMXE | MMCR0_FCECE);
        }
 
-       hw_perf_restore(pmudis);
+       perf_enable();
+       local_irq_restore(flags);
+}
+
+/*
+ * Re-enable interrupts on a counter after they were throttled
+ * because they were coming too fast.
+ */
+static void power_pmu_unthrottle(struct perf_counter *counter)
+{
+       s64 val, left;
+       unsigned long flags;
+
+       if (!counter->hw.idx || !counter->hw.sample_period)
+               return;
+       local_irq_save(flags);
+       perf_disable();
+       power_pmu_read(counter);
+       left = counter->hw.sample_period;
+       val = 0;
+       if (left < 0x80000000L)
+               val = 0x80000000L - left;
+       write_pmc(counter->hw.idx, val);
+       atomic64_set(&counter->hw.prev_count, val);
+       atomic64_set(&counter->hw.period_left, left);
+       perf_counter_update_userpage(counter);
+       perf_enable();
        local_irq_restore(flags);
 }
 
@@ -743,6 +770,7 @@ struct pmu power_pmu = {
        .enable         = power_pmu_enable,
        .disable        = power_pmu_disable,
        .read           = power_pmu_read,
+       .unthrottle     = power_pmu_unthrottle,
 };
 
 /*
@@ -752,16 +780,16 @@ struct pmu power_pmu = {
  * that a limited PMC can count, doesn't require interrupts, and
  * doesn't exclude any processor mode.
  */
-static int can_go_on_limited_pmc(struct perf_counter *counter, unsigned int ev,
+static int can_go_on_limited_pmc(struct perf_counter *counter, u64 ev,
                                 unsigned int flags)
 {
        int n;
-       unsigned int alt[MAX_EVENT_ALTERNATIVES];
+       u64 alt[MAX_EVENT_ALTERNATIVES];
 
-       if (counter->hw_event.exclude_user
-           || counter->hw_event.exclude_kernel
-           || counter->hw_event.exclude_hv
-           || counter->hw_event.irq_period)
+       if (counter->attr.exclude_user
+           || counter->attr.exclude_kernel
+           || counter->attr.exclude_hv
+           || counter->attr.sample_period)
                return 0;
 
        if (ppmu->limited_pmc_event(ev))
@@ -776,10 +804,8 @@ static int can_go_on_limited_pmc(struct perf_counter *counter, unsigned int ev,
 
        flags |= PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD;
        n = ppmu->get_alternatives(ev, flags, alt);
-       if (n)
-               return alt[0];
 
-       return 0;
+       return n > 0;
 }
 
 /*
@@ -787,10 +813,9 @@ static int can_go_on_limited_pmc(struct perf_counter *counter, unsigned int ev,
  * and return the event code, or 0 if there is no such alternative.
  * (Note: event code 0 is "don't count" on all machines.)
  */
-static unsigned long normal_pmc_alternative(unsigned long ev,
-                                           unsigned long flags)
+static u64 normal_pmc_alternative(u64 ev, unsigned long flags)
 {
-       unsigned int alt[MAX_EVENT_ALTERNATIVES];
+       u64 alt[MAX_EVENT_ALTERNATIVES];
        int n;
 
        flags &= ~(PPMU_LIMITED_PMC_OK | PPMU_LIMITED_PMC_REQD);
@@ -820,24 +845,23 @@ static void hw_perf_counter_destroy(struct perf_counter *counter)
 
 const struct pmu *hw_perf_counter_init(struct perf_counter *counter)
 {
-       unsigned long ev, flags;
+       u64 ev;
+       unsigned long flags;
        struct perf_counter *ctrs[MAX_HWCOUNTERS];
-       unsigned int events[MAX_HWCOUNTERS];
+       u64 events[MAX_HWCOUNTERS];
        unsigned int cflags[MAX_HWCOUNTERS];
        int n;
        int err;
 
        if (!ppmu)
                return ERR_PTR(-ENXIO);
-       if ((s64)counter->hw_event.irq_period < 0)
-               return ERR_PTR(-EINVAL);
-       if (!perf_event_raw(&counter->hw_event)) {
-               ev = perf_event_id(&counter->hw_event);
+       if (!perf_event_raw(&counter->attr)) {
+               ev = perf_event_id(&counter->attr);
                if (ev >= ppmu->n_generic || ppmu->generic_events[ev] == 0)
                        return ERR_PTR(-EOPNOTSUPP);
                ev = ppmu->generic_events[ev];
        } else {
-               ev = perf_event_config(&counter->hw_event);
+               ev = perf_event_config(&counter->attr);
        }
        counter->hw.config_base = ev;
        counter->hw.idx = 0;
@@ -848,7 +872,7 @@ const struct pmu *hw_perf_counter_init(struct perf_counter *counter)
         * the user set it to.
         */
        if (!firmware_has_feature(FW_FEATURE_LPAR))
-               counter->hw_event.exclude_hv = 0;
+               counter->attr.exclude_hv = 0;
 
        /*
         * If this is a per-task counter, then we can use
@@ -864,7 +888,7 @@ const struct pmu *hw_perf_counter_init(struct perf_counter *counter)
         * If this machine has limited counters, check whether this
         * event could go on a limited counter.
         */
-       if (ppmu->limited_pmc5_6) {
+       if (ppmu->flags & PPMU_LIMITED_PMC5_6) {
                if (can_go_on_limited_pmc(counter, ev, flags)) {
                        flags |= PPMU_LIMITED_PMC_OK;
                } else if (ppmu->limited_pmc_event(ev)) {
@@ -901,7 +925,7 @@ const struct pmu *hw_perf_counter_init(struct perf_counter *counter)
 
        counter->hw.config = events[n];
        counter->hw.counter_base = cflags[n];
-       atomic64_set(&counter->hw.period_left, counter->hw_event.irq_period);
+       atomic64_set(&counter->hw.period_left, counter->hw.sample_period);
 
        /*
         * See if we need to reserve the PMU.
@@ -934,8 +958,10 @@ const struct pmu *hw_perf_counter_init(struct perf_counter *counter)
 static void record_and_restart(struct perf_counter *counter, long val,
                               struct pt_regs *regs, int nmi)
 {
+       u64 period = counter->hw.sample_period;
        s64 prev, delta, left;
        int record = 0;
+       u64 addr, mmcra, sdsync;
 
        /* we don't have to worry about interrupts here */
        prev = atomic64_read(&counter->hw.prev_count);
@@ -948,26 +974,105 @@ static void record_and_restart(struct perf_counter *counter, long val,
         */
        val = 0;
        left = atomic64_read(&counter->hw.period_left) - delta;
-       if (counter->hw_event.irq_period) {
+       if (period) {
                if (left <= 0) {
-                       left += counter->hw_event.irq_period;
+                       left += period;
                        if (left <= 0)
-                               left = counter->hw_event.irq_period;
+                               left = period;
                        record = 1;
                }
                if (left < 0x80000000L)
                        val = 0x80000000L - left;
        }
+
+       /*
+        * Finally record data if requested.
+        */
+       if (record) {
+               addr = 0;
+               if (counter->attr.record_type & PERF_RECORD_ADDR) {
+                       /*
+                        * The user wants a data address recorded.
+                        * 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.
+                        */
+                       mmcra = regs->dsisr;
+                       sdsync = (ppmu->flags & PPMU_ALT_SIPR) ?
+                               POWER6_MMCRA_SDSYNC : MMCRA_SDSYNC;
+                       if (!(mmcra & MMCRA_SAMPLE_ENABLE) || (mmcra & sdsync))
+                               addr = mfspr(SPRN_SDAR);
+               }
+               if (perf_counter_overflow(counter, nmi, regs, addr)) {
+                       /*
+                        * Interrupts are coming too fast - throttle them
+                        * by setting the counter to 0, so it will be
+                        * at least 2^30 cycles until the next interrupt
+                        * (assuming each counter counts at most 2 counts
+                        * per cycle).
+                        */
+                       val = 0;
+                       left = ~0ULL >> 1;
+               }
+       }
+
        write_pmc(counter->hw.idx, val);
        atomic64_set(&counter->hw.prev_count, val);
        atomic64_set(&counter->hw.period_left, left);
        perf_counter_update_userpage(counter);
+}
 
-       /*
-        * Finally record data if requested.
-        */
-       if (record)
-               perf_counter_overflow(counter, nmi, regs, 0);
+/*
+ * Called from generic code to get the misc flags (i.e. processor mode)
+ * for an event.
+ */
+unsigned long perf_misc_flags(struct pt_regs *regs)
+{
+       unsigned long mmcra;
+
+       if (TRAP(regs) != 0xf00) {
+               /* not a PMU interrupt */
+               return user_mode(regs) ? PERF_EVENT_MISC_USER :
+                       PERF_EVENT_MISC_KERNEL;
+       }
+
+       mmcra = regs->dsisr;
+       if (ppmu->flags & PPMU_ALT_SIPR) {
+               if (mmcra & POWER6_MMCRA_SIHV)
+                       return PERF_EVENT_MISC_HYPERVISOR;
+               return (mmcra & POWER6_MMCRA_SIPR) ? PERF_EVENT_MISC_USER :
+                       PERF_EVENT_MISC_KERNEL;
+       }
+       if (mmcra & MMCRA_SIHV)
+               return PERF_EVENT_MISC_HYPERVISOR;
+       return (mmcra & MMCRA_SIPR) ? PERF_EVENT_MISC_USER :
+                       PERF_EVENT_MISC_KERNEL;
+}
+
+/*
+ * Called from generic code to get the instruction pointer
+ * for an event.
+ */
+unsigned long perf_instruction_pointer(struct pt_regs *regs)
+{
+       unsigned long mmcra;
+       unsigned long ip;
+       unsigned long slot;
+
+       if (TRAP(regs) != 0xf00)
+               return regs->nip;       /* not a PMU interrupt */
+
+       ip = mfspr(SPRN_SIAR);
+       mmcra = regs->dsisr;
+       if ((mmcra & MMCRA_SAMPLE_ENABLE) && !(ppmu->flags & PPMU_ALT_SIPR)) {
+               slot = (mmcra & MMCRA_SLOT) >> MMCRA_SLOT_SHIFT;
+               if (slot > 1)
+                       ip += 4 * (slot - 1);
+       }
+       return ip;
 }
 
 /*
@@ -986,6 +1091,11 @@ static void perf_counter_interrupt(struct pt_regs *regs)
                freeze_limited_counters(cpuhw, mfspr(SPRN_PMC5),
                                        mfspr(SPRN_PMC6));
 
+       /*
+        * Overload regs->dsisr to store MMCRA so we only need to read it once.
+        */
+       regs->dsisr = mfspr(SPRN_MMCRA);
+
        /*
         * If interrupts were soft-disabled when this PMU interrupt
         * occurred, treat it as an NMI.
This page took 0.047175 seconds and 5 git commands to generate.