X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=arch%2Fppc64%2Foprofile%2Fop_model_power4.c;h=b1ca798f4c2908fbfdf7f491ce7fab520a1df6bf;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=46f79d380b500dce31663e72350f33f232ddb9e2;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/arch/ppc64/oprofile/op_model_power4.c b/arch/ppc64/oprofile/op_model_power4.c index 46f79d380..b1ca798f4 100644 --- a/arch/ppc64/oprofile/op_model_power4.c +++ b/arch/ppc64/oprofile/op_model_power4.c @@ -17,13 +17,27 @@ #include #include -#define dbg(args...) printk(args) +#define dbg(args...) #include "op_impl.h" static unsigned long reset_value[OP_MAX_COUNTER]; static int num_counters; +static int oprofile_running; +static int mmcra_has_sihv; + +/* mmcr values are set in power4_reg_setup, used in power4_cpu_setup */ +static u32 mmcr0_val; +static u64 mmcr1_val; +static u32 mmcra_val; + +/* + * Since we do not have an NMI, backtracing through spinlocks is + * only a best guess. In light of this, allow it to be disabled at + * runtime. + */ +static int backtrace_spinlocks; static void power4_reg_setup(struct op_counter_config *ctr, struct op_system_config *sys, @@ -33,18 +47,48 @@ static void power4_reg_setup(struct op_counter_config *ctr, num_counters = num_ctrs; + /* + * SIHV / SIPR bits are only implemented on POWER4+ (GQ) and above. + * However we disable it on all POWER4 until we verify it works + * (I was seeing some strange behaviour last time I tried). + * + * It has been verified to work on POWER5 so we enable it there. + */ + if (cur_cpu_spec->cpu_features & CPU_FTR_MMCRA_SIHV) + mmcra_has_sihv = 1; + + /* + * The performance counter event settings are given in the mmcr0, + * mmcr1 and mmcra values passed from the user in the + * op_system_config structure (sys variable). + */ + mmcr0_val = sys->mmcr0; + mmcr1_val = sys->mmcr1; + mmcra_val = sys->mmcra; + + backtrace_spinlocks = sys->backtrace_spinlocks; + for (i = 0; i < num_counters; ++i) reset_value[i] = 0x80000000UL - ctr[i].count; - /* XXX setup user and kernel profiling */ + /* setup user and kernel profiling */ + if (sys->enable_kernel) + mmcr0_val &= ~MMCR0_KERNEL_DISABLE; + else + mmcr0_val |= MMCR0_KERNEL_DISABLE; + + if (sys->enable_user) + mmcr0_val &= ~MMCR0_PROBLEM_DISABLE; + else + mmcr0_val |= MMCR0_PROBLEM_DISABLE; } extern void ppc64_enable_pmcs(void); static void power4_cpu_setup(void *unused) { - unsigned int mmcr0 = mfspr(SPRN_MMCR0); - unsigned long mmcra = mfspr(SPRN_MMCRA); + unsigned int mmcr0 = mmcr0_val; + unsigned long mmcra = mmcra_val; ppc64_enable_pmcs(); @@ -53,9 +97,11 @@ static void power4_cpu_setup(void *unused) mtspr(SPRN_MMCR0, mmcr0); mmcr0 |= MMCR0_FCM1|MMCR0_PMXE|MMCR0_FCECE; - mmcr0 |= MMCR0_PMC1INTCONTROL|MMCR0_PMCNINTCONTROL; + mmcr0 |= MMCR0_PMC1CE|MMCR0_PMCjCE; mtspr(SPRN_MMCR0, mmcr0); + mtspr(SPRN_MMCR1, mmcr1_val); + mmcra |= MMCRA_SAMPLE_ENABLE; mtspr(SPRN_MMCRA, mmcra); @@ -99,6 +145,8 @@ static void power4_start(struct op_counter_config *ctr) mmcr0 &= ~MMCR0_FC; mtspr(SPRN_MMCR0, mmcr0); + oprofile_running = 1; + dbg("start on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0); } @@ -111,6 +159,8 @@ static void power4_stop(void) mmcr0 |= MMCR0_FC; mtspr(SPRN_MMCR0, mmcr0); + oprofile_running = 0; + dbg("stop on cpu %d, mmcr0 %x\n", smp_processor_id(), mmcr0); mb(); @@ -129,22 +179,38 @@ static void __attribute_used__ kernel_unknown_bucket(void) { } -/* XXX Not currently working */ -static int mmcra_has_sihv = 0; +static unsigned long check_spinlock_pc(struct pt_regs *regs, + unsigned long profile_pc) +{ + unsigned long pc = instruction_pointer(regs); + + /* + * If both the SIAR (sampled instruction) and the perfmon exception + * occurred in a spinlock region then we account the sample to the + * calling function. This isnt 100% correct, we really need soft + * IRQ disable so we always get the perfmon exception at the + * point at which the SIAR is set. + */ + if (backtrace_spinlocks && in_lock_functions(pc) && + in_lock_functions(profile_pc)) + return regs->link; + else + return profile_pc; +} /* * On GQ and newer the MMCRA stores the HV and PR bits at the time * the SIAR was sampled. We use that to work out if the SIAR was sampled in * the hypervisor, our exception vectors or RTAS. */ -static unsigned long get_pc(void) +static unsigned long get_pc(struct pt_regs *regs) { unsigned long pc = mfspr(SPRN_SIAR); unsigned long mmcra; /* Cant do much about it */ if (!mmcra_has_sihv) - return pc; + return check_spinlock_pc(regs, pc); mmcra = mfspr(SPRN_MMCRA); @@ -158,10 +224,6 @@ static unsigned long get_pc(void) if (mmcra & MMCRA_SIPR) return pc; - /* Were we in our exception vectors? */ - if (pc < 0x4000UL) - return (unsigned long)__va(pc); - #ifdef CONFIG_PPC_PSERIES /* Were we in RTAS? */ if (pc >= rtas.base && pc < (rtas.base + rtas.size)) @@ -169,12 +231,16 @@ static unsigned long get_pc(void) return *((unsigned long *)rtas_bucket); #endif + /* Were we in our exception vectors or SLB real mode miss handler? */ + if (pc < 0x1000000UL) + return (unsigned long)__va(pc); + /* Not sure where we were */ if (pc < KERNELBASE) /* function descriptor madness */ return *((unsigned long *)kernel_unknown_bucket); - return pc; + return check_spinlock_pc(regs, pc); } static int get_kernel(unsigned long pc) @@ -198,10 +264,9 @@ static void power4_handle_interrupt(struct pt_regs *regs, int is_kernel; int val; int i; - unsigned int cpu = smp_processor_id(); unsigned int mmcr0; - pc = get_pc(); + pc = get_pc(regs); is_kernel = get_kernel(pc); /* set the PMM bit (see comment below) */ @@ -210,8 +275,8 @@ static void power4_handle_interrupt(struct pt_regs *regs, for (i = 0; i < num_counters; ++i) { val = ctr_read(i); if (val < 0) { - if (ctr[i].enabled) { - oprofile_add_sample(pc, is_kernel, i, cpu); + if (oprofile_running && ctr[i].enabled) { + oprofile_add_pc(pc, is_kernel, i); ctr_write(i, reset_value[i]); } else { ctr_write(i, 0);