diff --git a/arch/powerpc/include/asm/perf_event_server.h b/arch/powerpc/include/asm/perf_event_server.h index 078019b5b353c916f49dd0d71087260302569c60..9710be3a2d1753e7fef95287d182811e5ce5a0ba 100644 --- a/arch/powerpc/include/asm/perf_event_server.h +++ b/arch/powerpc/include/asm/perf_event_server.h @@ -49,6 +49,7 @@ struct power_pmu { #define PPMU_ALT_SIPR 2 /* uses alternate posn for SIPR/HV */ #define PPMU_NO_SIPR 4 /* no SIPR/HV in MMCRA at all */ #define PPMU_NO_CONT_SAMPLING 8 /* no continuous sampling */ +#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 a1096fb6281673edef18701ef3d9c6e972f8a170..d24c14163966599ed3a78f559f80191f7cd53693 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -606,6 +606,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/perf/core-book3s.c b/arch/powerpc/perf/core-book3s.c index fb55da91aa45028f796170cf63ece7239d9d89b1..0db88f501f91c953c86dcbef4ab2d768be126f9d 100644 --- a/arch/powerpc/perf/core-book3s.c +++ b/arch/powerpc/perf/core-book3s.c @@ -82,6 +82,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 */ /* @@ -106,14 +111,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); @@ -230,6 +241,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); @@ -1291,6 +1320,7 @@ struct pmu power_pmu = { .event_idx = power_pmu_event_idx, }; + /* * A counter has overflowed; update its count and record * things if requested. Note that interrupts are hard-disabled @@ -1324,7 +1354,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) @@ -1374,8 +1404,10 @@ unsigned long perf_instruction_pointer(struct pt_regs *regs) { unsigned long use_siar = regs->result; - if (use_siar) + 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; } diff --git a/arch/powerpc/perf/power7-pmu.c b/arch/powerpc/perf/power7-pmu.c index 1251e4d7e2627b891475cea5231c5f977ef819e7..441af08edf434cc9729d0df344e422f32f487962 100644 --- a/arch/powerpc/perf/power7-pmu.c +++ b/arch/powerpc/perf/power7-pmu.c @@ -373,6 +373,9 @@ static int __init init_power7_pmu(void) strcmp(cur_cpu_spec->oprofile_cpu_type, "ppc64/power7")) return -ENODEV; + if (pvr_version_is(PVR_POWER7p)) + power7_pmu.flags |= PPMU_SIAR_VALID; + return register_power_pmu(&power7_pmu); }