#include <linux/oprofile.h>
#include <linux/init.h>
#include <linux/smp.h>
-#include <asm/firmware.h>
#include <asm/ptrace.h>
#include <asm/system.h>
#include <asm/processor.h>
static unsigned long reset_value[OP_MAX_COUNTER];
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 u64 mmcra_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,
{
int i;
+ /*
+ * 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 (cpu_has_feature(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
mmcr1_val = sys->mmcr1;
mmcra_val = sys->mmcra;
+ backtrace_spinlocks = sys->backtrace_spinlocks;
+
for (i = 0; i < cur_cpu_spec->num_pmcs; ++i)
reset_value[i] = 0x80000000UL - ctr[i].count;
{
}
+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
unsigned long mmcra;
/* Cant do much about it */
- if (!cur_cpu_spec->oprofile_mmcra_sihv)
- return pc;
+ if (!mmcra_has_sihv)
+ return check_spinlock_pc(regs, pc);
mmcra = mfspr(SPRN_MMCRA);
/* Were we in the hypervisor? */
- if (firmware_has_feature(FW_FEATURE_LPAR) &&
- (mmcra & cur_cpu_spec->oprofile_mmcra_sihv))
+ if (platform_is_lpar() && (mmcra & MMCRA_SIHV))
/* function descriptor madness */
return *((unsigned long *)hypervisor_bucket);
/* We were in userspace, nothing to do */
- if (mmcra & cur_cpu_spec->oprofile_mmcra_sipr)
+ if (mmcra & MMCRA_SIPR)
return pc;
#ifdef CONFIG_PPC_RTAS
/* function descriptor madness */
return *((unsigned long *)kernel_unknown_bucket);
- return pc;
+ return check_spinlock_pc(regs, pc);
}
-static int get_kernel(unsigned long pc, unsigned long mmcra)
+static int get_kernel(unsigned long pc)
{
int is_kernel;
- if (!cur_cpu_spec->oprofile_mmcra_sihv) {
+ if (!mmcra_has_sihv) {
is_kernel = is_kernel_addr(pc);
} else {
- is_kernel = ((mmcra & cur_cpu_spec->oprofile_mmcra_sipr) == 0);
+ unsigned long mmcra = mfspr(SPRN_MMCRA);
+ is_kernel = ((mmcra & MMCRA_SIPR) == 0);
}
return is_kernel;
int val;
int i;
unsigned int mmcr0;
- unsigned long mmcra;
-
- mmcra = mfspr(SPRN_MMCRA);
pc = get_pc(regs);
- is_kernel = get_kernel(pc, mmcra);
+ is_kernel = get_kernel(pc);
/* set the PMM bit (see comment below) */
mtmsrd(mfmsr() | MSR_PMM);
val = ctr_read(i);
if (val < 0) {
if (oprofile_running && ctr[i].enabled) {
- oprofile_add_ext_sample(pc, regs, i, is_kernel);
+ oprofile_add_pc(pc, is_kernel, i);
ctr_write(i, reset_value[i]);
} else {
ctr_write(i, 0);
*/
mmcr0 &= ~MMCR0_PMAO;
- /* Clear the appropriate bits in the MMCRA */
- mmcra &= ~cur_cpu_spec->oprofile_mmcra_clear;
- mtspr(SPRN_MMCRA, mmcra);
-
/*
* now clear the freeze bit, counting will not start until we
* rfid from this exception, because only at that point will