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

[PATCH] ia64: perfmon update

Here is the patch. It is rather big because there is some renaming and
cleanups.  This patch bring 2.5 in line with 2.4.20: perfmon-1.3

It adds:
        - idle task exclusion
        - less ctxsw overhead in system wide
        - cleanups most of the inline asm
        - don't use PAL anymore to determine PMU features
        - added temporary hooks for custom overflow handlers (VTUNE/Oprofile)
        - renaming of the perfmon init functions

Thanks.
parent 51792fe4
...@@ -142,4 +142,8 @@ EXPORT_SYMBOL(efi_dir); ...@@ -142,4 +142,8 @@ EXPORT_SYMBOL(efi_dir);
EXPORT_SYMBOL(ia64_mv); EXPORT_SYMBOL(ia64_mv);
#endif #endif
EXPORT_SYMBOL(machvec_noop); EXPORT_SYMBOL(machvec_noop);
#ifdef CONFIG_PERFMON
#include <asm/perfmon.h>
EXPORT_SYMBOL(pfm_install_alternate_syswide_subsystem);
EXPORT_SYMBOL(pfm_remove_alternate_syswide_subsystem);
#endif
...@@ -178,7 +178,7 @@ init_IRQ (void) ...@@ -178,7 +178,7 @@ init_IRQ (void)
register_percpu_irq(IA64_IPI_VECTOR, &ipi_irqaction); register_percpu_irq(IA64_IPI_VECTOR, &ipi_irqaction);
#endif #endif
#ifdef CONFIG_PERFMON #ifdef CONFIG_PERFMON
perfmon_init_percpu(); pfm_init_percpu();
#endif #endif
platform_irq_init(); platform_irq_init();
} }
......
...@@ -28,7 +28,6 @@ ...@@ -28,7 +28,6 @@
#include <asm/bitops.h> #include <asm/bitops.h>
#include <asm/errno.h> #include <asm/errno.h>
#include <asm/page.h> #include <asm/page.h>
#include <asm/pal.h>
#include <asm/perfmon.h> #include <asm/perfmon.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/signal.h> #include <asm/signal.h>
...@@ -56,8 +55,8 @@ ...@@ -56,8 +55,8 @@
/* /*
* Reset register flags * Reset register flags
*/ */
#define PFM_RELOAD_LONG_RESET 1 #define PFM_PMD_LONG_RESET 1
#define PFM_RELOAD_SHORT_RESET 2 #define PFM_PMD_SHORT_RESET 2
/* /*
* Misc macros and definitions * Misc macros and definitions
...@@ -83,8 +82,10 @@ ...@@ -83,8 +82,10 @@
#define PFM_REG_CONFIG (0x4<<4|PFM_REG_IMPL) /* refine configuration */ #define PFM_REG_CONFIG (0x4<<4|PFM_REG_IMPL) /* refine configuration */
#define PFM_REG_BUFFER (0x5<<4|PFM_REG_IMPL) /* PMD used as buffer */ #define PFM_REG_BUFFER (0x5<<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.pfm_is_disabled #define PFM_IS_DISABLED() pmu_conf.disabled
#define PMC_OVFL_NOTIFY(ctx, i) ((ctx)->ctx_soft_pmds[i].flags & PFM_REGFL_OVFL_NOTIFY) #define PMC_OVFL_NOTIFY(ctx, i) ((ctx)->ctx_soft_pmds[i].flags & PFM_REGFL_OVFL_NOTIFY)
#define PFM_FL_INHERIT_MASK (PFM_FL_INHERIT_NONE|PFM_FL_INHERIT_ONCE|PFM_FL_INHERIT_ALL) #define PFM_FL_INHERIT_MASK (PFM_FL_INHERIT_NONE|PFM_FL_INHERIT_ONCE|PFM_FL_INHERIT_ALL)
...@@ -102,7 +103,6 @@ ...@@ -102,7 +103,6 @@
#define PMD_PMD_DEP(i) pmu_conf.pmd_desc[i].dep_pmd[0] #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_PMD_DEP(i) pmu_conf.pmc_desc[i].dep_pmd[0]
/* k assume unsigned */ /* k assume unsigned */
#define IBR_IS_IMPL(k) (k<pmu_conf.num_ibrs) #define IBR_IS_IMPL(k) (k<pmu_conf.num_ibrs)
#define DBR_IS_IMPL(k) (k<pmu_conf.num_dbrs) #define DBR_IS_IMPL(k) (k<pmu_conf.num_dbrs)
...@@ -131,6 +131,9 @@ ...@@ -131,6 +131,9 @@
#define PFM_REG_RETFLAG_SET(flags, val) do { flags &= ~PFM_REG_RETFL_MASK; flags |= (val); } while(0) #define PFM_REG_RETFLAG_SET(flags, val) do { flags &= ~PFM_REG_RETFL_MASK; flags |= (val); } while(0)
#define PFM_CPUINFO_CLEAR(v) __get_cpu_var(pfm_syst_info) &= ~(v)
#define PFM_CPUINFO_SET(v) __get_cpu_var(pfm_syst_info) |= (v)
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
#define cpu_is_online(i) (cpu_online_map & (1UL << i)) #define cpu_is_online(i) (cpu_online_map & (1UL << i))
#else #else
...@@ -211,7 +214,7 @@ typedef struct { ...@@ -211,7 +214,7 @@ typedef struct {
u64 reset_pmds[4]; /* which other pmds to reset when this counter overflows */ u64 reset_pmds[4]; /* which other pmds to reset when this counter overflows */
u64 seed; /* seed for random-number generator */ u64 seed; /* seed for random-number generator */
u64 mask; /* mask for random-number generator */ u64 mask; /* mask for random-number generator */
int flags; /* notify/do not notify */ unsigned int flags; /* notify/do not notify */
} pfm_counter_t; } pfm_counter_t;
/* /*
...@@ -226,7 +229,8 @@ typedef struct { ...@@ -226,7 +229,8 @@ typedef struct {
unsigned int frozen:1; /* pmu must be kept frozen on ctxsw in */ unsigned int frozen:1; /* pmu must be kept frozen on ctxsw in */
unsigned int protected:1; /* allow access to creator of context only */ unsigned int protected:1; /* allow access to creator of context only */
unsigned int using_dbreg:1; /* using range restrictions (debug registers) */ unsigned int using_dbreg:1; /* using range restrictions (debug registers) */
unsigned int reserved:24; unsigned int excl_idle:1; /* exclude idle task in system wide session */
unsigned int reserved:23;
} pfm_context_flags_t; } pfm_context_flags_t;
/* /*
...@@ -261,7 +265,7 @@ typedef struct pfm_context { ...@@ -261,7 +265,7 @@ typedef struct pfm_context {
u64 ctx_saved_psr; /* copy of psr used for lazy ctxsw */ u64 ctx_saved_psr; /* copy of psr used for lazy ctxsw */
unsigned long ctx_saved_cpus_allowed; /* copy of the task cpus_allowed (system wide) */ unsigned long ctx_saved_cpus_allowed; /* copy of the task cpus_allowed (system wide) */
unsigned long ctx_cpu; /* cpu to which perfmon is applied (system wide) */ unsigned int ctx_cpu; /* CPU used by system wide session */
atomic_t ctx_saving_in_progress; /* flag indicating actual save in progress */ atomic_t ctx_saving_in_progress; /* flag indicating actual save in progress */
atomic_t ctx_is_busy; /* context accessed by overflow handler */ atomic_t ctx_is_busy; /* context accessed by overflow handler */
...@@ -274,6 +278,7 @@ typedef struct pfm_context { ...@@ -274,6 +278,7 @@ typedef struct pfm_context {
#define ctx_fl_frozen ctx_flags.frozen #define ctx_fl_frozen ctx_flags.frozen
#define ctx_fl_protected ctx_flags.protected #define ctx_fl_protected ctx_flags.protected
#define ctx_fl_using_dbreg ctx_flags.using_dbreg #define ctx_fl_using_dbreg ctx_flags.using_dbreg
#define ctx_fl_excl_idle ctx_flags.excl_idle
/* /*
* global information about all sessions * global information about all sessions
...@@ -282,10 +287,10 @@ typedef struct pfm_context { ...@@ -282,10 +287,10 @@ typedef struct pfm_context {
typedef struct { typedef struct {
spinlock_t pfs_lock; /* lock the structure */ spinlock_t pfs_lock; /* lock the structure */
unsigned long pfs_task_sessions; /* number of per task sessions */ unsigned int pfs_task_sessions; /* number of per task sessions */
unsigned long pfs_sys_sessions; /* number of per system wide sessions */ unsigned int pfs_sys_sessions; /* number of per system wide sessions */
unsigned long pfs_sys_use_dbregs; /* incremented when a system wide session uses debug regs */ unsigned int pfs_sys_use_dbregs; /* incremented when a system wide session uses debug regs */
unsigned long pfs_ptrace_use_dbregs; /* incremented when a process uses debug regs */ unsigned int pfs_ptrace_use_dbregs; /* incremented when a process uses debug regs */
struct task_struct *pfs_sys_session[NR_CPUS]; /* point to task owning a system-wide session */ struct task_struct *pfs_sys_session[NR_CPUS]; /* point to task owning a system-wide session */
} pfm_session_t; } pfm_session_t;
...@@ -313,23 +318,22 @@ typedef struct { ...@@ -313,23 +318,22 @@ typedef struct {
/* /*
* This structure is initialized at boot time and contains * This structure is initialized at boot time and contains
* a description of the PMU main characteristic as indicated * a description of the PMU main characteristics.
* by PAL along with a list of inter-registers dependencies and configurations.
*/ */
typedef struct { typedef struct {
unsigned long pfm_is_disabled; /* indicates if perfmon is working properly */ unsigned int disabled; /* indicates if perfmon is working properly */
unsigned long perf_ovfl_val; /* overflow value for generic counters */ unsigned long ovfl_val; /* overflow value for generic counters */
unsigned long max_counters; /* upper limit on counter pair (PMC/PMD) */ unsigned long impl_pmcs[4]; /* bitmask of implemented PMCS */
unsigned long num_pmcs ; /* highest PMC implemented (may have holes) */ unsigned long impl_pmds[4]; /* bitmask of implemented PMDS */
unsigned long num_pmds; /* highest PMD implemented (may have holes) */ unsigned int num_pmcs; /* number of implemented PMCS */
unsigned long impl_regs[16]; /* buffer used to hold implememted PMC/PMD mask */ unsigned int num_pmds; /* number of implemented PMDS */
unsigned long num_ibrs; /* number of instruction debug registers */ unsigned int num_ibrs; /* number of implemented IBRS */
unsigned long num_dbrs; /* number of data debug registers */ unsigned int num_dbrs; /* number of implemented DBRS */
pfm_reg_desc_t *pmc_desc; /* detailed PMC register descriptions */ unsigned int num_counters; /* number of PMD/PMC counters */
pfm_reg_desc_t *pmd_desc; /* detailed PMD register descriptions */ pfm_reg_desc_t *pmc_desc; /* detailed PMC register dependencies descriptions */
pfm_reg_desc_t *pmd_desc; /* detailed PMD register dependencies descriptions */
} pmu_config_t; } pmu_config_t;
/* /*
* structure used to pass argument to/from remote CPU * structure used to pass argument to/from remote CPU
* using IPI to check and possibly save the PMU context on SMP systems. * using IPI to check and possibly save the PMU context on SMP systems.
...@@ -389,13 +393,12 @@ typedef struct { ...@@ -389,13 +393,12 @@ typedef struct {
/* /*
* perfmon internal variables * perfmon internal variables
*/ */
static pmu_config_t pmu_conf; /* PMU configuration */
static pfm_session_t pfm_sessions; /* global sessions information */ static pfm_session_t pfm_sessions; /* global sessions information */
static struct proc_dir_entry *perfmon_dir; /* for debug only */ static struct proc_dir_entry *perfmon_dir; /* for debug only */
static pfm_stats_t pfm_stats[NR_CPUS]; static pfm_stats_t pfm_stats[NR_CPUS];
static pfm_intr_handler_desc_t *pfm_alternate_intr_handler;
DEFINE_PER_CPU(int, pfm_syst_wide); DEFINE_PER_CPU(unsigned long, pfm_syst_info);
static DEFINE_PER_CPU(int, pfm_dcr_pp);
/* sysctl() controls */ /* sysctl() controls */
static pfm_sysctl_t pfm_sysctl; static pfm_sysctl_t pfm_sysctl;
...@@ -449,42 +452,62 @@ static void pfm_lazy_save_regs (struct task_struct *ta); ...@@ -449,42 +452,62 @@ static void pfm_lazy_save_regs (struct task_struct *ta);
#include "perfmon_generic.h" #include "perfmon_generic.h"
#endif #endif
static inline void
pfm_clear_psr_pp(void)
{
__asm__ __volatile__ ("rsm psr.pp;; srlz.i;;"::: "memory");
}
static inline void
pfm_set_psr_pp(void)
{
__asm__ __volatile__ ("ssm psr.pp;; srlz.i;;"::: "memory");
}
static inline void
pfm_clear_psr_up(void)
{
__asm__ __volatile__ ("rum psr.up;; srlz.i;;"::: "memory");
}
static inline void
pfm_set_psr_up(void)
{
__asm__ __volatile__ ("sum psr.up;; srlz.i;;"::: "memory");
}
static inline unsigned long
pfm_get_psr(void)
{
unsigned long tmp;
__asm__ __volatile__ ("mov %0=psr;;": "=r"(tmp) :: "memory");
return tmp;
}
static inline void
pfm_set_psr_l(unsigned long val)
{
__asm__ __volatile__ ("mov psr.l=%0;; srlz.i;;"::"r"(val): "memory");
}
static inline unsigned long static inline unsigned long
pfm_read_soft_counter(pfm_context_t *ctx, int i) pfm_read_soft_counter(pfm_context_t *ctx, int i)
{ {
return ctx->ctx_soft_pmds[i].val + (ia64_get_pmd(i) & pmu_conf.perf_ovfl_val); return ctx->ctx_soft_pmds[i].val + (ia64_get_pmd(i) & pmu_conf.ovfl_val);
} }
static inline void static inline void
pfm_write_soft_counter(pfm_context_t *ctx, int i, unsigned long val) pfm_write_soft_counter(pfm_context_t *ctx, int i, unsigned long val)
{ {
ctx->ctx_soft_pmds[i].val = val & ~pmu_conf.perf_ovfl_val; ctx->ctx_soft_pmds[i].val = val & ~pmu_conf.ovfl_val;
/* /*
* writing to unimplemented part is ignore, so we do not need to * writing to unimplemented part is ignore, so we do not need to
* mask off top part * mask off top part
*/ */
ia64_set_pmd(i, val & pmu_conf.perf_ovfl_val); ia64_set_pmd(i, val & pmu_conf.ovfl_val);
}
/*
* finds the number of PM(C|D) registers given
* the bitvector returned by PAL
*/
static unsigned long __init
find_num_pm_regs(long *buffer)
{
int i=3; /* 4 words/per bitvector */
/* start from the most significant word */
while (i>=0 && buffer[i] == 0 ) i--;
if (i< 0) {
printk(KERN_ERR "perfmon: No bit set in pm_buffer\n");
return 0;
}
return 1+ ia64_fls(buffer[i]) + 64 * i;
} }
/* /*
* Generates a unique (per CPU) timestamp * Generates a unique (per CPU) timestamp
*/ */
...@@ -875,6 +898,120 @@ pfm_smpl_buffer_alloc(pfm_context_t *ctx, unsigned long *which_pmds, unsigned lo ...@@ -875,6 +898,120 @@ pfm_smpl_buffer_alloc(pfm_context_t *ctx, unsigned long *which_pmds, unsigned lo
return -ENOMEM; return -ENOMEM;
} }
static int
pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned long cpu_mask)
{
unsigned long m, undo_mask;
unsigned int n, i;
/*
* validy checks on cpu_mask have been done upstream
*/
LOCK_PFS();
if (is_syswide) {
/*
* cannot mix system wide and per-task sessions
*/
if (pfm_sessions.pfs_task_sessions > 0UL) {
DBprintk(("system wide not possible, %u conflicting task_sessions\n",
pfm_sessions.pfs_task_sessions));
goto abort;
}
m = cpu_mask; undo_mask = 0UL; n = 0;
DBprintk(("cpu_mask=0x%lx\n", cpu_mask));
for(i=0; m; i++, m>>=1) {
if ((m & 0x1) == 0UL) continue;
if (pfm_sessions.pfs_sys_session[i]) goto undo;
DBprintk(("reserving CPU%d currently on CPU%d\n", i, smp_processor_id()));
pfm_sessions.pfs_sys_session[i] = task;
undo_mask |= 1UL << i;
n++;
}
pfm_sessions.pfs_sys_sessions += n;
} else {
if (pfm_sessions.pfs_sys_sessions) goto abort;
pfm_sessions.pfs_task_sessions++;
}
DBprintk(("task_sessions=%u sys_session[%d]=%d",
pfm_sessions.pfs_task_sessions,
smp_processor_id(), pfm_sessions.pfs_sys_session[smp_processor_id()] ? 1 : 0));
UNLOCK_PFS();
return 0;
undo:
DBprintk(("system wide not possible, conflicting session [%d] on CPU%d\n",
pfm_sessions.pfs_sys_session[i]->pid, i));
for(i=0; undo_mask; i++, undo_mask >>=1) {
pfm_sessions.pfs_sys_session[i] = NULL;
}
abort:
UNLOCK_PFS();
return -EBUSY;
}
static int
pfm_unreserve_session(struct task_struct *task, int is_syswide, unsigned long cpu_mask)
{
pfm_context_t *ctx;
unsigned long m;
unsigned int n, i;
ctx = task ? task->thread.pfm_context : NULL;
/*
* validy checks on cpu_mask have been done upstream
*/
LOCK_PFS();
DBprintk(("[%d] sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu_mask=0x%lx\n",
task->pid,
pfm_sessions.pfs_sys_sessions,
pfm_sessions.pfs_task_sessions,
pfm_sessions.pfs_sys_use_dbregs,
is_syswide,
cpu_mask));
if (is_syswide) {
m = cpu_mask; n = 0;
for(i=0; m; i++, m>>=1) {
if ((m & 0x1) == 0UL) continue;
pfm_sessions.pfs_sys_session[i] = NULL;
n++;
}
/*
* would not work with perfmon+more than one bit in cpu_mask
*/
if (ctx && ctx->ctx_fl_using_dbreg) {
if (pfm_sessions.pfs_sys_use_dbregs == 0) {
printk("perfmon: invalid release for [%d] sys_use_dbregs=0\n", task->pid);
} else {
pfm_sessions.pfs_sys_use_dbregs--;
}
}
pfm_sessions.pfs_sys_sessions -= n;
DBprintk(("CPU%d sys_sessions=%u\n",
smp_processor_id(), pfm_sessions.pfs_sys_sessions));
} else {
pfm_sessions.pfs_task_sessions--;
DBprintk(("[%d] task_sessions=%u\n",
task->pid, pfm_sessions.pfs_task_sessions));
}
UNLOCK_PFS();
return 0;
}
/* /*
* XXX: do something better here * XXX: do something better here
*/ */
...@@ -891,6 +1028,7 @@ pfm_bad_permissions(struct task_struct *task) ...@@ -891,6 +1028,7 @@ pfm_bad_permissions(struct task_struct *task)
static int static int
pfx_is_sane(struct task_struct *task, pfarg_context_t *pfx) pfx_is_sane(struct task_struct *task, pfarg_context_t *pfx)
{ {
unsigned long smpl_pmds = pfx->ctx_smpl_regs[0];
int ctx_flags; int ctx_flags;
int cpu; int cpu;
...@@ -957,6 +1095,11 @@ pfx_is_sane(struct task_struct *task, pfarg_context_t *pfx) ...@@ -957,6 +1095,11 @@ pfx_is_sane(struct task_struct *task, pfarg_context_t *pfx)
} }
#endif #endif
} }
/* verify validity of smpl_regs */
if ((smpl_pmds & pmu_conf.impl_pmds[0]) != smpl_pmds) {
DBprintk(("invalid smpl_regs 0x%lx\n", smpl_pmds));
return -EINVAL;
}
/* probably more to add here */ /* probably more to add here */
return 0; return 0;
...@@ -968,7 +1111,7 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int ...@@ -968,7 +1111,7 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int
{ {
pfarg_context_t tmp; pfarg_context_t tmp;
void *uaddr = NULL; void *uaddr = NULL;
int ret, cpu = 0; int ret;
int ctx_flags; int ctx_flags;
pid_t notify_pid; pid_t notify_pid;
...@@ -987,40 +1130,8 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int ...@@ -987,40 +1130,8 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int
ctx_flags = tmp.ctx_flags; ctx_flags = tmp.ctx_flags;
ret = -EBUSY; ret = pfm_reserve_session(task, ctx_flags & PFM_FL_SYSTEM_WIDE, tmp.ctx_cpu_mask);
if (ret) goto abort;
LOCK_PFS();
if (ctx_flags & PFM_FL_SYSTEM_WIDE) {
/* at this point, we know there is at least one bit set */
cpu = ffz(~tmp.ctx_cpu_mask);
DBprintk(("requesting CPU%d currently on CPU%d\n",cpu, smp_processor_id()));
if (pfm_sessions.pfs_task_sessions > 0) {
DBprintk(("system wide not possible, task_sessions=%ld\n", pfm_sessions.pfs_task_sessions));
goto abort;
}
if (pfm_sessions.pfs_sys_session[cpu]) {
DBprintk(("system wide not possible, conflicting session [%d] on CPU%d\n",pfm_sessions.pfs_sys_session[cpu]->pid, cpu));
goto abort;
}
pfm_sessions.pfs_sys_session[cpu] = task;
/*
* count the number of system wide sessions
*/
pfm_sessions.pfs_sys_sessions++;
} else if (pfm_sessions.pfs_sys_sessions == 0) {
pfm_sessions.pfs_task_sessions++;
} else {
/* no per-process monitoring while there is a system wide session */
goto abort;
}
UNLOCK_PFS();
ret = -ENOMEM; ret = -ENOMEM;
...@@ -1103,6 +1214,7 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int ...@@ -1103,6 +1214,7 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int
ctx->ctx_fl_inherit = ctx_flags & PFM_FL_INHERIT_MASK; ctx->ctx_fl_inherit = ctx_flags & PFM_FL_INHERIT_MASK;
ctx->ctx_fl_block = (ctx_flags & PFM_FL_NOTIFY_BLOCK) ? 1 : 0; ctx->ctx_fl_block = (ctx_flags & PFM_FL_NOTIFY_BLOCK) ? 1 : 0;
ctx->ctx_fl_system = (ctx_flags & PFM_FL_SYSTEM_WIDE) ? 1: 0; ctx->ctx_fl_system = (ctx_flags & PFM_FL_SYSTEM_WIDE) ? 1: 0;
ctx->ctx_fl_excl_idle = (ctx_flags & PFM_FL_EXCL_IDLE) ? 1: 0;
ctx->ctx_fl_frozen = 0; ctx->ctx_fl_frozen = 0;
/* /*
* setting this flag to 0 here means, that the creator or the task that the * setting this flag to 0 here means, that the creator or the task that the
...@@ -1113,7 +1225,7 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int ...@@ -1113,7 +1225,7 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int
ctx->ctx_fl_protected = 0; ctx->ctx_fl_protected = 0;
/* for system wide mode only (only 1 bit set) */ /* for system wide mode only (only 1 bit set) */
ctx->ctx_cpu = cpu; ctx->ctx_cpu = ffz(~tmp.ctx_cpu_mask);
atomic_set(&ctx->ctx_last_cpu,-1); /* SMP only, means no CPU */ atomic_set(&ctx->ctx_last_cpu,-1); /* SMP only, means no CPU */
...@@ -1131,9 +1243,9 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int ...@@ -1131,9 +1243,9 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int
DBprintk(("context=%p, pid=%d notify_task=%p\n", DBprintk(("context=%p, pid=%d notify_task=%p\n",
(void *)ctx, task->pid, ctx->ctx_notify_task)); (void *)ctx, task->pid, ctx->ctx_notify_task));
DBprintk(("context=%p, pid=%d flags=0x%x inherit=%d block=%d system=%d\n", DBprintk(("context=%p, pid=%d flags=0x%x inherit=%d block=%d system=%d excl_idle=%d\n",
(void *)ctx, task->pid, ctx_flags, ctx->ctx_fl_inherit, (void *)ctx, task->pid, ctx_flags, ctx->ctx_fl_inherit,
ctx->ctx_fl_block, ctx->ctx_fl_system)); ctx->ctx_fl_block, ctx->ctx_fl_system, ctx->ctx_fl_excl_idle));
/* /*
* when no notification is required, we can make this visible at the last moment * when no notification is required, we can make this visible at the last moment
...@@ -1146,8 +1258,8 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int ...@@ -1146,8 +1258,8 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int
*/ */
if (ctx->ctx_fl_system) { if (ctx->ctx_fl_system) {
ctx->ctx_saved_cpus_allowed = task->cpus_allowed; ctx->ctx_saved_cpus_allowed = task->cpus_allowed;
set_cpus_allowed(task, 1UL << cpu); set_cpus_allowed(task, tmp.ctx_cpu_mask);
DBprintk(("[%d] rescheduled allowed=0x%lx\n", task->pid,task->cpus_allowed)); DBprintk(("[%d] rescheduled allowed=0x%lx\n", task->pid, task->cpus_allowed));
} }
return 0; return 0;
...@@ -1155,20 +1267,8 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int ...@@ -1155,20 +1267,8 @@ pfm_context_create(struct task_struct *task, pfm_context_t *ctx, void *req, int
buffer_error: buffer_error:
pfm_context_free(ctx); pfm_context_free(ctx);
error: error:
/* pfm_unreserve_session(task, ctx_flags & PFM_FL_SYSTEM_WIDE , tmp.ctx_cpu_mask);
* undo session reservation
*/
LOCK_PFS();
if (ctx_flags & PFM_FL_SYSTEM_WIDE) {
pfm_sessions.pfs_sys_session[cpu] = NULL;
pfm_sessions.pfs_sys_sessions--;
} else {
pfm_sessions.pfs_task_sessions--;
}
abort: abort:
UNLOCK_PFS();
/* make sure we don't leave anything behind */ /* make sure we don't leave anything behind */
task->thread.pfm_context = NULL; task->thread.pfm_context = NULL;
...@@ -1200,9 +1300,7 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag) ...@@ -1200,9 +1300,7 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag)
unsigned long mask = ovfl_regs[0]; unsigned long mask = ovfl_regs[0];
unsigned long reset_others = 0UL; unsigned long reset_others = 0UL;
unsigned long val; unsigned long val;
int i, is_long_reset = (flag & PFM_RELOAD_LONG_RESET); int i, is_long_reset = (flag == PFM_PMD_LONG_RESET);
DBprintk(("masks=0x%lx\n", mask));
/* /*
* now restore reset value on sampling overflowed counters * now restore reset value on sampling overflowed counters
...@@ -1213,7 +1311,7 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag) ...@@ -1213,7 +1311,7 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag)
val = pfm_new_counter_value(ctx->ctx_soft_pmds + i, is_long_reset); val = pfm_new_counter_value(ctx->ctx_soft_pmds + i, is_long_reset);
reset_others |= ctx->ctx_soft_pmds[i].reset_pmds[0]; reset_others |= ctx->ctx_soft_pmds[i].reset_pmds[0];
DBprintk(("[%d] %s reset soft_pmd[%d]=%lx\n", current->pid, DBprintk_ovfl(("[%d] %s reset soft_pmd[%d]=%lx\n", current->pid,
is_long_reset ? "long" : "short", i, val)); is_long_reset ? "long" : "short", i, val));
/* upper part is ignored on rval */ /* upper part is ignored on rval */
...@@ -1235,7 +1333,7 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag) ...@@ -1235,7 +1333,7 @@ pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag)
} else { } else {
ia64_set_pmd(i, val); ia64_set_pmd(i, val);
} }
DBprintk(("[%d] %s reset_others pmd[%d]=%lx\n", current->pid, DBprintk_ovfl(("[%d] %s reset_others pmd[%d]=%lx\n", current->pid,
is_long_reset ? "long" : "short", i, val)); is_long_reset ? "long" : "short", i, val));
} }
ia64_srlz_d(); ia64_srlz_d();
...@@ -1246,7 +1344,7 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1246,7 +1344,7 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
{ {
struct thread_struct *th = &task->thread; struct thread_struct *th = &task->thread;
pfarg_reg_t tmp, *req = (pfarg_reg_t *)arg; pfarg_reg_t tmp, *req = (pfarg_reg_t *)arg;
unsigned long value; unsigned long value, reset_pmds;
unsigned int cnum, reg_flags, flags; unsigned int cnum, reg_flags, flags;
int i; int i;
int ret = -EINVAL; int ret = -EINVAL;
...@@ -1262,10 +1360,11 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1262,10 +1360,11 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
if (__copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT; if (__copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT;
cnum = tmp.reg_num; cnum = tmp.reg_num;
reg_flags = tmp.reg_flags; reg_flags = tmp.reg_flags;
value = tmp.reg_value; value = tmp.reg_value;
flags = 0; reset_pmds = tmp.reg_reset_pmds[0];
flags = 0;
/* /*
* we reject all non implemented PMC as well * we reject all non implemented PMC as well
...@@ -1283,6 +1382,8 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1283,6 +1382,8 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
* any other configuration is rejected. * any other configuration is rejected.
*/ */
if (PMC_IS_MONITOR(cnum) || PMC_IS_COUNTING(cnum)) { if (PMC_IS_MONITOR(cnum) || PMC_IS_COUNTING(cnum)) {
DBprintk(("pmc[%u].pm=%ld\n", cnum, PMC_PM(cnum, value)));
if (ctx->ctx_fl_system ^ PMC_PM(cnum, value)) { if (ctx->ctx_fl_system ^ PMC_PM(cnum, value)) {
DBprintk(("pmc_pm=%ld fl_system=%d\n", PMC_PM(cnum, value), ctx->ctx_fl_system)); DBprintk(("pmc_pm=%ld fl_system=%d\n", PMC_PM(cnum, value), ctx->ctx_fl_system));
goto error; goto error;
...@@ -1310,6 +1411,11 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1310,6 +1411,11 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
if (reg_flags & PFM_REGFL_RANDOM) flags |= PFM_REGFL_RANDOM; if (reg_flags & PFM_REGFL_RANDOM) flags |= PFM_REGFL_RANDOM;
/* verify validity of reset_pmds */
if ((reset_pmds & pmu_conf.impl_pmds[0]) != reset_pmds) {
DBprintk(("invalid reset_pmds 0x%lx for pmc%u\n", reset_pmds, cnum));
goto error;
}
} else if (reg_flags & (PFM_REGFL_OVFL_NOTIFY|PFM_REGFL_RANDOM)) { } else if (reg_flags & (PFM_REGFL_OVFL_NOTIFY|PFM_REGFL_RANDOM)) {
DBprintk(("cannot set ovfl_notify or random on pmc%u\n", cnum)); DBprintk(("cannot set ovfl_notify or random on pmc%u\n", cnum));
goto error; goto error;
...@@ -1348,13 +1454,10 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1348,13 +1454,10 @@ pfm_write_pmcs(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
ctx->ctx_soft_pmds[cnum].flags = flags; ctx->ctx_soft_pmds[cnum].flags = flags;
if (PMC_IS_COUNTING(cnum)) { if (PMC_IS_COUNTING(cnum)) {
/* ctx->ctx_soft_pmds[cnum].reset_pmds[0] = reset_pmds;
* copy reset vector
*/ /* mark all PMDS to be accessed as used */
ctx->ctx_soft_pmds[cnum].reset_pmds[0] = tmp.reg_reset_pmds[0]; CTX_USED_PMD(ctx, reset_pmds);
ctx->ctx_soft_pmds[cnum].reset_pmds[1] = tmp.reg_reset_pmds[1];
ctx->ctx_soft_pmds[cnum].reset_pmds[2] = tmp.reg_reset_pmds[2];
ctx->ctx_soft_pmds[cnum].reset_pmds[3] = tmp.reg_reset_pmds[3];
} }
/* /*
...@@ -1397,7 +1500,7 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1397,7 +1500,7 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
unsigned long value, hw_value; unsigned long value, hw_value;
unsigned int cnum; unsigned int cnum;
int i; int i;
int ret; int ret = 0;
/* we don't quite support this right now */ /* we don't quite support this right now */
if (task != current) return -EINVAL; if (task != current) return -EINVAL;
...@@ -1448,9 +1551,9 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1448,9 +1551,9 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
/* update virtualized (64bits) counter */ /* update virtualized (64bits) counter */
if (PMD_IS_COUNTING(cnum)) { if (PMD_IS_COUNTING(cnum)) {
ctx->ctx_soft_pmds[cnum].lval = value; ctx->ctx_soft_pmds[cnum].lval = value;
ctx->ctx_soft_pmds[cnum].val = value & ~pmu_conf.perf_ovfl_val; ctx->ctx_soft_pmds[cnum].val = value & ~pmu_conf.ovfl_val;
hw_value = value & pmu_conf.perf_ovfl_val; hw_value = value & pmu_conf.ovfl_val;
ctx->ctx_soft_pmds[cnum].long_reset = tmp.reg_long_reset; ctx->ctx_soft_pmds[cnum].long_reset = tmp.reg_long_reset;
ctx->ctx_soft_pmds[cnum].short_reset = tmp.reg_short_reset; ctx->ctx_soft_pmds[cnum].short_reset = tmp.reg_short_reset;
...@@ -1478,7 +1581,7 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1478,7 +1581,7 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
ctx->ctx_soft_pmds[cnum].val, ctx->ctx_soft_pmds[cnum].val,
ctx->ctx_soft_pmds[cnum].short_reset, ctx->ctx_soft_pmds[cnum].short_reset,
ctx->ctx_soft_pmds[cnum].long_reset, ctx->ctx_soft_pmds[cnum].long_reset,
ia64_get_pmd(cnum) & pmu_conf.perf_ovfl_val, ia64_get_pmd(cnum) & pmu_conf.ovfl_val,
PMC_OVFL_NOTIFY(ctx, cnum) ? 'Y':'N', PMC_OVFL_NOTIFY(ctx, cnum) ? 'Y':'N',
ctx->ctx_used_pmds[0], ctx->ctx_used_pmds[0],
ctx->ctx_soft_pmds[cnum].reset_pmds[0])); ctx->ctx_soft_pmds[cnum].reset_pmds[0]));
...@@ -1504,15 +1607,18 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun ...@@ -1504,15 +1607,18 @@ pfm_write_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int coun
return ret; return ret;
} }
static int static int
pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs) pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
{ {
struct thread_struct *th = &task->thread; struct thread_struct *th = &task->thread;
unsigned long val = 0UL; unsigned long val, lval;
pfarg_reg_t *req = (pfarg_reg_t *)arg; pfarg_reg_t *req = (pfarg_reg_t *)arg;
unsigned int cnum, reg_flags = 0; unsigned int cnum, reg_flags = 0;
int i, ret = -EINVAL; int i, ret = 0;
#if __GNUC__ < 3
int foo;
#endif
if (!CTX_IS_ENABLED(ctx)) return -EINVAL; if (!CTX_IS_ENABLED(ctx)) return -EINVAL;
...@@ -1528,9 +1634,16 @@ pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count ...@@ -1528,9 +1634,16 @@ pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count
DBprintk(("ctx_last_cpu=%d for [%d]\n", atomic_read(&ctx->ctx_last_cpu), task->pid)); DBprintk(("ctx_last_cpu=%d for [%d]\n", atomic_read(&ctx->ctx_last_cpu), task->pid));
for (i = 0; i < count; i++, req++) { for (i = 0; i < count; i++, req++) {
#if __GNUC__ < 3
foo = __get_user(cnum, &req->reg_num);
if (foo) return -EFAULT;
foo = __get_user(reg_flags, &req->reg_flags);
if (foo) return -EFAULT;
#else
if (__get_user(cnum, &req->reg_num)) return -EFAULT; if (__get_user(cnum, &req->reg_num)) return -EFAULT;
if (__get_user(reg_flags, &req->reg_flags)) return -EFAULT; if (__get_user(reg_flags, &req->reg_flags)) return -EFAULT;
#endif
lval = 0UL;
if (!PMD_IS_IMPL(cnum)) goto abort_mission; if (!PMD_IS_IMPL(cnum)) goto abort_mission;
/* /*
...@@ -1578,9 +1691,10 @@ pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count ...@@ -1578,9 +1691,10 @@ pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count
/* /*
* XXX: need to check for overflow * XXX: need to check for overflow
*/ */
val &= pmu_conf.ovfl_val;
val &= pmu_conf.perf_ovfl_val;
val += ctx->ctx_soft_pmds[cnum].val; val += ctx->ctx_soft_pmds[cnum].val;
lval = ctx->ctx_soft_pmds[cnum].lval;
} }
/* /*
...@@ -1592,10 +1706,11 @@ pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count ...@@ -1592,10 +1706,11 @@ pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count
val = v; val = v;
} }
PFM_REG_RETFLAG_SET(reg_flags, 0); PFM_REG_RETFLAG_SET(reg_flags, ret);
DBprintk(("read pmd[%u] ret=%d value=0x%lx pmc=0x%lx\n", DBprintk(("read pmd[%u] ret=%d value=0x%lx pmc=0x%lx\n",
cnum, ret, val, ia64_get_pmc(cnum))); cnum, ret, val, ia64_get_pmc(cnum)));
/* /*
* update register return value, abort all if problem during copy. * update register return value, abort all if problem during copy.
* we only modify the reg_flags field. no check mode is fine because * we only modify the reg_flags field. no check mode is fine because
...@@ -1604,16 +1719,19 @@ pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count ...@@ -1604,16 +1719,19 @@ pfm_read_pmds(struct task_struct *task, pfm_context_t *ctx, void *arg, int count
if (__put_user(cnum, &req->reg_num)) return -EFAULT; if (__put_user(cnum, &req->reg_num)) return -EFAULT;
if (__put_user(val, &req->reg_value)) return -EFAULT; if (__put_user(val, &req->reg_value)) return -EFAULT;
if (__put_user(reg_flags, &req->reg_flags)) return -EFAULT; if (__put_user(reg_flags, &req->reg_flags)) return -EFAULT;
if (__put_user(lval, &req->reg_last_reset_value)) return -EFAULT;
} }
return 0; return 0;
abort_mission: abort_mission:
PFM_REG_RETFLAG_SET(reg_flags, PFM_REG_RETFL_EINVAL); PFM_REG_RETFLAG_SET(reg_flags, PFM_REG_RETFL_EINVAL);
/*
* XXX: if this fails, we stick with the original failure, flag not updated!
*/
__put_user(reg_flags, &req->reg_flags);
if (__put_user(reg_flags, &req->reg_flags)) ret = -EFAULT; return -EINVAL;
return ret;
} }
#ifdef PFM_PMU_USES_DBR #ifdef PFM_PMU_USES_DBR
...@@ -1655,7 +1773,7 @@ pfm_use_debug_registers(struct task_struct *task) ...@@ -1655,7 +1773,7 @@ pfm_use_debug_registers(struct task_struct *task)
else else
pfm_sessions.pfs_ptrace_use_dbregs++; pfm_sessions.pfs_ptrace_use_dbregs++;
DBprintk(("ptrace_use_dbregs=%lu sys_use_dbregs=%lu by [%d] ret = %d\n", DBprintk(("ptrace_use_dbregs=%u sys_use_dbregs=%u by [%d] ret = %d\n",
pfm_sessions.pfs_ptrace_use_dbregs, pfm_sessions.pfs_ptrace_use_dbregs,
pfm_sessions.pfs_sys_use_dbregs, pfm_sessions.pfs_sys_use_dbregs,
task->pid, ret)); task->pid, ret));
...@@ -1673,7 +1791,6 @@ pfm_use_debug_registers(struct task_struct *task) ...@@ -1673,7 +1791,6 @@ pfm_use_debug_registers(struct task_struct *task)
* perfmormance monitoring, so we only decrement the number * perfmormance monitoring, so we only decrement the number
* of "ptraced" debug register users to keep the count up to date * of "ptraced" debug register users to keep the count up to date
*/ */
int int
pfm_release_debug_registers(struct task_struct *task) pfm_release_debug_registers(struct task_struct *task)
{ {
...@@ -1702,6 +1819,7 @@ pfm_use_debug_registers(struct task_struct *task) ...@@ -1702,6 +1819,7 @@ pfm_use_debug_registers(struct task_struct *task)
{ {
return 0; return 0;
} }
int int
pfm_release_debug_registers(struct task_struct *task) pfm_release_debug_registers(struct task_struct *task)
{ {
...@@ -1721,9 +1839,12 @@ pfm_restart(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, ...@@ -1721,9 +1839,12 @@ pfm_restart(struct task_struct *task, pfm_context_t *ctx, void *arg, int count,
if (!CTX_IS_ENABLED(ctx)) return -EINVAL; if (!CTX_IS_ENABLED(ctx)) return -EINVAL;
if (task == current) { if (task == current) {
DBprintk(("restarting self %d frozen=%d \n", current->pid, ctx->ctx_fl_frozen)); DBprintk(("restarting self %d frozen=%d ovfl_regs=0x%lx\n",
task->pid,
ctx->ctx_fl_frozen,
ctx->ctx_ovfl_regs[0]));
pfm_reset_regs(ctx, ctx->ctx_ovfl_regs, PFM_RELOAD_LONG_RESET); pfm_reset_regs(ctx, ctx->ctx_ovfl_regs, PFM_PMD_LONG_RESET);
ctx->ctx_ovfl_regs[0] = 0UL; ctx->ctx_ovfl_regs[0] = 0UL;
...@@ -1806,18 +1927,18 @@ pfm_stop(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, ...@@ -1806,18 +1927,18 @@ pfm_stop(struct task_struct *task, pfm_context_t *ctx, void *arg, int count,
ia64_set_dcr(ia64_get_dcr() & ~IA64_DCR_PP); ia64_set_dcr(ia64_get_dcr() & ~IA64_DCR_PP);
/* stop monitoring */ /* stop monitoring */
__asm__ __volatile__ ("rsm psr.pp;;"::: "memory"); pfm_clear_psr_pp();
ia64_srlz_i(); ia64_srlz_i();
__get_cpu_var(pfm_dcr_pp) = 0; PFM_CPUINFO_CLEAR(PFM_CPUINFO_DCR_PP);
ia64_psr(regs)->pp = 0; ia64_psr(regs)->pp = 0;
} else { } else {
/* stop monitoring */ /* stop monitoring */
__asm__ __volatile__ ("rum psr.up;;"::: "memory"); pfm_clear_psr_up();
ia64_srlz_i(); ia64_srlz_i();
...@@ -1979,14 +2100,9 @@ pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, stru ...@@ -1979,14 +2100,9 @@ pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, stru
int i, ret = 0; int i, ret = 0;
/* /*
* for range restriction: psr.db must be cleared or the * we do not need to check for ipsr.db because we do clear ibr.x, dbr.r, and dbr.w
* the PMU will ignore the debug registers. * ensuring that no real breakpoint can be installed via this call.
*
* XXX: may need more in system wide mode,
* no task can have this bit set?
*/ */
if (ia64_psr(regs)->db == 1) return -EINVAL;
first_time = ctx->ctx_fl_using_dbreg == 0; first_time = ctx->ctx_fl_using_dbreg == 0;
...@@ -2055,7 +2171,6 @@ pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, stru ...@@ -2055,7 +2171,6 @@ pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, stru
* Now install the values into the registers * Now install the values into the registers
*/ */
for (i = 0; i < count; i++, req++) { for (i = 0; i < count; i++, req++) {
if (__copy_from_user(&tmp, req, sizeof(tmp))) goto abort_mission; if (__copy_from_user(&tmp, req, sizeof(tmp))) goto abort_mission;
...@@ -2145,7 +2260,7 @@ pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, stru ...@@ -2145,7 +2260,7 @@ pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, stru
* XXX: for now we can only come here on EINVAL * XXX: for now we can only come here on EINVAL
*/ */
PFM_REG_RETFLAG_SET(tmp.dbreg_flags, PFM_REG_RETFL_EINVAL); PFM_REG_RETFLAG_SET(tmp.dbreg_flags, PFM_REG_RETFL_EINVAL);
__put_user(tmp.dbreg_flags, &req->dbreg_flags); if (__put_user(tmp.dbreg_flags, &req->dbreg_flags)) ret = -EFAULT;
} }
return ret; return ret;
} }
...@@ -2215,13 +2330,13 @@ pfm_start(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, ...@@ -2215,13 +2330,13 @@ pfm_start(struct task_struct *task, pfm_context_t *ctx, void *arg, int count,
if (ctx->ctx_fl_system) { if (ctx->ctx_fl_system) {
__get_cpu_var(pfm_dcr_pp) = 1; PFM_CPUINFO_SET(PFM_CPUINFO_DCR_PP);
/* set user level psr.pp */ /* set user level psr.pp */
ia64_psr(regs)->pp = 1; ia64_psr(regs)->pp = 1;
/* start monitoring at kernel level */ /* start monitoring at kernel level */
__asm__ __volatile__ ("ssm psr.pp;;"::: "memory"); pfm_set_psr_pp();
/* enable dcr pp */ /* enable dcr pp */
ia64_set_dcr(ia64_get_dcr()|IA64_DCR_PP); ia64_set_dcr(ia64_get_dcr()|IA64_DCR_PP);
...@@ -2237,7 +2352,7 @@ pfm_start(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, ...@@ -2237,7 +2352,7 @@ pfm_start(struct task_struct *task, pfm_context_t *ctx, void *arg, int count,
ia64_psr(regs)->up = 1; ia64_psr(regs)->up = 1;
/* start monitoring at kernel level */ /* start monitoring at kernel level */
__asm__ __volatile__ ("sum psr.up;;"::: "memory"); pfm_set_psr_up();
ia64_srlz_i(); ia64_srlz_i();
} }
...@@ -2264,11 +2379,12 @@ pfm_enable(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, ...@@ -2264,11 +2379,12 @@ pfm_enable(struct task_struct *task, pfm_context_t *ctx, void *arg, int count,
ia64_psr(regs)->up = 0; /* just to make sure! */ ia64_psr(regs)->up = 0; /* just to make sure! */
/* make sure monitoring is stopped */ /* make sure monitoring is stopped */
__asm__ __volatile__ ("rsm psr.pp;;"::: "memory"); pfm_clear_psr_pp();
ia64_srlz_i(); ia64_srlz_i();
__get_cpu_var(pfm_dcr_pp) = 0; PFM_CPUINFO_CLEAR(PFM_CPUINFO_DCR_PP);
__get_cpu_var(pfm_syst_wide) = 1; PFM_CPUINFO_SET(PFM_CPUINFO_SYST_WIDE);
if (ctx->ctx_fl_excl_idle) PFM_CPUINFO_SET(PFM_CPUINFO_EXCL_IDLE);
} else { } else {
/* /*
* needed in case the task was a passive task during * needed in case the task was a passive task during
...@@ -2279,7 +2395,7 @@ pfm_enable(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, ...@@ -2279,7 +2395,7 @@ pfm_enable(struct task_struct *task, pfm_context_t *ctx, void *arg, int count,
ia64_psr(regs)->up = 0; ia64_psr(regs)->up = 0;
/* make sure monitoring is stopped */ /* make sure monitoring is stopped */
__asm__ __volatile__ ("rum psr.up;;"::: "memory"); pfm_clear_psr_up();
ia64_srlz_i(); ia64_srlz_i();
DBprintk(("clearing psr.sp for [%d]\n", current->pid)); DBprintk(("clearing psr.sp for [%d]\n", current->pid));
...@@ -2331,6 +2447,7 @@ pfm_get_pmc_reset(struct task_struct *task, pfm_context_t *ctx, void *arg, int c ...@@ -2331,6 +2447,7 @@ pfm_get_pmc_reset(struct task_struct *task, pfm_context_t *ctx, void *arg, int c
abort_mission: abort_mission:
PFM_REG_RETFLAG_SET(tmp.reg_flags, PFM_REG_RETFL_EINVAL); PFM_REG_RETFLAG_SET(tmp.reg_flags, PFM_REG_RETFL_EINVAL);
if (__copy_to_user(req, &tmp, sizeof(tmp))) ret = -EFAULT; if (__copy_to_user(req, &tmp, sizeof(tmp))) ret = -EFAULT;
return ret; return ret;
} }
...@@ -2532,7 +2649,7 @@ pfm_ovfl_block_reset(void) ...@@ -2532,7 +2649,7 @@ pfm_ovfl_block_reset(void)
* use the local reference * use the local reference
*/ */
pfm_reset_regs(ctx, ctx->ctx_ovfl_regs, PFM_RELOAD_LONG_RESET); pfm_reset_regs(ctx, ctx->ctx_ovfl_regs, PFM_PMD_LONG_RESET);
ctx->ctx_ovfl_regs[0] = 0UL; ctx->ctx_ovfl_regs[0] = 0UL;
...@@ -2591,19 +2708,11 @@ pfm_record_sample(struct task_struct *task, pfm_context_t *ctx, unsigned long ov ...@@ -2591,19 +2708,11 @@ pfm_record_sample(struct task_struct *task, pfm_context_t *ctx, unsigned long ov
h->pid = current->pid; h->pid = current->pid;
h->cpu = smp_processor_id(); h->cpu = smp_processor_id();
h->last_reset_value = ovfl_mask ? ctx->ctx_soft_pmds[ffz(~ovfl_mask)].lval : 0UL; h->last_reset_value = ovfl_mask ? ctx->ctx_soft_pmds[ffz(~ovfl_mask)].lval : 0UL;
/* h->ip = regs ? regs->cr_iip | ((regs->cr_ipsr >> 41) & 0x3): 0x0UL;
* where did the fault happen h->regs = ovfl_mask; /* which registers overflowed */
*/
h->ip = regs ? regs->cr_iip | ((regs->cr_ipsr >> 41) & 0x3): 0x0UL;
/*
* which registers overflowed
*/
h->regs = ovfl_mask;
/* guaranteed to monotonically increase on each cpu */ /* guaranteed to monotonically increase on each cpu */
h->stamp = pfm_get_stamp(); h->stamp = pfm_get_stamp();
h->period = 0UL; /* not yet used */
/* position for first pmd */ /* position for first pmd */
e = (unsigned long *)(h+1); e = (unsigned long *)(h+1);
...@@ -2724,7 +2833,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str ...@@ -2724,7 +2833,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
* pfm_read_pmds(). * pfm_read_pmds().
*/ */
old_val = ctx->ctx_soft_pmds[i].val; old_val = ctx->ctx_soft_pmds[i].val;
ctx->ctx_soft_pmds[i].val += 1 + pmu_conf.perf_ovfl_val; ctx->ctx_soft_pmds[i].val += 1 + pmu_conf.ovfl_val;
/* /*
* check for overflow condition * check for overflow condition
...@@ -2739,9 +2848,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str ...@@ -2739,9 +2848,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
} }
DBprintk_ovfl(("soft_pmd[%d].val=0x%lx old_val=0x%lx pmd=0x%lx ovfl_pmds=0x%lx ovfl_notify=0x%lx\n", DBprintk_ovfl(("soft_pmd[%d].val=0x%lx old_val=0x%lx pmd=0x%lx ovfl_pmds=0x%lx ovfl_notify=0x%lx\n",
i, ctx->ctx_soft_pmds[i].val, old_val, i, ctx->ctx_soft_pmds[i].val, old_val,
ia64_get_pmd(i) & pmu_conf.perf_ovfl_val, ovfl_pmds, ovfl_notify)); ia64_get_pmd(i) & pmu_conf.ovfl_val, ovfl_pmds, ovfl_notify));
} }
/* /*
...@@ -2776,7 +2883,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str ...@@ -2776,7 +2883,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
*/ */
if (ovfl_notify == 0UL) { if (ovfl_notify == 0UL) {
if (ovfl_pmds) if (ovfl_pmds)
pfm_reset_regs(ctx, &ovfl_pmds, PFM_RELOAD_SHORT_RESET); pfm_reset_regs(ctx, &ovfl_pmds, PFM_PMD_SHORT_RESET);
return 0x0; return 0x0;
} }
...@@ -2924,7 +3031,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str ...@@ -2924,7 +3031,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
} }
static void static void
perfmon_interrupt (int irq, void *arg, struct pt_regs *regs) pfm_interrupt_handler(int irq, void *arg, struct pt_regs *regs)
{ {
u64 pmc0; u64 pmc0;
struct task_struct *task; struct task_struct *task;
...@@ -2932,6 +3039,14 @@ perfmon_interrupt (int irq, void *arg, struct pt_regs *regs) ...@@ -2932,6 +3039,14 @@ perfmon_interrupt (int irq, void *arg, struct pt_regs *regs)
pfm_stats[smp_processor_id()].pfm_ovfl_intr_count++; pfm_stats[smp_processor_id()].pfm_ovfl_intr_count++;
/*
* if an alternate handler is registered, just bypass the default one
*/
if (pfm_alternate_intr_handler) {
(*pfm_alternate_intr_handler->handler)(irq, arg, regs);
return;
}
/* /*
* srlz.d done before arriving here * srlz.d done before arriving here
* *
...@@ -2994,14 +3109,13 @@ perfmon_interrupt (int irq, void *arg, struct pt_regs *regs) ...@@ -2994,14 +3109,13 @@ perfmon_interrupt (int irq, void *arg, struct pt_regs *regs)
/* for debug only */ /* for debug only */
static int static int
perfmon_proc_info(char *page) pfm_proc_info(char *page)
{ {
char *p = page; char *p = page;
int i; int i;
p += sprintf(p, "enabled : %s\n", pmu_conf.pfm_is_disabled ? "No": "Yes");
p += sprintf(p, "fastctxsw : %s\n", pfm_sysctl.fastctxsw > 0 ? "Yes": "No"); p += sprintf(p, "fastctxsw : %s\n", pfm_sysctl.fastctxsw > 0 ? "Yes": "No");
p += sprintf(p, "ovfl_mask : 0x%lx\n", pmu_conf.perf_ovfl_val); p += sprintf(p, "ovfl_mask : 0x%lx\n", pmu_conf.ovfl_val);
for(i=0; i < NR_CPUS; i++) { for(i=0; i < NR_CPUS; i++) {
if (cpu_is_online(i) == 0) continue; if (cpu_is_online(i) == 0) continue;
...@@ -3009,16 +3123,18 @@ perfmon_proc_info(char *page) ...@@ -3009,16 +3123,18 @@ perfmon_proc_info(char *page)
p += sprintf(p, "CPU%-2d spurious intrs : %lu\n", i, pfm_stats[i].pfm_spurious_ovfl_intr_count); p += sprintf(p, "CPU%-2d spurious intrs : %lu\n", i, pfm_stats[i].pfm_spurious_ovfl_intr_count);
p += sprintf(p, "CPU%-2d recorded samples : %lu\n", i, pfm_stats[i].pfm_recorded_samples_count); p += sprintf(p, "CPU%-2d recorded samples : %lu\n", i, pfm_stats[i].pfm_recorded_samples_count);
p += sprintf(p, "CPU%-2d smpl buffer full : %lu\n", i, pfm_stats[i].pfm_full_smpl_buffer_count); p += sprintf(p, "CPU%-2d smpl buffer full : %lu\n", i, pfm_stats[i].pfm_full_smpl_buffer_count);
p += sprintf(p, "CPU%-2d syst_wide : %d\n", i, per_cpu(pfm_syst_info, i) & PFM_CPUINFO_SYST_WIDE ? 1 : 0);
p += sprintf(p, "CPU%-2d dcr_pp : %d\n", i, per_cpu(pfm_syst_info, i) & PFM_CPUINFO_DCR_PP ? 1 : 0);
p += sprintf(p, "CPU%-2d exclude idle : %d\n", i, per_cpu(pfm_syst_info, i) & PFM_CPUINFO_EXCL_IDLE ? 1 : 0);
p += sprintf(p, "CPU%-2d owner : %d\n", i, pmu_owners[i].owner ? pmu_owners[i].owner->pid: -1); p += sprintf(p, "CPU%-2d owner : %d\n", i, pmu_owners[i].owner ? pmu_owners[i].owner->pid: -1);
p += sprintf(p, "CPU%-2d syst_wide : %d\n", i, per_cpu(pfm_syst_wide, i));
p += sprintf(p, "CPU%-2d dcr_pp : %d\n", i, per_cpu(pfm_dcr_pp, i));
} }
LOCK_PFS(); LOCK_PFS();
p += sprintf(p, "proc_sessions : %lu\n"
"sys_sessions : %lu\n" p += sprintf(p, "proc_sessions : %u\n"
"sys_use_dbregs : %lu\n" "sys_sessions : %u\n"
"ptrace_use_dbregs : %lu\n", "sys_use_dbregs : %u\n"
"ptrace_use_dbregs : %u\n",
pfm_sessions.pfs_task_sessions, pfm_sessions.pfs_task_sessions,
pfm_sessions.pfs_sys_sessions, pfm_sessions.pfs_sys_sessions,
pfm_sessions.pfs_sys_use_dbregs, pfm_sessions.pfs_sys_use_dbregs,
...@@ -3033,7 +3149,7 @@ perfmon_proc_info(char *page) ...@@ -3033,7 +3149,7 @@ perfmon_proc_info(char *page)
static int static int
perfmon_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data) perfmon_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data)
{ {
int len = perfmon_proc_info(page); int len = pfm_proc_info(page);
if (len <= off+count) *eof = 1; if (len <= off+count) *eof = 1;
...@@ -3046,17 +3162,57 @@ perfmon_read_entry(char *page, char **start, off_t off, int count, int *eof, voi ...@@ -3046,17 +3162,57 @@ perfmon_read_entry(char *page, char **start, off_t off, int count, int *eof, voi
return len; return len;
} }
/*
* we come here as soon as PFM_CPUINFO_SYST_WIDE is set. This happens
* during pfm_enable() hence before pfm_start(). We cannot assume monitoring
* is active or inactive based on mode. We must rely on the value in
* cpu_data(i)->pfm_syst_info
*/
void void
pfm_syst_wide_update_task(struct task_struct *task, int mode) pfm_syst_wide_update_task(struct task_struct *task, unsigned long info, int is_ctxswin)
{ {
struct pt_regs *regs = (struct pt_regs *)((unsigned long) task + IA64_STK_OFFSET); struct pt_regs *regs;
unsigned long dcr;
unsigned long dcr_pp;
regs--; dcr_pp = info & PFM_CPUINFO_DCR_PP ? 1 : 0;
/* /*
* propagate the value of the dcr_pp bit to the psr * pid 0 is guaranteed to be the idle task. There is one such task with pid 0
* on every CPU, so we can rely on the pid to identify the idle task.
*/
if ((info & PFM_CPUINFO_EXCL_IDLE) == 0 || task->pid) {
regs = (struct pt_regs *)((unsigned long) task + IA64_STK_OFFSET);
regs--;
ia64_psr(regs)->pp = is_ctxswin ? dcr_pp : 0;
return;
}
/*
* if monitoring has started
*/ */
ia64_psr(regs)->pp = mode ? __get_cpu_var(pfm_dcr_pp) : 0; if (dcr_pp) {
dcr = ia64_get_dcr();
/*
* context switching in?
*/
if (is_ctxswin) {
/* mask monitoring for the idle task */
ia64_set_dcr(dcr & ~IA64_DCR_PP);
pfm_clear_psr_pp();
ia64_srlz_i();
return;
}
/*
* context switching out
* restore monitoring for next task
*
* Due to inlining this odd if-then-else construction generates
* better code.
*/
ia64_set_dcr(dcr |IA64_DCR_PP);
pfm_set_psr_pp();
ia64_srlz_i();
}
} }
void void
...@@ -3067,11 +3223,10 @@ pfm_save_regs (struct task_struct *task) ...@@ -3067,11 +3223,10 @@ pfm_save_regs (struct task_struct *task)
ctx = task->thread.pfm_context; ctx = task->thread.pfm_context;
/* /*
* save current PSR: needed because we modify it * save current PSR: needed because we modify it
*/ */
__asm__ __volatile__ ("mov %0=psr;;": "=r"(psr) :: "memory"); psr = pfm_get_psr();
/* /*
* stop monitoring: * stop monitoring:
...@@ -3369,7 +3524,7 @@ pfm_load_regs (struct task_struct *task) ...@@ -3369,7 +3524,7 @@ pfm_load_regs (struct task_struct *task)
*/ */
mask = pfm_sysctl.fastctxsw || ctx->ctx_fl_protected ? ctx->ctx_used_pmds[0] : ctx->ctx_reload_pmds[0]; mask = pfm_sysctl.fastctxsw || ctx->ctx_fl_protected ? ctx->ctx_used_pmds[0] : ctx->ctx_reload_pmds[0];
for (i=0; mask; i++, mask>>=1) { for (i=0; mask; i++, mask>>=1) {
if (mask & 0x1) ia64_set_pmd(i, t->pmd[i] & pmu_conf.perf_ovfl_val); if (mask & 0x1) ia64_set_pmd(i, t->pmd[i] & pmu_conf.ovfl_val);
} }
/* /*
...@@ -3419,7 +3574,7 @@ pfm_reset_pmu(struct task_struct *task) ...@@ -3419,7 +3574,7 @@ pfm_reset_pmu(struct task_struct *task)
int i; int i;
if (task != current) { if (task != current) {
printk("perfmon: invalid task in ia64_reset_pmu()\n"); printk("perfmon: invalid task in pfm_reset_pmu()\n");
return; return;
} }
...@@ -3428,6 +3583,7 @@ pfm_reset_pmu(struct task_struct *task) ...@@ -3428,6 +3583,7 @@ pfm_reset_pmu(struct task_struct *task)
/* /*
* install reset values for PMC. We skip PMC0 (done above) * install reset values for PMC. We skip PMC0 (done above)
* XX: good up to 64 PMCS
*/ */
for (i=1; (pmu_conf.pmc_desc[i].type & PFM_REG_END) == 0; i++) { for (i=1; (pmu_conf.pmc_desc[i].type & PFM_REG_END) == 0; i++) {
if ((pmu_conf.pmc_desc[i].type & PFM_REG_IMPL) == 0) continue; if ((pmu_conf.pmc_desc[i].type & PFM_REG_IMPL) == 0) continue;
...@@ -3444,7 +3600,7 @@ pfm_reset_pmu(struct task_struct *task) ...@@ -3444,7 +3600,7 @@ pfm_reset_pmu(struct task_struct *task)
/* /*
* clear reset values for PMD. * clear reset values for PMD.
* XXX: good up to 64 PMDS. Suppose that zero is a valid value. * XXX: good up to 64 PMDS.
*/ */
for (i=0; (pmu_conf.pmd_desc[i].type & PFM_REG_END) == 0; i++) { for (i=0; (pmu_conf.pmd_desc[i].type & PFM_REG_END) == 0; i++) {
if ((pmu_conf.pmd_desc[i].type & PFM_REG_IMPL) == 0) continue; if ((pmu_conf.pmd_desc[i].type & PFM_REG_IMPL) == 0) continue;
...@@ -3477,13 +3633,13 @@ pfm_reset_pmu(struct task_struct *task) ...@@ -3477,13 +3633,13 @@ pfm_reset_pmu(struct task_struct *task)
* *
* We never directly restore PMC0 so we do not include it in the mask. * We never directly restore PMC0 so we do not include it in the mask.
*/ */
ctx->ctx_reload_pmcs[0] = pmu_conf.impl_regs[0] & ~0x1; ctx->ctx_reload_pmcs[0] = pmu_conf.impl_pmcs[0] & ~0x1;
/* /*
* We must include all the PMD in this mask to avoid picking * We must include all the PMD in this mask to avoid picking
* up stale value and leak information, especially directly * up stale value and leak information, especially directly
* at the user level when psr.sp=0 * at the user level when psr.sp=0
*/ */
ctx->ctx_reload_pmds[0] = pmu_conf.impl_regs[4]; ctx->ctx_reload_pmds[0] = pmu_conf.impl_pmds[0];
/* /*
* Keep track of the pmds we want to sample * Keep track of the pmds we want to sample
...@@ -3493,7 +3649,7 @@ pfm_reset_pmu(struct task_struct *task) ...@@ -3493,7 +3649,7 @@ pfm_reset_pmu(struct task_struct *task)
* *
* We ignore the unimplemented pmds specified by the user * We ignore the unimplemented pmds specified by the user
*/ */
ctx->ctx_used_pmds[0] = ctx->ctx_smpl_regs[0] & pmu_conf.impl_regs[4]; ctx->ctx_used_pmds[0] = ctx->ctx_smpl_regs[0];
ctx->ctx_used_pmcs[0] = 1; /* always save/restore PMC[0] */ ctx->ctx_used_pmcs[0] = 1; /* always save/restore PMC[0] */
/* /*
...@@ -3547,16 +3703,17 @@ pfm_flush_regs (struct task_struct *task) ...@@ -3547,16 +3703,17 @@ pfm_flush_regs (struct task_struct *task)
ia64_set_dcr(ia64_get_dcr() & ~IA64_DCR_PP); ia64_set_dcr(ia64_get_dcr() & ~IA64_DCR_PP);
/* stop monitoring */ /* stop monitoring */
__asm__ __volatile__ ("rsm psr.pp;;"::: "memory"); pfm_clear_psr_pp();
ia64_srlz_i(); ia64_srlz_i();
__get_cpu_var(pfm_syst_wide) = 0; PFM_CPUINFO_CLEAR(PFM_CPUINFO_SYST_WIDE);
__get_cpu_var(pfm_dcr_pp) = 0; PFM_CPUINFO_CLEAR(PFM_CPUINFO_DCR_PP);
PFM_CPUINFO_CLEAR(PFM_CPUINFO_EXCL_IDLE);
} else { } else {
/* stop monitoring */ /* stop monitoring */
__asm__ __volatile__ ("rum psr.up;;"::: "memory"); pfm_clear_psr_up();
ia64_srlz_i(); ia64_srlz_i();
...@@ -3622,10 +3779,14 @@ pfm_flush_regs (struct task_struct *task) ...@@ -3622,10 +3779,14 @@ pfm_flush_regs (struct task_struct *task)
val = ia64_get_pmd(i); val = ia64_get_pmd(i);
if (PMD_IS_COUNTING(i)) { if (PMD_IS_COUNTING(i)) {
DBprintk(("[%d] pmd[%d] soft_pmd=0x%lx hw_pmd=0x%lx\n", task->pid, i, ctx->ctx_soft_pmds[i].val, val & pmu_conf.perf_ovfl_val)); DBprintk(("[%d] pmd[%d] soft_pmd=0x%lx hw_pmd=0x%lx\n",
task->pid,
i,
ctx->ctx_soft_pmds[i].val,
val & pmu_conf.ovfl_val));
/* collect latest results */ /* collect latest results */
ctx->ctx_soft_pmds[i].val += val & pmu_conf.perf_ovfl_val; ctx->ctx_soft_pmds[i].val += val & pmu_conf.ovfl_val;
/* /*
* now everything is in ctx_soft_pmds[] and we need * now everything is in ctx_soft_pmds[] and we need
...@@ -3638,7 +3799,7 @@ pfm_flush_regs (struct task_struct *task) ...@@ -3638,7 +3799,7 @@ pfm_flush_regs (struct task_struct *task)
* take care of overflow inline * take care of overflow inline
*/ */
if (pmc0 & (1UL << i)) { if (pmc0 & (1UL << i)) {
ctx->ctx_soft_pmds[i].val += 1 + pmu_conf.perf_ovfl_val; ctx->ctx_soft_pmds[i].val += 1 + pmu_conf.ovfl_val;
DBprintk(("[%d] pmd[%d] overflowed soft_pmd=0x%lx\n", DBprintk(("[%d] pmd[%d] overflowed soft_pmd=0x%lx\n",
task->pid, i, ctx->ctx_soft_pmds[i].val)); task->pid, i, ctx->ctx_soft_pmds[i].val));
} }
...@@ -3771,8 +3932,8 @@ pfm_inherit(struct task_struct *task, struct pt_regs *regs) ...@@ -3771,8 +3932,8 @@ pfm_inherit(struct task_struct *task, struct pt_regs *regs)
m = nctx->ctx_used_pmds[0] >> PMU_FIRST_COUNTER; m = nctx->ctx_used_pmds[0] >> PMU_FIRST_COUNTER;
for(i = PMU_FIRST_COUNTER ; m ; m>>=1, i++) { for(i = PMU_FIRST_COUNTER ; m ; m>>=1, i++) {
if ((m & 0x1) && pmu_conf.pmd_desc[i].type == PFM_REG_COUNTING) { if ((m & 0x1) && pmu_conf.pmd_desc[i].type == PFM_REG_COUNTING) {
nctx->ctx_soft_pmds[i].val = nctx->ctx_soft_pmds[i].lval & ~pmu_conf.perf_ovfl_val; nctx->ctx_soft_pmds[i].val = nctx->ctx_soft_pmds[i].lval & ~pmu_conf.ovfl_val;
thread->pmd[i] = nctx->ctx_soft_pmds[i].lval & pmu_conf.perf_ovfl_val; thread->pmd[i] = nctx->ctx_soft_pmds[i].lval & pmu_conf.ovfl_val;
} else { } else {
thread->pmd[i] = 0UL; /* reset to initial state */ thread->pmd[i] = 0UL; /* reset to initial state */
} }
...@@ -3939,30 +4100,14 @@ pfm_context_exit(struct task_struct *task) ...@@ -3939,30 +4100,14 @@ pfm_context_exit(struct task_struct *task)
UNLOCK_CTX(ctx); UNLOCK_CTX(ctx);
LOCK_PFS(); pfm_unreserve_session(task, ctx->ctx_fl_system, 1UL << ctx->ctx_cpu);
if (ctx->ctx_fl_system) { if (ctx->ctx_fl_system) {
pfm_sessions.pfs_sys_session[ctx->ctx_cpu] = NULL;
pfm_sessions.pfs_sys_sessions--;
DBprintk(("freeing syswide session on CPU%ld\n", ctx->ctx_cpu));
/* update perfmon debug register usage counter */
if (ctx->ctx_fl_using_dbreg) {
if (pfm_sessions.pfs_sys_use_dbregs == 0) {
printk("perfmon: invalid release for [%d] sys_use_dbregs=0\n", task->pid);
} else
pfm_sessions.pfs_sys_use_dbregs--;
}
/* /*
* remove any CPU pinning * remove any CPU pinning
*/ */
set_cpus_allowed(task, ctx->ctx_saved_cpus_allowed); set_cpus_allowed(task, ctx->ctx_saved_cpus_allowed);
} else { }
pfm_sessions.pfs_task_sessions--;
}
UNLOCK_PFS();
pfm_context_free(ctx); pfm_context_free(ctx);
/* /*
...@@ -3990,8 +4135,7 @@ pfm_cleanup_smpl_buf(struct task_struct *task) ...@@ -3990,8 +4135,7 @@ pfm_cleanup_smpl_buf(struct task_struct *task)
* Walk through the list and free the sampling buffer and psb * Walk through the list and free the sampling buffer and psb
*/ */
while (psb) { while (psb) {
DBprintk(("[%d] freeing smpl @%p size %ld\n", DBprintk(("[%d] freeing smpl @%p size %ld\n", current->pid, psb->psb_hdr, psb->psb_size));
current->pid, psb->psb_hdr, psb->psb_size));
pfm_rvfree(psb->psb_hdr, psb->psb_size); pfm_rvfree(psb->psb_hdr, psb->psb_size);
tmp = psb->psb_next; tmp = psb->psb_next;
...@@ -4095,16 +4239,16 @@ pfm_cleanup_notifiers(struct task_struct *task) ...@@ -4095,16 +4239,16 @@ pfm_cleanup_notifiers(struct task_struct *task)
if (ctx && ctx->ctx_notify_task == task) { if (ctx && ctx->ctx_notify_task == task) {
DBprintk(("trying for notifier [%d] in [%d]\n", task->pid, p->pid)); DBprintk(("trying for notifier [%d] in [%d]\n", task->pid, p->pid));
/* /*
* the spinlock is required to take care of a race condition with * the spinlock is required to take care of a race condition
* the send_sig_info() call. We must make sure that either the * with the send_sig_info() call. We must make sure that
* send_sig_info() completes using a valid task, or the * either the send_sig_info() completes using a valid task,
* notify_task is cleared before the send_sig_info() can pick up a * or the notify_task is cleared before the send_sig_info()
* stale value. Note that by the time this function is executed * can pick up a stale value. Note that by the time this
* the 'task' is already detached from the tasklist. The problem * function is executed the 'task' is already detached from the
* is that the notifiers have a direct pointer to it. It is okay * tasklist. The problem is that the notifiers have a direct
* to send a signal to a task in this stage, it simply will have * pointer to it. It is okay to send a signal to a task in this
* no effect. But it is better than sending to a completely * stage, it simply will have no effect. But it is better than sending
* destroyed task or worse to a new task using the same * to a completely destroyed task or worse to a new task using the same
* task_struct address. * task_struct address.
*/ */
LOCK_CTX(ctx); LOCK_CTX(ctx);
...@@ -4123,87 +4267,131 @@ pfm_cleanup_notifiers(struct task_struct *task) ...@@ -4123,87 +4267,131 @@ pfm_cleanup_notifiers(struct task_struct *task)
} }
static struct irqaction perfmon_irqaction = { static struct irqaction perfmon_irqaction = {
.handler = perfmon_interrupt, .handler = pfm_interrupt_handler,
.flags = SA_INTERRUPT, .flags = SA_INTERRUPT,
.name = "perfmon" .name = "perfmon"
}; };
int
pfm_install_alternate_syswide_subsystem(pfm_intr_handler_desc_t *hdl)
{
int ret;
/* some sanity checks */
if (hdl == NULL || hdl->handler == NULL) return -EINVAL;
/* do the easy test first */
if (pfm_alternate_intr_handler) return -EBUSY;
/* reserve our session */
ret = pfm_reserve_session(NULL, 1, cpu_online_map);
if (ret) return ret;
if (pfm_alternate_intr_handler) {
printk("perfmon: install_alternate, intr_handler not NULL after reserve\n");
return -EINVAL;
}
pfm_alternate_intr_handler = hdl;
return 0;
}
int
pfm_remove_alternate_syswide_subsystem(pfm_intr_handler_desc_t *hdl)
{
if (hdl == NULL) return -EINVAL;
/* cannot remove someone else's handler! */
if (pfm_alternate_intr_handler != hdl) return -EINVAL;
pfm_alternate_intr_handler = NULL;
/*
* XXX: assume cpu_online_map has not changed since reservation
*/
pfm_unreserve_session(NULL, 1, cpu_online_map);
return 0;
}
/* /*
* perfmon initialization routine, called from the initcall() table * perfmon initialization routine, called from the initcall() table
*/ */
int __init int __init
perfmon_init (void) pfm_init(void)
{ {
pal_perf_mon_info_u_t pm_info; unsigned int n, n_counters, i;
s64 status;
pmu_conf.pfm_is_disabled = 1; pmu_conf.disabled = 1;
printk("perfmon: version %u.%u (sampling format v%u.%u) IRQ %u\n", printk("perfmon: version %u.%u IRQ %u\n",
PFM_VERSION_MAJ, PFM_VERSION_MAJ,
PFM_VERSION_MIN, PFM_VERSION_MIN,
PFM_SMPL_VERSION_MAJ,
PFM_SMPL_VERSION_MIN,
IA64_PERFMON_VECTOR); IA64_PERFMON_VECTOR);
if ((status=ia64_pal_perf_mon_info(pmu_conf.impl_regs, &pm_info)) != 0) {
printk("perfmon: PAL call failed (%ld), perfmon disabled\n", status);
return -1;
}
pmu_conf.perf_ovfl_val = (1UL << pm_info.pal_perf_mon_info_s.width) - 1;
/* /*
* XXX: use the pfm_*_desc tables instead and simply verify with PAL * compute the number of implemented PMD/PMC from the
* description tables
*/ */
pmu_conf.max_counters = pm_info.pal_perf_mon_info_s.generic; n = 0;
pmu_conf.num_pmcs = find_num_pm_regs(pmu_conf.impl_regs); for (i=0; PMC_IS_LAST(i) == 0; i++) {
pmu_conf.num_pmds = find_num_pm_regs(&pmu_conf.impl_regs[4]); if (PMC_IS_IMPL(i) == 0) continue;
pmu_conf.impl_pmcs[i>>6] |= 1UL << (i&63);
printk("perfmon: %u bits counters\n", pm_info.pal_perf_mon_info_s.width); 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);
n++;
if (PMD_IS_COUNTING(i)) n_counters++;
}
pmu_conf.num_pmds = n;
pmu_conf.num_counters = n_counters;
printk("perfmon: %lu PMC/PMD pairs, %lu PMCs, %lu PMDs\n", printk("perfmon: %u PMCs, %u PMDs, %u counters (%lu bits)\n",
pmu_conf.max_counters, pmu_conf.num_pmcs, pmu_conf.num_pmds); pmu_conf.num_pmcs,
pmu_conf.num_pmds,
pmu_conf.num_counters,
ffz(pmu_conf.ovfl_val));
/* sanity check */ /* 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 is DISABLED\n"); printk(KERN_ERR "perfmon: not enough pmc/pmd, perfmon disabled\n");
return -1; /* no need to continue anyway */ return -1;
}
if (ia64_pal_debug_info(&pmu_conf.num_ibrs, &pmu_conf.num_dbrs)) {
printk(KERN_WARNING "perfmon: unable to get number of debug registers\n");
pmu_conf.num_ibrs = pmu_conf.num_dbrs = 0;
} }
/* PAL reports the number of pairs */
pmu_conf.num_ibrs <<=1;
pmu_conf.num_dbrs <<=1;
/*
* setup the register configuration descriptions for the CPU
*/
pmu_conf.pmc_desc = pfm_pmc_desc;
pmu_conf.pmd_desc = pfm_pmd_desc;
/* we are all set */
pmu_conf.pfm_is_disabled = 0;
/* /*
* for now here for debug purposes * for now here for debug purposes
*/ */
perfmon_dir = create_proc_read_entry ("perfmon", 0, 0, perfmon_read_entry, NULL); 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");
return -1;
}
/*
* create /proc/perfmon
*/
pfm_sysctl_header = register_sysctl_table(pfm_sysctl_root, 0); pfm_sysctl_header = register_sysctl_table(pfm_sysctl_root, 0);
/*
* initialize all our spinlocks
*/
spin_lock_init(&pfm_sessions.pfs_lock); spin_lock_init(&pfm_sessions.pfs_lock);
/* we are all set */
pmu_conf.disabled = 0;
return 0; return 0;
} }
__initcall(pfm_init);
__initcall(perfmon_init);
void void
perfmon_init_percpu (void) pfm_init_percpu(void)
{ {
int i; int i;
...@@ -4222,17 +4410,17 @@ perfmon_init_percpu (void) ...@@ -4222,17 +4410,17 @@ perfmon_init_percpu (void)
* *
* On McKinley, this code is ineffective until PMC4 is initialized. * On McKinley, this code is ineffective until PMC4 is initialized.
*/ */
for (i=1; (pfm_pmc_desc[i].type & PFM_REG_END) == 0; i++) { for (i=1; PMC_IS_LAST(i) == 0; i++) {
if ((pfm_pmc_desc[i].type & PFM_REG_IMPL) == 0) continue; if (PMC_IS_IMPL(i) == 0) continue;
ia64_set_pmc(i, pfm_pmc_desc[i].default_value); ia64_set_pmc(i, PMC_DFL_VAL(i));
} }
for (i=0; (pfm_pmd_desc[i].type & PFM_REG_END) == 0; i++) {
if ((pfm_pmd_desc[i].type & PFM_REG_IMPL) == 0) continue; for (i=0; PMD_IS_LAST(i); i++) {
if (PMD_IS_IMPL(i) == 0) continue;
ia64_set_pmd(i, 0UL); ia64_set_pmd(i, 0UL);
} }
ia64_set_pmc(0,1UL); ia64_set_pmc(0,1UL);
ia64_srlz_d(); ia64_srlz_d();
} }
#else /* !CONFIG_PERFMON */ #else /* !CONFIG_PERFMON */
......
/*
* This file contains the architected PMU register description tables
* and pmc checker used by perfmon.c.
*
* Copyright (C) 2002 Hewlett Packard Co
* Stephane Eranian <eranian@hpl.hp.com>
*/
#define RDEP(x) (1UL<<(x)) #define RDEP(x) (1UL<<(x))
#if defined(CONFIG_ITANIUM) || defined(CONFIG_MCKINLEY) #if defined(CONFIG_ITANIUM) || defined (CONFIG_MCKINLEY)
#error "This file should only be used when CONFIG_ITANIUM and CONFIG_MCKINLEY are not defined" #error "This file should not be used when CONFIG_ITANIUM or CONFIG_MCKINLEY is defined"
#endif #endif
static pfm_reg_desc_t pmc_desc[PMU_MAX_PMCS]={ static pfm_reg_desc_t pmc_gen_desc[PMU_MAX_PMCS]={
/* pmc0 */ { PFM_REG_CONTROL , 0, 0x1UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, /* 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}}, /* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, /* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
...@@ -13,10 +20,10 @@ static pfm_reg_desc_t pmc_desc[PMU_MAX_PMCS]={ ...@@ -13,10 +20,10 @@ static pfm_reg_desc_t pmc_desc[PMU_MAX_PMCS]={
/* pmc5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, /* pmc5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
/* pmc6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, /* pmc6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
/* pmc7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, /* pmc7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
}; };
static pfm_reg_desc_t pmd_desc[PMU_MAX_PMDS]={ static pfm_reg_desc_t pmd_gen_desc[PMU_MAX_PMDS]={
/* pmd0 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* pmd0 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}},
/* pmd1 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* pmd1 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}},
/* pmd2 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* pmd2 */ { PFM_REG_NOTIMPL , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}},
...@@ -25,5 +32,17 @@ static pfm_reg_desc_t pmd_desc[PMU_MAX_PMDS]={ ...@@ -25,5 +32,17 @@ static pfm_reg_desc_t pmd_desc[PMU_MAX_PMDS]={
/* pmd5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}}, /* pmd5 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}},
/* pmd6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}}, /* pmd6 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}},
/* pmd7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}}, /* pmd7 */ { PFM_REG_COUNTING, 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}},
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ { 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={
disabled: 1,
ovfl_val: (1UL << 32) - 1,
num_ibrs: 8,
num_dbrs: 8,
pmd_desc: pfm_gen_pmd_desc,
pmc_desc: pfm_gen_pmc_desc
}; };
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
static int pfm_ita_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs); static int pfm_ita_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
static int pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, struct pt_regs *regs); static int pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, struct pt_regs *regs);
static pfm_reg_desc_t pfm_pmc_desc[PMU_MAX_PMCS]={ 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}}, /* 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}}, /* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, /* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
...@@ -33,7 +33,7 @@ static pfm_reg_desc_t pfm_pmc_desc[PMU_MAX_PMCS]={ ...@@ -33,7 +33,7 @@ static pfm_reg_desc_t pfm_pmc_desc[PMU_MAX_PMCS]={
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
}; };
static pfm_reg_desc_t pfm_pmd_desc[PMU_MAX_PMDS]={ static pfm_reg_desc_t pfm_ita_pmd_desc[PMU_MAX_PMDS]={
/* pmd0 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, /* pmd0 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}},
/* pmd1 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, /* pmd1 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}},
/* pmd2 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, /* pmd2 */ { PFM_REG_BUFFER , 0, 0UL, -1UL, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}},
...@@ -55,6 +55,19 @@ static pfm_reg_desc_t pfm_pmd_desc[PMU_MAX_PMDS]={ ...@@ -55,6 +55,19 @@ static pfm_reg_desc_t pfm_pmd_desc[PMU_MAX_PMDS]={
{ PFM_REG_END , 0, 0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ { 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={
disabled: 1,
ovfl_val: (1UL << 32) - 1,
num_ibrs: 8,
num_dbrs: 8,
pmd_desc: pfm_ita_pmd_desc,
pmc_desc: pfm_ita_pmc_desc
};
static int static int
pfm_ita_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs) pfm_ita_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs)
{ {
......
...@@ -16,7 +16,7 @@ static int pfm_mck_reserved(struct task_struct *task, unsigned int cnum, unsigne ...@@ -16,7 +16,7 @@ static int pfm_mck_reserved(struct task_struct *task, unsigned int cnum, unsigne
static int pfm_mck_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs); static int pfm_mck_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs);
static int pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, struct pt_regs *regs); static int pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, struct pt_regs *regs);
static pfm_reg_desc_t pfm_pmc_desc[PMU_MAX_PMCS]={ 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}}, /* 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}}, /* pmc1 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
/* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, /* pmc2 */ { PFM_REG_CONTROL , 0, 0x0UL, -1UL, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}},
...@@ -36,7 +36,7 @@ static pfm_reg_desc_t pfm_pmc_desc[PMU_MAX_PMCS]={ ...@@ -36,7 +36,7 @@ static pfm_reg_desc_t pfm_pmc_desc[PMU_MAX_PMCS]={
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ { PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */
}; };
static pfm_reg_desc_t pfm_pmd_desc[PMU_MAX_PMDS]={ static pfm_reg_desc_t pfm_mck_pmd_desc[PMU_MAX_PMDS]={
/* pmd0 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, /* pmd0 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}},
/* pmd1 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, /* pmd1 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}},
/* pmd2 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, /* pmd2 */ { PFM_REG_BUFFER , 0, 0x0UL, -1UL, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}},
...@@ -58,6 +58,19 @@ static pfm_reg_desc_t pfm_pmd_desc[PMU_MAX_PMDS]={ ...@@ -58,6 +58,19 @@ static pfm_reg_desc_t pfm_pmd_desc[PMU_MAX_PMDS]={
{ PFM_REG_END , 0, 0x0UL, -1UL, NULL, NULL, {0,}, {0,}}, /* end marker */ { 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={
disabled: 1,
ovfl_val: (1UL << 47) - 1,
num_ibrs: 8,
num_dbrs: 8,
pmd_desc: pfm_mck_pmd_desc,
pmc_desc: pfm_mck_pmc_desc
};
/* /*
* PMC reserved fields must have their power-up values preserved * PMC reserved fields must have their power-up values preserved
*/ */
......
...@@ -205,6 +205,10 @@ cpu_idle (void *unused) ...@@ -205,6 +205,10 @@ cpu_idle (void *unused)
void void
ia64_save_extra (struct task_struct *task) ia64_save_extra (struct task_struct *task)
{ {
#ifdef CONFIG_PERFMON
unsigned long info;
#endif
if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0) if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
ia64_save_debug_regs(&task->thread.dbr[0]); ia64_save_debug_regs(&task->thread.dbr[0]);
...@@ -212,8 +216,9 @@ ia64_save_extra (struct task_struct *task) ...@@ -212,8 +216,9 @@ ia64_save_extra (struct task_struct *task)
if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0) if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0)
pfm_save_regs(task); pfm_save_regs(task);
if (__get_cpu_var(pfm_syst_wide)) info = __get_cpu_var(pfm_syst_info);
pfm_syst_wide_update_task(task, 0); if (info & PFM_CPUINFO_SYST_WIDE)
pfm_syst_wide_update_task(task, info, 0);
#endif #endif
#ifdef CONFIG_IA32_SUPPORT #ifdef CONFIG_IA32_SUPPORT
...@@ -225,6 +230,10 @@ ia64_save_extra (struct task_struct *task) ...@@ -225,6 +230,10 @@ ia64_save_extra (struct task_struct *task)
void void
ia64_load_extra (struct task_struct *task) ia64_load_extra (struct task_struct *task)
{ {
#ifdef CONFIG_PERFMON
unsigned long info;
#endif
if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0) if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0)
ia64_load_debug_regs(&task->thread.dbr[0]); ia64_load_debug_regs(&task->thread.dbr[0]);
...@@ -232,8 +241,9 @@ ia64_load_extra (struct task_struct *task) ...@@ -232,8 +241,9 @@ ia64_load_extra (struct task_struct *task)
if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0) if ((task->thread.flags & IA64_THREAD_PM_VALID) != 0)
pfm_load_regs(task); pfm_load_regs(task);
if (__get_cpu_var(pfm_syst_wide)) info = __get_cpu_var(pfm_syst_info);
pfm_syst_wide_update_task(task, 1); if (info & PFM_CPUINFO_SYST_WIDE)
pfm_syst_wide_update_task(task, info, 1);
#endif #endif
#ifdef CONFIG_IA32_SUPPORT #ifdef CONFIG_IA32_SUPPORT
......
...@@ -265,7 +265,7 @@ smp_callin (void) ...@@ -265,7 +265,7 @@ smp_callin (void)
extern void ia64_init_itm(void); extern void ia64_init_itm(void);
#ifdef CONFIG_PERFMON #ifdef CONFIG_PERFMON
extern void perfmon_init_percpu(void); extern void pfm_init_percpu(void);
#endif #endif
cpuid = smp_processor_id(); cpuid = smp_processor_id();
...@@ -300,7 +300,7 @@ smp_callin (void) ...@@ -300,7 +300,7 @@ smp_callin (void)
#endif #endif
#ifdef CONFIG_PERFMON #ifdef CONFIG_PERFMON
perfmon_init_percpu(); pfm_init_percpu();
#endif #endif
local_irq_enable(); local_irq_enable();
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#define PFM_FL_INHERIT_ALL 0x02 /* always clone pfm_context across fork() */ #define PFM_FL_INHERIT_ALL 0x02 /* always clone pfm_context across fork() */
#define PFM_FL_NOTIFY_BLOCK 0x04 /* block task on user level notifications */ #define PFM_FL_NOTIFY_BLOCK 0x04 /* block task on user level notifications */
#define PFM_FL_SYSTEM_WIDE 0x08 /* create a system wide context */ #define PFM_FL_SYSTEM_WIDE 0x08 /* create a system wide context */
#define PFM_FL_EXCL_IDLE 0x20 /* exclude idle task from system wide session */
/* /*
* PMC flags * PMC flags
...@@ -86,11 +87,12 @@ typedef struct { ...@@ -86,11 +87,12 @@ typedef struct {
unsigned long reg_long_reset; /* reset after sampling buffer overflow (large) */ unsigned long reg_long_reset; /* reset after sampling buffer overflow (large) */
unsigned long reg_short_reset;/* reset after counter overflow (small) */ unsigned long reg_short_reset;/* reset after counter overflow (small) */
unsigned long reg_reset_pmds[4]; /* which other counters to reset on overflow */ unsigned long reg_reset_pmds[4]; /* which other counters to reset on overflow */
unsigned long reg_random_seed; /* seed value when randomization is used */ unsigned long reg_random_seed; /* seed value when randomization is used */
unsigned long reg_random_mask; /* bitmask used to limit random value */ unsigned long reg_random_mask; /* bitmask used to limit random value */
unsigned long reg_last_reset_value;/* last value used to reset the PMD (PFM_READ_PMDS) */
unsigned long reserved[14]; /* for future use */ unsigned long reserved[13]; /* for future use */
} pfarg_reg_t; } pfarg_reg_t;
typedef struct { typedef struct {
...@@ -123,7 +125,7 @@ typedef struct { ...@@ -123,7 +125,7 @@ typedef struct {
* Define the version numbers for both perfmon as a whole and the sampling buffer format. * Define the version numbers for both perfmon as a whole and the sampling buffer format.
*/ */
#define PFM_VERSION_MAJ 1U #define PFM_VERSION_MAJ 1U
#define PFM_VERSION_MIN 1U #define PFM_VERSION_MIN 3U
#define PFM_VERSION (((PFM_VERSION_MAJ&0xffff)<<16)|(PFM_VERSION_MIN & 0xffff)) #define PFM_VERSION (((PFM_VERSION_MAJ&0xffff)<<16)|(PFM_VERSION_MIN & 0xffff))
#define PFM_SMPL_VERSION_MAJ 1U #define PFM_SMPL_VERSION_MAJ 1U
...@@ -156,13 +158,17 @@ typedef struct { ...@@ -156,13 +158,17 @@ typedef struct {
unsigned long stamp; /* timestamp */ unsigned long stamp; /* timestamp */
unsigned long ip; /* where did the overflow interrupt happened */ unsigned long ip; /* where did the overflow interrupt happened */
unsigned long regs; /* bitmask of which registers overflowed */ unsigned long regs; /* bitmask of which registers overflowed */
unsigned long period; /* unused */ unsigned long reserved; /* unused */
} perfmon_smpl_entry_t; } perfmon_smpl_entry_t;
extern int perfmonctl(pid_t pid, int cmd, void *arg, int narg); extern int perfmonctl(pid_t pid, int cmd, void *arg, int narg);
#ifdef __KERNEL__ #ifdef __KERNEL__
typedef struct {
void (*handler)(int irq, void *arg, struct pt_regs *regs);
} pfm_intr_handler_desc_t;
extern void pfm_save_regs (struct task_struct *); extern void pfm_save_regs (struct task_struct *);
extern void pfm_load_regs (struct task_struct *); extern void pfm_load_regs (struct task_struct *);
...@@ -174,9 +180,24 @@ extern void pfm_cleanup_owners (struct task_struct *); ...@@ -174,9 +180,24 @@ extern void pfm_cleanup_owners (struct task_struct *);
extern int pfm_use_debug_registers(struct task_struct *); extern int pfm_use_debug_registers(struct task_struct *);
extern int pfm_release_debug_registers(struct task_struct *); extern int pfm_release_debug_registers(struct task_struct *);
extern int pfm_cleanup_smpl_buf(struct task_struct *); extern int pfm_cleanup_smpl_buf(struct task_struct *);
extern void pfm_syst_wide_update_task(struct task_struct *, int); extern void pfm_syst_wide_update_task(struct task_struct *, unsigned long info, int is_ctxswin);
extern void pfm_ovfl_block_reset(void); extern void pfm_ovfl_block_reset(void);
extern void perfmon_init_percpu(void); extern void pfm_init_percpu(void);
/*
* hooks to allow VTune/Prospect to cooperate with perfmon.
* (reserved for system wide monitoring modules only)
*/
extern int pfm_install_alternate_syswide_subsystem(pfm_intr_handler_desc_t *h);
extern int pfm_remove_alternate_syswide_subsystem(pfm_intr_handler_desc_t *h);
/*
* describe the content of the local_cpu_date->pfm_syst_info field
*/
#define PFM_CPUINFO_SYST_WIDE 0x1 /* if set a system wide session exist */
#define PFM_CPUINFO_DCR_PP 0x2 /* if set the system wide session has started */
#define PFM_CPUINFO_EXCL_IDLE 0x4 /* the system wide session excludes the idle task */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -205,8 +205,8 @@ extern void ia64_save_extra (struct task_struct *task); ...@@ -205,8 +205,8 @@ extern void ia64_save_extra (struct task_struct *task);
extern void ia64_load_extra (struct task_struct *task); extern void ia64_load_extra (struct task_struct *task);
#ifdef CONFIG_PERFMON #ifdef CONFIG_PERFMON
DECLARE_PER_CPU(int, pfm_syst_wide); DECLARE_PER_CPU(unsigned long, pfm_syst_info);
# define PERFMON_IS_SYSWIDE() (get_cpu_var(pfm_syst_wide) != 0) # define PERFMON_IS_SYSWIDE() (get_cpu_var(pfm_syst_info) & 0x1)
#else #else
# define PERFMON_IS_SYSWIDE() (0) # define PERFMON_IS_SYSWIDE() (0)
#endif #endif
......
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