perf_counter: powerpc: Make powerpc perf_counter code safe for 32-bit kernels

This abstracts a few things in arch/powerpc/kernel/perf_counter.c
that are specific to 64-bit kernels, and provides definitions for
32-bit kernels.  In particular,

* Only 64-bit has MMCRA and the bits in it that give information
  about a PMU interrupt (sampled PR, HV, slot number etc.)
* Only 64-bit has the lppaca and the lppaca->pmcregs_in_use field
* Use of SDAR is confined to 64-bit for now
* Only 64-bit has soft/lazy interrupt disable and therefore
  pseudo-NMIs (interrupts that occur while interrupts are soft-disabled)
* Only 64-bit has PMC7 and PMC8
* Only 64-bit has the MSR_HV bit.

This also fixes the types used in a couple of places, where we were
using long types for things that need to be 64-bit.

Signed-off-by: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: linuxppc-dev@ozlabs.org
Cc: benh@kernel.crashing.org
LKML-Reference: <19000.55590.634126.876084@cargo.ozlabs.ibm.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
This commit is contained in:
Paul Mackerras 2009-06-17 21:53:10 +10:00 committed by Ingo Molnar
parent 079b3c569c
commit 98fb1807b9
1 changed files with 133 additions and 60 deletions

View File

@ -46,6 +46,115 @@ struct power_pmu *ppmu;
*/
static unsigned int freeze_counters_kernel = MMCR0_FCS;
/*
* 32-bit doesn't have MMCRA but does have an MMCR2,
* and a few other names are different.
*/
#ifdef CONFIG_PPC32
#define MMCR0_FCHV 0
#define MMCR0_PMCjCE MMCR0_PMCnCE
#define SPRN_MMCRA SPRN_MMCR2
#define MMCRA_SAMPLE_ENABLE 0
static inline unsigned long perf_ip_adjust(struct pt_regs *regs)
{
return 0;
}
static inline void perf_set_pmu_inuse(int inuse) { }
static inline void perf_get_data_addr(struct pt_regs *regs, u64 *addrp) { }
static inline u32 perf_get_misc_flags(struct pt_regs *regs)
{
return 0;
}
static inline void perf_read_regs(struct pt_regs *regs) { }
static inline int perf_intr_is_nmi(struct pt_regs *regs)
{
return 0;
}
#endif /* CONFIG_PPC32 */
/*
* Things that are specific to 64-bit implementations.
*/
#ifdef CONFIG_PPC64
static inline unsigned long perf_ip_adjust(struct pt_regs *regs)
{
unsigned long mmcra = regs->dsisr;
if ((mmcra & MMCRA_SAMPLE_ENABLE) && !(ppmu->flags & PPMU_ALT_SIPR)) {
unsigned long slot = (mmcra & MMCRA_SLOT) >> MMCRA_SLOT_SHIFT;
if (slot > 1)
return 4 * (slot - 1);
}
return 0;
}
static inline void perf_set_pmu_inuse(int inuse)
{
get_lppaca()->pmcregs_in_use = inuse;
}
/*
* 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.
*/
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;
if (!(mmcra & MMCRA_SAMPLE_ENABLE) || (mmcra & sdsync))
*addrp = mfspr(SPRN_SDAR);
}
static inline u32 perf_get_misc_flags(struct pt_regs *regs)
{
unsigned long mmcra = regs->dsisr;
if (TRAP(regs) != 0xf00)
return 0; /* not a PMU interrupt */
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;
}
/*
* Overload regs->dsisr to store MMCRA so we only need to read it once
* on each interrupt.
*/
static inline void perf_read_regs(struct pt_regs *regs)
{
regs->dsisr = mfspr(SPRN_MMCRA);
}
/*
* If interrupts were soft-disabled when a PMU interrupt occurs, treat
* it as an NMI.
*/
static inline int perf_intr_is_nmi(struct pt_regs *regs)
{
return !regs->softe;
}
#endif /* CONFIG_PPC64 */
static void perf_counter_interrupt(struct pt_regs *regs);
void perf_counter_print_debug(void)
@ -78,12 +187,14 @@ static unsigned long read_pmc(int idx)
case 6:
val = mfspr(SPRN_PMC6);
break;
#ifdef CONFIG_PPC64
case 7:
val = mfspr(SPRN_PMC7);
break;
case 8:
val = mfspr(SPRN_PMC8);
break;
#endif /* CONFIG_PPC64 */
default:
printk(KERN_ERR "oops trying to read PMC%d\n", idx);
val = 0;
@ -115,12 +226,14 @@ static void write_pmc(int idx, unsigned long val)
case 6:
mtspr(SPRN_PMC6, val);
break;
#ifdef CONFIG_PPC64
case 7:
mtspr(SPRN_PMC7, val);
break;
case 8:
mtspr(SPRN_PMC8, val);
break;
#endif /* CONFIG_PPC64 */
default:
printk(KERN_ERR "oops trying to write PMC%d\n", idx);
}
@ -283,7 +396,7 @@ static int check_excludes(struct perf_counter **ctrs, unsigned int cflags[],
static void power_pmu_read(struct perf_counter *counter)
{
long val, delta, prev;
s64 val, delta, prev;
if (!counter->hw.idx)
return;
@ -477,7 +590,7 @@ void hw_perf_enable(void)
mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE);
mtspr(SPRN_MMCR1, cpuhw->mmcr[1]);
if (cpuhw->n_counters == 0)
get_lppaca()->pmcregs_in_use = 0;
perf_set_pmu_inuse(0);
goto out_enable;
}
@ -510,7 +623,7 @@ void hw_perf_enable(void)
* bit set and set the hardware counters to their initial values.
* Then unfreeze the counters.
*/
get_lppaca()->pmcregs_in_use = 1;
perf_set_pmu_inuse(1);
mtspr(SPRN_MMCRA, cpuhw->mmcr[2] & ~MMCRA_SAMPLE_ENABLE);
mtspr(SPRN_MMCR1, cpuhw->mmcr[1]);
mtspr(SPRN_MMCR0, (cpuhw->mmcr[0] & ~(MMCR0_PMC1CE | MMCR0_PMCjCE))
@ -1007,11 +1120,10 @@ const struct pmu *hw_perf_counter_init(struct perf_counter *counter)
* things if requested. Note that interrupts are hard-disabled
* here so there is no possibility of being interrupted.
*/
static void record_and_restart(struct perf_counter *counter, long val,
static void record_and_restart(struct perf_counter *counter, unsigned long val,
struct pt_regs *regs, int nmi)
{
u64 period = counter->hw.sample_period;
unsigned long mmcra, sdsync;
s64 prev, delta, left;
int record = 0;
@ -1033,8 +1145,8 @@ static void record_and_restart(struct perf_counter *counter, long val,
left = period;
record = 1;
}
if (left < 0x80000000L)
val = 0x80000000L - left;
if (left < 0x80000000LL)
val = 0x80000000LL - left;
}
/*
@ -1047,22 +1159,9 @@ static void record_and_restart(struct perf_counter *counter, long val,
.period = counter->hw.last_period,
};
if (counter->attr.sample_type & PERF_SAMPLE_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))
data.addr = mfspr(SPRN_SDAR);
}
if (counter->attr.sample_type & PERF_SAMPLE_ADDR)
perf_get_data_addr(regs, &data.addr);
if (perf_counter_overflow(counter, nmi, &data)) {
/*
* Interrupts are coming too fast - throttle them
@ -1088,25 +1187,12 @@ static void record_and_restart(struct perf_counter *counter, long val,
*/
unsigned long perf_misc_flags(struct pt_regs *regs)
{
unsigned long mmcra;
u32 flags = perf_get_misc_flags(regs);
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;
if (flags)
return flags;
return user_mode(regs) ? PERF_EVENT_MISC_USER :
PERF_EVENT_MISC_KERNEL;
}
/*
@ -1115,20 +1201,12 @@ unsigned long perf_misc_flags(struct pt_regs *regs)
*/
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);
}
ip = mfspr(SPRN_SIAR) + perf_ip_adjust(regs);
return ip;
}
@ -1140,7 +1218,7 @@ static void perf_counter_interrupt(struct pt_regs *regs)
int i;
struct cpu_hw_counters *cpuhw = &__get_cpu_var(cpu_hw_counters);
struct perf_counter *counter;
long val;
unsigned long val;
int found = 0;
int nmi;
@ -1148,16 +1226,9 @@ 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);
perf_read_regs(regs);
/*
* If interrupts were soft-disabled when this PMU interrupt
* occurred, treat it as an NMI.
*/
nmi = !regs->softe;
nmi = perf_intr_is_nmi(regs);
if (nmi)
nmi_enter();
else
@ -1223,11 +1294,13 @@ int register_power_pmu(struct power_pmu *pmu)
pr_info("%s performance monitor hardware support registered\n",
pmu->name);
#ifdef MSR_HV
/*
* Use FCHV to ignore kernel events if MSR.HV is set.
*/
if (mfmsr() & MSR_HV)
freeze_counters_kernel = MMCR0_FCHV;
#endif /* CONFIG_PPC64 */
return 0;
}