Commit c34d5588 authored by Stéphane Eranian's avatar Stéphane Eranian Committed by David Mosberger

[PATCH] ia64: perfmon update

- Cleanup the read/write check routines for pfm_write_pmcs,
  pfm_write_pmds, pfm_read_pmds.

- Autodetect the PMU model. No need to have the kernel compiled for
  Itanium, HP Simulator, or Itanium2. The support for all PMU models
  is included.  Probing is based on processor family and
  platform_name, if necessary.  With this patch, it is possible to use
  an Itanium2 compiled kernel on an Itanium 1 system and get perfmon
  to work.

- Removed remaining dependency on CONFIG_MCKINLEY by adding a new
  field (flags) to pmu_config_t.  Update /proc/perfmon to show the new
  field.

- Fixed a bug in the Itanium2 pmc_ita2_write_check() where an inactive
  PMC13 would be considered active.
parent 7810c5f1
......@@ -86,27 +86,25 @@
#define PFM_REG_CONFIG (0x8<<4|PFM_REG_IMPL) /* configuration register */
#define PFM_REG_BUFFER (0xc<<4|PFM_REG_IMPL) /* PMD used as buffer */
#define PMC_IS_LAST(i) (pmu_conf.pmc_desc[i].type & PFM_REG_END)
#define PMD_IS_LAST(i) (pmu_conf.pmd_desc[i].type & PFM_REG_END)
#define PFM_IS_DISABLED() (pmu_conf.enabled == 0)
#define PMC_IS_LAST(i) (pmu_conf->pmc_desc[i].type & PFM_REG_END)
#define PMD_IS_LAST(i) (pmu_conf->pmd_desc[i].type & PFM_REG_END)
#define PMC_OVFL_NOTIFY(ctx, i) ((ctx)->ctx_pmds[i].flags & PFM_REGFL_OVFL_NOTIFY)
/* i assumed unsigned */
#define PMC_IS_IMPL(i) (i< PMU_MAX_PMCS && (pmu_conf.pmc_desc[i].type & PFM_REG_IMPL))
#define PMD_IS_IMPL(i) (i< PMU_MAX_PMDS && (pmu_conf.pmd_desc[i].type & PFM_REG_IMPL))
#define PMC_IS_IMPL(i) (i< PMU_MAX_PMCS && (pmu_conf->pmc_desc[i].type & PFM_REG_IMPL))
#define PMD_IS_IMPL(i) (i< PMU_MAX_PMDS && (pmu_conf->pmd_desc[i].type & PFM_REG_IMPL))
/* XXX: these assume that register i is implemented */
#define PMD_IS_COUNTING(i) ((pmu_conf.pmd_desc[i].type & PFM_REG_COUNTING) == PFM_REG_COUNTING)
#define PMC_IS_COUNTING(i) ((pmu_conf.pmc_desc[i].type & PFM_REG_COUNTING) == PFM_REG_COUNTING)
#define PMC_IS_MONITOR(i) ((pmu_conf.pmc_desc[i].type & PFM_REG_MONITOR) == PFM_REG_MONITOR)
#define PMC_IS_CONTROL(i) ((pmu_conf.pmc_desc[i].type & PFM_REG_CONTROL) == PFM_REG_CONTROL)
#define PMD_IS_COUNTING(i) ((pmu_conf->pmd_desc[i].type & PFM_REG_COUNTING) == PFM_REG_COUNTING)
#define PMC_IS_COUNTING(i) ((pmu_conf->pmc_desc[i].type & PFM_REG_COUNTING) == PFM_REG_COUNTING)
#define PMC_IS_MONITOR(i) ((pmu_conf->pmc_desc[i].type & PFM_REG_MONITOR) == PFM_REG_MONITOR)
#define PMC_IS_CONTROL(i) ((pmu_conf->pmc_desc[i].type & PFM_REG_CONTROL) == PFM_REG_CONTROL)
#define PMC_DFL_VAL(i) pmu_conf.pmc_desc[i].default_value
#define PMC_RSVD_MASK(i) pmu_conf.pmc_desc[i].reserved_mask
#define PMD_PMD_DEP(i) pmu_conf.pmd_desc[i].dep_pmd[0]
#define PMC_PMD_DEP(i) pmu_conf.pmc_desc[i].dep_pmd[0]
#define PMC_DFL_VAL(i) pmu_conf->pmc_desc[i].default_value
#define PMC_RSVD_MASK(i) pmu_conf->pmc_desc[i].reserved_mask
#define PMD_PMD_DEP(i) pmu_conf->pmd_desc[i].dep_pmd[0]
#define PMC_PMD_DEP(i) pmu_conf->pmc_desc[i].dep_pmd[0]
#define PFM_NUM_IBRS IA64_NUM_DBG_REGS
#define PFM_NUM_DBRS IA64_NUM_DBG_REGS
......@@ -133,6 +131,8 @@
#define PFM_CPUINFO_SET(v) pfm_get_cpu_var(pfm_syst_info) |= (v)
#define PFM_CPUINFO_GET() pfm_get_cpu_var(pfm_syst_info)
#define RDEP(x) (1UL<<(x))
/*
* context protection macros
* in SMP:
......@@ -374,26 +374,32 @@ typedef struct {
* dep_pmd[]: a bitmask of dependent PMD registers
* dep_pmc[]: a bitmask of dependent PMC registers
*/
typedef int (*pfm_reg_check_t)(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
typedef struct {
unsigned int type;
int pm_pos;
unsigned long default_value; /* power-on default value */
unsigned long reserved_mask; /* bitmask of reserved bits */
int (*read_check)(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
int (*write_check)(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
pfm_reg_check_t read_check;
pfm_reg_check_t write_check;
unsigned long dep_pmd[4];
unsigned long dep_pmc[4];
} pfm_reg_desc_t;
/* assume cnum is a valid monitor */
#define PMC_PM(cnum, val) (((val) >> (pmu_conf.pmc_desc[cnum].pm_pos)) & 0x1)
#define PMC_WR_FUNC(cnum) (pmu_conf.pmc_desc[cnum].write_check)
#define PMD_WR_FUNC(cnum) (pmu_conf.pmd_desc[cnum].write_check)
#define PMD_RD_FUNC(cnum) (pmu_conf.pmd_desc[cnum].read_check)
#define PMC_PM(cnum, val) (((val) >> (pmu_conf->pmc_desc[cnum].pm_pos)) & 0x1)
/*
* This structure is initialized at boot time and contains
* a description of the PMU main characteristics.
*
* If the probe function is defined, detection is based
* on its return value:
* - 0 means recognized PMU
* - anything else means not supported
* When the probe function is not defined, then the pmu_family field
* is used and it must match the host CPU family such that:
* - cpu->family & config->pmu_family != 0
*/
typedef struct {
unsigned long ovfl_val; /* overflow value for counters */
......@@ -407,15 +413,18 @@ typedef struct {
unsigned long impl_pmds[4]; /* bitmask of implemented PMDS */
char *pmu_name; /* PMU family name */
unsigned int enabled; /* indicates if perfmon initialized properly */
unsigned int pmu_family; /* cpuid family pattern used to identify pmu */
unsigned int flags; /* pmu specific flags */
unsigned int num_ibrs; /* number of IBRS: computed at init time */
unsigned int num_dbrs; /* number of DBRS: computed at init time */
unsigned int num_counters; /* PMC/PMD counting pairs : computed at init time */
int (*probe)(void); /* customized probe routine */
unsigned int use_rr_dbregs:1; /* set if debug registers used for range restriction */
} pmu_config_t;
/*
* PMU specific flags
*/
#define PFM_PMU_IRQ_RESEND 1 /* PMU needs explicit IRQ resend */
/*
* debug register related type definitions
......@@ -500,6 +509,8 @@ static pfm_uuid_t pfm_null_uuid = {0,};
static spinlock_t pfm_buffer_fmt_lock;
static LIST_HEAD(pfm_buffer_fmt_list);
static pmu_config_t *pmu_conf;
/* sysctl() controls */
static pfm_sysctl_t pfm_sysctl;
int pfm_debug_var;
......@@ -620,20 +631,21 @@ static void pfm_lazy_save_regs (struct task_struct *ta);
#endif
void dump_pmu_state(const char *);
static int pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs);
/*
* the HP simulator must be first because
* CONFIG_IA64_HP_SIM is independent of CONFIG_MCKINLEY or CONFIG_ITANIUM
*/
#if defined(CONFIG_IA64_HP_SIM)
#include "perfmon_hpsim.h"
#elif defined(CONFIG_ITANIUM)
#include "perfmon_itanium.h"
#elif defined(CONFIG_MCKINLEY)
#include "perfmon_mckinley.h"
#else
#include "perfmon_generic.h"
#endif
static pmu_config_t *pmu_confs[]={
&pmu_conf_mck,
&pmu_conf_ita,
&pmu_conf_hpsim,
&pmu_conf_gen, /* must be last */
NULL
};
static int pfm_end_notify_user(pfm_context_t *ctx);
......@@ -723,7 +735,7 @@ pfm_restore_dbrs(unsigned long *dbrs, unsigned int ndbrs)
static inline unsigned long
pfm_read_soft_counter(pfm_context_t *ctx, int i)
{
return ctx->ctx_pmds[i].val + (ia64_get_pmd(i) & pmu_conf.ovfl_val);
return ctx->ctx_pmds[i].val + (ia64_get_pmd(i) & pmu_conf->ovfl_val);
}
/*
......@@ -732,7 +744,7 @@ pfm_read_soft_counter(pfm_context_t *ctx, int i)
static inline void
pfm_write_soft_counter(pfm_context_t *ctx, int i, unsigned long val)
{
unsigned long ovfl_val = pmu_conf.ovfl_val;
unsigned long ovfl_val = pmu_conf->ovfl_val;
ctx->ctx_pmds[i].val = val & ~ovfl_val;
/*
......@@ -878,7 +890,7 @@ pfm_mask_monitoring(struct task_struct *task)
DPRINT_ovfl(("masking monitoring for [%d]\n", task->pid));
ovfl_mask = pmu_conf.ovfl_val;
ovfl_mask = pmu_conf->ovfl_val;
/*
* monitoring can only be masked as a result of a valid
* counter overflow. In UP, it means that the PMU still
......@@ -953,7 +965,7 @@ pfm_restore_monitoring(struct task_struct *task)
int i, is_system;
is_system = ctx->ctx_fl_system;
ovfl_mask = pmu_conf.ovfl_val;
ovfl_mask = pmu_conf->ovfl_val;
if (task != current) {
printk(KERN_ERR "perfmon.%d: invalid task[%d] current[%d]\n", __LINE__, task->pid, current->pid);
......@@ -1024,8 +1036,8 @@ pfm_restore_monitoring(struct task_struct *task)
* XXX: need to optimize
*/
if (ctx->ctx_fl_using_dbreg) {
pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf.num_ibrs);
pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf.num_dbrs);
pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs);
pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs);
}
/*
......@@ -1058,7 +1070,7 @@ static inline void
pfm_restore_pmds(unsigned long *pmds, unsigned long mask)
{
int i;
unsigned long val, ovfl_val = pmu_conf.ovfl_val;
unsigned long val, ovfl_val = pmu_conf->ovfl_val;
for (i=0; mask; i++, mask>>=1) {
if ((mask & 0x1) == 0) continue;
......@@ -1075,7 +1087,7 @@ static inline void
pfm_copy_pmds(struct task_struct *task, pfm_context_t *ctx)
{
struct thread_struct *thread = &task->thread;
unsigned long ovfl_val = pmu_conf.ovfl_val;
unsigned long ovfl_val = pmu_conf->ovfl_val;
unsigned long mask = ctx->ctx_all_pmds[0];
unsigned long val;
int i;
......@@ -2513,12 +2525,12 @@ pfm_reset_pmu_state(pfm_context_t *ctx)
*
* PMC0 is treated differently.
*/
ctx->ctx_all_pmcs[0] = pmu_conf.impl_pmcs[0] & ~0x1;
ctx->ctx_all_pmcs[0] = pmu_conf->impl_pmcs[0] & ~0x1;
/*
* bitmask of all PMDs that are accesible to this context
*/
ctx->ctx_all_pmds[0] = pmu_conf.impl_pmds[0];
ctx->ctx_all_pmds[0] = pmu_conf->impl_pmds[0];
DPRINT(("<%d> all_pmcs=0x%lx all_pmds=0x%lx\n", ctx->ctx_fd, ctx->ctx_all_pmcs[0],ctx->ctx_all_pmds[0]));
......@@ -2858,16 +2870,17 @@ pfm_write_pmcs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
unsigned long value, pmc_pm;
unsigned long smpl_pmds, reset_pmds, impl_pmds;
unsigned int cnum, reg_flags, flags, pmc_type;
int i, can_access_pmu = 0, is_loaded, is_system;
int i, can_access_pmu = 0, is_loaded, is_system, expert_mode;
int is_monitor, is_counting, state;
int ret = -EINVAL;
pfm_reg_check_t wr_func;
#define PFM_CHECK_PMC_PM(x, y, z) ((x)->ctx_fl_system ^ PMC_PM(y, z))
state = ctx->ctx_state;
is_loaded = state == PFM_CTX_LOADED ? 1 : 0;
is_system = ctx->ctx_fl_system;
task = ctx->ctx_task;
impl_pmds = pmu_conf.impl_pmds[0];
impl_pmds = pmu_conf->impl_pmds[0];
if (state == PFM_CTX_ZOMBIE) return -EINVAL;
......@@ -2884,6 +2897,7 @@ pfm_write_pmcs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
}
can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0;
}
expert_mode = pfm_sysctl.expert_mode;
for (i = 0; i < count; i++, req++) {
......@@ -2900,8 +2914,8 @@ pfm_write_pmcs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
goto error;
}
pmc_type = pmu_conf.pmc_desc[cnum].type;
pmc_pm = (value >> pmu_conf.pmc_desc[cnum].pm_pos) & 0x1;
pmc_type = pmu_conf->pmc_desc[cnum].type;
pmc_pm = (value >> pmu_conf->pmc_desc[cnum].pm_pos) & 0x1;
is_counting = (pmc_type & PFM_REG_COUNTING) == PFM_REG_COUNTING ? 1 : 0;
is_monitor = (pmc_type & PFM_REG_MONITOR) == PFM_REG_MONITOR ? 1 : 0;
......@@ -2914,6 +2928,7 @@ pfm_write_pmcs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
DPRINT(("pmc%u is unimplemented or no-access pmc_type=%x\n", cnum, pmc_type));
goto error;
}
wr_func = pmu_conf->pmc_desc[cnum].write_check;
/*
* If the PMC is a monitor, then if the value is not the default:
* - system-wide session: PMCx.pm=1 (privileged monitor)
......@@ -2962,8 +2977,8 @@ pfm_write_pmcs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
/*
* execute write checker, if any
*/
if (pfm_sysctl.expert_mode == 0 && PMC_WR_FUNC(cnum)) {
ret = PMC_WR_FUNC(cnum)(task, ctx, cnum, &value, regs);
if (likely(expert_mode == 0 && wr_func)) {
ret = (*wr_func)(task, ctx, cnum, &value, regs);
if (ret) goto error;
ret = -EINVAL;
}
......@@ -3014,7 +3029,7 @@ pfm_write_pmcs(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
* PMD. Clearing is done indirectly via pfm_reset_pmu_state() so there is no
* possible leak here.
*/
CTX_USED_PMD(ctx, pmu_conf.pmc_desc[cnum].dep_pmd[0]);
CTX_USED_PMD(ctx, pmu_conf->pmc_desc[cnum].dep_pmd[0]);
/*
* keep track of the monitor PMC that we are using.
......@@ -3096,14 +3111,15 @@ pfm_write_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
unsigned long value, hw_value, ovfl_mask;
unsigned int cnum;
int i, can_access_pmu = 0, state;
int is_counting, is_loaded, is_system;
int is_counting, is_loaded, is_system, expert_mode;
int ret = -EINVAL;
pfm_reg_check_t wr_func;
state = ctx->ctx_state;
is_loaded = state == PFM_CTX_LOADED ? 1 : 0;
is_system = ctx->ctx_fl_system;
ovfl_mask = pmu_conf.ovfl_val;
ovfl_mask = pmu_conf->ovfl_val;
task = ctx->ctx_task;
if (unlikely(state == PFM_CTX_ZOMBIE)) return -EINVAL;
......@@ -3125,6 +3141,7 @@ pfm_write_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
}
can_access_pmu = GET_PMU_OWNER() == task || is_system ? 1 : 0;
}
expert_mode = pfm_sysctl.expert_mode;
for (i = 0; i < count; i++, req++) {
......@@ -3136,14 +3153,15 @@ pfm_write_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
goto abort_mission;
}
is_counting = PMD_IS_COUNTING(cnum);
wr_func = pmu_conf->pmd_desc[cnum].write_check;
/*
* execute write checker, if any
*/
if (pfm_sysctl.expert_mode == 0 && PMD_WR_FUNC(cnum)) {
if (unlikely(expert_mode == 0 && wr_func)) {
unsigned long v = value;
ret = PMD_WR_FUNC(cnum)(task, ctx, cnum, &v, regs);
ret = (*wr_func)(task, ctx, cnum, &v, regs);
if (ret) goto abort_mission;
value = v;
......@@ -3289,8 +3307,9 @@ pfm_read_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
pfarg_reg_t *req = (pfarg_reg_t *)arg;
unsigned int cnum, reg_flags = 0;
int i, can_access_pmu = 0, state;
int is_loaded, is_system, is_counting;
int is_loaded, is_system, is_counting, expert_mode;
int ret = -EINVAL;
pfm_reg_check_t rd_func;
/*
* access is possible when loaded only for
......@@ -3300,7 +3319,7 @@ pfm_read_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
state = ctx->ctx_state;
is_loaded = state == PFM_CTX_LOADED ? 1 : 0;
is_system = ctx->ctx_fl_system;
ovfl_mask = pmu_conf.ovfl_val;
ovfl_mask = pmu_conf->ovfl_val;
task = ctx->ctx_task;
if (state == PFM_CTX_ZOMBIE) return -EINVAL;
......@@ -3323,6 +3342,7 @@ pfm_read_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
if (can_access_pmu) ia64_srlz_d();
}
expert_mode = pfm_sysctl.expert_mode;
DPRINT(("loaded=%d access_pmu=%d ctx_state=%d\n",
is_loaded,
......@@ -3369,6 +3389,7 @@ pfm_read_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
*/
val = is_loaded ? thread->pmds[cnum] : 0UL;
}
rd_func = pmu_conf->pmd_desc[cnum].read_check;
if (is_counting) {
/*
......@@ -3381,9 +3402,9 @@ pfm_read_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
/*
* execute read checker, if any
*/
if (unlikely(pfm_sysctl.expert_mode == 0 && PMD_RD_FUNC(cnum))) {
if (unlikely(expert_mode == 0 && rd_func)) {
unsigned long v = val;
ret = PMD_RD_FUNC(cnum)(ctx->ctx_task, ctx, cnum, &v, regs);
ret = (*rd_func)(ctx->ctx_task, ctx, cnum, &v, regs);
if (ret) goto error;
val = v;
ret = -EINVAL;
......@@ -3463,7 +3484,7 @@ pfm_use_debug_registers(struct task_struct *task)
unsigned long flags;
int ret = 0;
if (pmu_conf.use_rr_dbregs == 0) return 0;
if (pmu_conf->use_rr_dbregs == 0) return 0;
DPRINT(("called for [%d]\n", task->pid));
......@@ -3517,7 +3538,7 @@ pfm_release_debug_registers(struct task_struct *task)
unsigned long flags;
int ret;
if (pmu_conf.use_rr_dbregs == 0) return 0;
if (pmu_conf->use_rr_dbregs == 0) return 0;
LOCK_PFS(flags);
if (pfm_sessions.pfs_ptrace_use_dbregs == 0) {
......@@ -3720,7 +3741,7 @@ pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_
int i, can_access_pmu = 0;
int is_system, is_loaded;
if (pmu_conf.use_rr_dbregs == 0) return -EINVAL;
if (pmu_conf->use_rr_dbregs == 0) return -EINVAL;
state = ctx->ctx_state;
is_loaded = state == PFM_CTX_LOADED ? 1 : 0;
......@@ -3802,12 +3823,12 @@ pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_
*/
if (first_time && can_access_pmu) {
DPRINT(("[%d] clearing ibrs, dbrs\n", task->pid));
for (i=0; i < pmu_conf.num_ibrs; i++) {
for (i=0; i < pmu_conf->num_ibrs; i++) {
ia64_set_ibr(i, 0UL);
ia64_srlz_i();
}
ia64_srlz_i();
for (i=0; i < pmu_conf.num_dbrs; i++) {
for (i=0; i < pmu_conf->num_dbrs; i++) {
ia64_set_dbr(i, 0UL);
ia64_srlz_d();
}
......@@ -4367,8 +4388,8 @@ pfm_context_load(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
* guaranteed safe by earlier check against DBG_VALID
*/
if (ctx->ctx_fl_using_dbreg) {
pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf.num_ibrs);
pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf.num_dbrs);
pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs);
pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs);
}
/*
* set new ownership
......@@ -4777,7 +4798,7 @@ sys_perfmonctl (int fd, int cmd, void *arg, int count, long arg5, long arg6, lon
/*
* reject any call if perfmon was disabled at initialization
*/
if (unlikely(PFM_IS_DISABLED())) return -ENOSYS;
if (unlikely(pmu_conf == NULL)) return -ENOSYS;
if (unlikely(cmd < 0 || cmd >= PFM_CMD_COUNT)) {
DPRINT(("invalid cmd=%d\n", cmd));
......@@ -5178,7 +5199,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
tstamp = ia64_get_itc();
mask = pmc0 >> PMU_FIRST_COUNTER;
ovfl_val = pmu_conf.ovfl_val;
ovfl_val = pmu_conf->ovfl_val;
has_smpl = CTX_HAS_SMPL(ctx);
DPRINT_ovfl(("pmc0=0x%lx pid=%d iip=0x%lx, %s "
......@@ -5549,10 +5570,11 @@ pfm_proc_info(char *page)
int i;
p += sprintf(p, "perfmon version : %u.%u\n", PFM_VERSION_MAJ, PFM_VERSION_MIN);
p += sprintf(p, "model : %s\n", pmu_conf.pmu_name);
p += sprintf(p, "model : %s\n", pmu_conf->pmu_name);
p += sprintf(p, "fastctxsw : %s\n", pfm_sysctl.fastctxsw > 0 ? "Yes": "No");
p += sprintf(p, "expert mode : %s\n", pfm_sysctl.expert_mode > 0 ? "Yes": "No");
p += sprintf(p, "ovfl_mask : 0x%lx\n", pmu_conf.ovfl_val);
p += sprintf(p, "ovfl_mask : 0x%lx\n", pmu_conf->ovfl_val);
p += sprintf(p, "flags : 0x%lx\n", pmu_conf->flags);
for(i=0; i < NR_CPUS; i++) {
if (cpu_online(i) == 0) continue;
......@@ -5899,6 +5921,7 @@ pfm_load_regs (struct task_struct *task)
unsigned long pmc_mask = 0UL, pmd_mask = 0UL;
unsigned long flags;
u64 psr, psr_up;
int need_irq_resend;
ctx = PFM_GET_CTX(task);
if (unlikely(ctx == NULL)) return;
......@@ -5919,6 +5942,8 @@ pfm_load_regs (struct task_struct *task)
flags = pfm_protect_ctx_ctxsw(ctx);
psr = pfm_get_psr();
need_irq_resend = pmu_conf->flags & PFM_PMU_IRQ_RESEND;
BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP));
BUG_ON(psr & IA64_PSR_I);
......@@ -5944,8 +5969,8 @@ pfm_load_regs (struct task_struct *task)
* stale state.
*/
if (ctx->ctx_fl_using_dbreg) {
pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf.num_ibrs);
pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf.num_dbrs);
pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs);
pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs);
}
/*
* retrieve saved psr.up
......@@ -6004,12 +6029,12 @@ pfm_load_regs (struct task_struct *task)
ia64_set_pmc(0, t->pmcs[0]);
ia64_srlz_d();
t->pmcs[0] = 0UL;
#ifndef CONFIG_MCKINLEY
/*
* will replay the PMU interrupt
*/
hw_resend_irq(NULL, IA64_PERFMON_VECTOR);
#endif
if (need_irq_resend) hw_resend_irq(NULL, IA64_PERFMON_VECTOR);
pfm_stats[smp_processor_id()].pfm_replay_ovfl_intr_count++;
}
......@@ -6061,6 +6086,7 @@ pfm_load_regs (struct task_struct *task)
struct task_struct *owner;
unsigned long pmd_mask, pmc_mask;
u64 psr, psr_up;
int need_irq_resend;
owner = GET_PMU_OWNER();
ctx = PFM_GET_CTX(task);
......@@ -6079,14 +6105,15 @@ pfm_load_regs (struct task_struct *task)
* (not perfmon) by the previous task.
*/
if (ctx->ctx_fl_using_dbreg) {
pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf.num_ibrs);
pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf.num_dbrs);
pfm_restore_ibrs(ctx->ctx_ibrs, pmu_conf->num_ibrs);
pfm_restore_dbrs(ctx->ctx_dbrs, pmu_conf->num_dbrs);
}
/*
* retrieved saved psr.up
*/
psr_up = ctx->ctx_saved_psr_up;
need_irq_resend = pmu_conf->flags & PFM_PMU_IRQ_RESEND;
/*
* short path, our state is still there, just
......@@ -6143,12 +6170,11 @@ pfm_load_regs (struct task_struct *task)
t->pmcs[0] = 0UL;
#ifndef CONFIG_MCKINLEY
/*
* will replay the PMU interrupt
*/
hw_resend_irq(NULL, IA64_PERFMON_VECTOR);
#endif
if (need_irq_resend) hw_resend_irq(NULL, IA64_PERFMON_VECTOR);
pfm_stats[smp_processor_id()].pfm_replay_ovfl_intr_count++;
}
......@@ -6222,7 +6248,7 @@ pfm_flush_pmds(struct task_struct *task, pfm_context_t *ctx)
*/
task->thread.pmcs[0] = 0;
}
ovfl_val = pmu_conf.ovfl_val;
ovfl_val = pmu_conf->ovfl_val;
/*
* we save all the used pmds
* we take care of overflows for counting PMDs
......@@ -6287,6 +6313,29 @@ static struct irqaction perfmon_irqaction = {
*/
static int init_pfm_fs(void);
static int __init
pfm_probe_pmu(void)
{
pmu_config_t **p;
int family;
family = local_cpu_data->family;
p = pmu_confs;
while(*p) {
if ((*p)->probe) {
if ((*p)->probe() == 0) goto found;
} else if ((*p)->pmu_family == family || (*p)->pmu_family == 0xff) {
goto found;
}
p++;
}
return -1;
found:
pmu_conf = *p;
return 0;
}
int __init
pfm_init(void)
{
......@@ -6297,12 +6346,9 @@ pfm_init(void)
PFM_VERSION_MIN,
IA64_PERFMON_VECTOR);
/*
* PMU type sanity check
* XXX: maybe better to implement autodetection (but then we have a larger kernel)
*/
if (local_cpu_data->family != pmu_conf.pmu_family) {
printk(KERN_INFO "perfmon: disabled, kernel only supports %s PMU family\n", pmu_conf.pmu_name);
if (pfm_probe_pmu()) {
printk(KERN_INFO "perfmon: disabled, there is no support for processor family %d\n",
local_cpu_data->family);
return -ENODEV;
}
......@@ -6313,45 +6359,48 @@ pfm_init(void)
n = 0;
for (i=0; PMC_IS_LAST(i) == 0; i++) {
if (PMC_IS_IMPL(i) == 0) continue;
pmu_conf.impl_pmcs[i>>6] |= 1UL << (i&63);
pmu_conf->impl_pmcs[i>>6] |= 1UL << (i&63);
n++;
}
pmu_conf.num_pmcs = n;
pmu_conf->num_pmcs = n;
n = 0; n_counters = 0;
for (i=0; PMD_IS_LAST(i) == 0; i++) {
if (PMD_IS_IMPL(i) == 0) continue;
pmu_conf.impl_pmds[i>>6] |= 1UL << (i&63);
pmu_conf->impl_pmds[i>>6] |= 1UL << (i&63);
n++;
if (PMD_IS_COUNTING(i)) n_counters++;
}
pmu_conf.num_pmds = n;
pmu_conf.num_counters = n_counters;
pmu_conf->num_pmds = n;
pmu_conf->num_counters = n_counters;
/*
* sanity checks on the number of debug registers
*/
if (pmu_conf.use_rr_dbregs) {
if (pmu_conf.num_ibrs > IA64_NUM_DBG_REGS) {
printk(KERN_INFO "perfmon: unsupported number of code debug registers (%u)\n", pmu_conf.num_ibrs);
if (pmu_conf->use_rr_dbregs) {
if (pmu_conf->num_ibrs > IA64_NUM_DBG_REGS) {
printk(KERN_INFO "perfmon: unsupported number of code debug registers (%u)\n", pmu_conf->num_ibrs);
pmu_conf = NULL;
return -1;
}
if (pmu_conf.num_dbrs > IA64_NUM_DBG_REGS) {
printk(KERN_INFO "perfmon: unsupported number of data debug registers (%u)\n", pmu_conf.num_ibrs);
if (pmu_conf->num_dbrs > IA64_NUM_DBG_REGS) {
printk(KERN_INFO "perfmon: unsupported number of data debug registers (%u)\n", pmu_conf->num_ibrs);
pmu_conf = NULL;
return -1;
}
}
printk("perfmon: %s PMU detected, %u PMCs, %u PMDs, %u counters (%lu bits)\n",
pmu_conf.pmu_name,
pmu_conf.num_pmcs,
pmu_conf.num_pmds,
pmu_conf.num_counters,
ffz(pmu_conf.ovfl_val));
pmu_conf->pmu_name,
pmu_conf->num_pmcs,
pmu_conf->num_pmds,
pmu_conf->num_counters,
ffz(pmu_conf->ovfl_val));
/* sanity check */
if (pmu_conf.num_pmds >= IA64_NUM_PMD_REGS || pmu_conf.num_pmcs >= IA64_NUM_PMC_REGS) {
if (pmu_conf->num_pmds >= IA64_NUM_PMD_REGS || pmu_conf->num_pmcs >= IA64_NUM_PMC_REGS) {
printk(KERN_ERR "perfmon: not enough pmc/pmd, perfmon disabled\n");
pmu_conf = NULL;
return -1;
}
......@@ -6361,6 +6410,7 @@ pfm_init(void)
perfmon_dir = create_proc_read_entry ("perfmon", 0, 0, perfmon_read_entry, NULL);
if (perfmon_dir == NULL) {
printk(KERN_ERR "perfmon: cannot create /proc entry, perfmon disabled\n");
pmu_conf = NULL;
return -1;
}
......@@ -6379,9 +6429,6 @@ pfm_init(void)
for(i=0; i < NR_CPUS; i++) pfm_stats[i].pfm_ovfl_intr_cycles_min = ~0UL;
/* we are all set */
pmu_conf.enabled = 1;
return 0;
}
......@@ -6393,8 +6440,6 @@ __initcall(pfm_init);
void
pfm_init_percpu (void)
{
int i;
/*
* make sure no measurement is active
* (may inherit programmed PMCs from EFI).
......@@ -6412,28 +6457,6 @@ pfm_init_percpu (void)
ia64_setreg(_IA64_REG_CR_PMV, IA64_PERFMON_VECTOR);
ia64_srlz_d();
/*
* we first initialize the PMU to a stable state.
* the values may have been changed from their power-up
* values by software executed before the kernel took over.
*
* At this point, pmu_conf has not yet been initialized
*
* On McKinley, this code is ineffective until PMC4 is initialized
* but that's all right because we take care of pmc0 later.
*
* XXX: potential problems with pmc1.
*/
for (i=1; PMC_IS_LAST(i) == 0; i++) {
if (PMC_IS_IMPL(i) == 0) continue;
ia64_set_pmc(i, PMC_DFL_VAL(i));
}
for (i=0; PMD_IS_LAST(i) == 0; i++) {
if (PMD_IS_IMPL(i) == 0) continue;
ia64_set_pmd(i, 0UL);
}
}
/*
......
......@@ -6,13 +6,6 @@
* Stephane Eranian <eranian@hpl.hp.com>
*/
#define RDEP(x) (1UL<<(x))
#if defined(CONFIG_ITANIUM) || defined (CONFIG_MCKINLEY)
#error "This file should not be used when CONFIG_ITANIUM or CONFIG_MCKINLEY is defined"
#endif
static pfm_reg_desc_t pfm_gen_pmc_desc[PMU_MAX_PMCS]={
/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
......@@ -40,10 +33,9 @@ static pfm_reg_desc_t pfm_gen_pmd_desc[PMU_MAX_PMDS]={
/*
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
*/
static pmu_config_t pmu_conf={
static pmu_config_t pmu_conf_gen={
.pmu_name = "Generic",
.pmu_family = 0xff, /* any */
.enabled = 0,
.ovfl_val = (1UL << 32) - 1,
.num_ibrs = 0, /* does not use */
.num_dbrs = 0, /* does not use */
......
......@@ -14,12 +14,6 @@
* they do not count anything. But you can read/write them.
*/
#define RDEP(x) (1UL<<(x))
#ifndef CONFIG_IA64_HP_SIM
#error "This file should only be included for the HP Simulator"
#endif
static pfm_reg_desc_t pfm_hpsim_pmc_desc[PMU_MAX_PMCS]={
/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL, 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
/* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL, 0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
......@@ -60,16 +54,22 @@ static pfm_reg_desc_t pfm_hpsim_pmd_desc[PMU_MAX_PMDS]={
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
};
static int
pfm_hpsim_probe(void)
{
return local_cpu_data->family == 0x7 && ia64_platform_is("hpsim") ? 0 : -1;
}
/*
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
*/
static pmu_config_t pmu_conf={
static pmu_config_t pmu_conf_hpsim={
.pmu_name = "hpsim",
.pmu_family = 0x7, /* ski emulator reports as Itanium */
.enabled = 0,
.ovfl_val = (1UL << 32) - 1,
.num_ibrs = 0, /* does not use */
.num_dbrs = 0, /* does not use */
.pmd_desc = pfm_hpsim_pmd_desc,
.pmc_desc = pfm_hpsim_pmc_desc
.pmc_desc = pfm_hpsim_pmc_desc,
.probe = pfm_hpsim_probe
};
......@@ -5,15 +5,7 @@
* Copyright (C) 2002-2003 Hewlett Packard Co
* Stephane Eranian <eranian@hpl.hp.com>
*/
#define RDEP(x) (1UL<<(x))
#ifndef CONFIG_ITANIUM
#error "This file is only valid when CONFIG_ITANIUM is defined"
#endif
static int pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
static int pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs);
static pfm_reg_desc_t pfm_ita_pmc_desc[PMU_MAX_PMCS]={
/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
......@@ -55,31 +47,28 @@ static pfm_reg_desc_t pfm_ita_pmd_desc[PMU_MAX_PMDS]={
{ PFM_REG_END , 0, 0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
};
/*
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
*/
static pmu_config_t pmu_conf={
.pmu_name = "Itanium",
.pmu_family = 0x7,
.enabled = 0,
.ovfl_val = (1UL << 32) - 1,
.pmd_desc = pfm_ita_pmd_desc,
.pmc_desc = pfm_ita_pmc_desc,
.num_ibrs = 8,
.num_dbrs = 8,
.use_rr_dbregs = 1 /* debug register are use for range retrictions */
};
static int
pfm_ita_probe(void)
{
return local_cpu_data->family == 0x7 && !ia64_platform_is("hpsim") ? 0 : -1;
}
static int
pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs)
{
int ret;
int is_loaded;
/* sanitfy check */
if (ctx == NULL) return -EINVAL;
is_loaded = ctx->ctx_state == PFM_CTX_LOADED || ctx->ctx_state == PFM_CTX_MASKED;
/*
* we must clear the (instruction) debug registers if pmc13.ta bit is cleared
* before they are written (fl_using_dbreg==0) to avoid picking up stale information.
*/
if (cnum == 13 && ((*val & 0x1) == 0UL) && ctx->ctx_fl_using_dbreg == 0) {
if (cnum == 13 && is_loaded && ((*val & 0x1) == 0UL) && ctx->ctx_fl_using_dbreg == 0) {
DPRINT(("pmc[%d]=0x%lx has active pmc13.ta cleared, clearing ibr\n", cnum, *val));
......@@ -98,7 +87,7 @@ pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnu
* we must clear the (data) debug registers if pmc11.pt bit is cleared
* before they are written (fl_using_dbreg==0) to avoid picking up stale information.
*/
if (cnum == 11 && ((*val >> 28)& 0x1) == 0 && ctx->ctx_fl_using_dbreg == 0) {
if (cnum == 11 && is_loaded && ((*val >> 28)& 0x1) == 0 && ctx->ctx_fl_using_dbreg == 0) {
DPRINT(("pmc[%d]=0x%lx has active pmc11.pt cleared, clearing dbr\n", cnum, *val));
......@@ -115,3 +104,19 @@ pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnu
return 0;
}
/*
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
*/
static pmu_config_t pmu_conf_ita={
.pmu_name = "Itanium",
.pmu_family = 0x7,
.ovfl_val = (1UL << 32) - 1,
.pmd_desc = pfm_ita_pmd_desc,
.pmc_desc = pfm_ita_pmc_desc,
.num_ibrs = 8,
.num_dbrs = 8,
.use_rr_dbregs = 1, /* debug register are use for range retrictions */
.probe = pfm_ita_probe
};
......@@ -5,15 +5,7 @@
* Copyright (C) 2002-2003 Hewlett Packard Co
* Stephane Eranian <eranian@hpl.hp.com>
*/
#define RDEP(x) (1UL<<(x))
#ifndef CONFIG_MCKINLEY
#error "This file is only valid when CONFIG_MCKINLEY is defined"
#endif
static int pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
static int pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs);
static pfm_reg_desc_t pfm_mck_pmc_desc[PMU_MAX_PMCS]={
/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
......@@ -57,21 +49,6 @@ static pfm_reg_desc_t pfm_mck_pmd_desc[PMU_MAX_PMDS]={
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
};
/*
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
*/
static pmu_config_t pmu_conf={
.pmu_name = "Itanium 2",
.pmu_family = 0x1f,
.enabled = 0,
.ovfl_val = (1UL << 47) - 1,
.pmd_desc = pfm_mck_pmd_desc,
.pmc_desc = pfm_mck_pmc_desc,
.num_ibrs = 8,
.num_dbrs = 8,
.use_rr_dbregs = 1 /* debug register are use for range retrictions */
};
/*
* PMC reserved fields must have their power-up values preserved
*/
......@@ -120,12 +97,11 @@ pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnu
* one of the pmc13.cfg_dbrpXX field is different from 0x3
* AND
* at the corresponding pmc13.ena_dbrpXX is set.
*
* For now, we just check on cfg_dbrXX != 0x3.
*/
DPRINT(("cnum=%u val=0x%lx, using_dbreg=%d loaded=%d\n", cnum, *val, ctx->ctx_fl_using_dbreg, is_loaded));
if (cnum == 13 && is_loaded && ((*val & 0x18181818UL) != 0x18181818UL) && ctx->ctx_fl_using_dbreg == 0) {
if (cnum == 13 && is_loaded
&& (*val & 0x1e00000000000UL) && (*val & 0x18181818UL) != 0x18181818UL && ctx->ctx_fl_using_dbreg == 0) {
DPRINT(("pmc[%d]=0x%lx has active pmc13 settings, clearing dbr\n", cnum, *val));
......@@ -192,3 +168,20 @@ pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnu
return ret ? -EINVAL : 0;
}
/*
* impl_pmcs, impl_pmds are computed at runtime to minimize errors!
*/
static pmu_config_t pmu_conf_mck={
.pmu_name = "Itanium 2",
.pmu_family = 0x1f,
.flags = PFM_PMU_IRQ_RESEND,
.ovfl_val = (1UL << 47) - 1,
.pmd_desc = pfm_mck_pmd_desc,
.pmc_desc = pfm_mck_pmc_desc,
.num_ibrs = 8,
.num_dbrs = 8,
.use_rr_dbregs = 1 /* debug register are use for range retrictions */
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment