Home Home > GIT Browse
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSt├ęphane Eranian <eranian@hpl.hp.com>2003-10-16 01:13:18 -0700
committerDavid Mosberger <davidm@tiger.hpl.hp.com>2003-10-16 01:13:18 -0700
commitd93c430861bdd0f7bd07363cc3edc57c93bd9687 (patch)
treed8c1cbb7c7e86ee26bf972a35bdf6de030b54b8c
parent761552e742622c31814c96e3611fe4985c1cb7d5 (diff)
[PATCH] ia64: perfmon-2 fixes
- remove PFM_FL_UNSECURE support because broken - remove instrumentation code for syswide ctx update (5% speedup) - integrated cleanups from John Levon - remove references to pmu_conf.ovfl_val from loops, use local variable - updated pfm_bad_permissions() - support reset values for non-counting pmds - serialization of PFM_RESTART - added support for restart_active in per-task mode - remove potential deadlock caused by calling pfm_overflow_handler() from pfm_load_regs() - fix invalid check in perfmon_mckinley() for PMC13
-rw-r--r--arch/ia64/kernel/perfmon.c573
-rw-r--r--arch/ia64/kernel/perfmon_itanium.h4
-rw-r--r--arch/ia64/kernel/perfmon_mckinley.h22
-rw-r--r--include/asm-ia64/perfmon.h11
4 files changed, 328 insertions, 282 deletions
diff --git a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c
index c5d8dcb5ddf9..addb4fd434ce 100644
--- a/arch/ia64/kernel/perfmon.c
+++ b/arch/ia64/kernel/perfmon.c
@@ -140,7 +140,7 @@
* in UP:
* - we need to protect against PMU overflow interrupts (local_irq_disable)
*
- * spin_lock_irqsave()/spin_unlock_irqrestore():
+ * spin_lock_irqsave()/spin_lock_irqrestore():
* in SMP: local_irq_disable + spin_lock
* in UP : local_irq_disable
*
@@ -254,7 +254,6 @@ typedef struct {
unsigned long seed; /* seed for random-number generator */
unsigned long mask; /* mask for random-number generator */
unsigned int flags; /* notify/do not notify */
- int next_reset_type;/* PFM_PMD_NO_RESET, PFM_PMD_LONG_RESET, PFM_PMD_SHORT_RESET */
unsigned long eventid; /* overflow event identifier */
} pfm_counter_t;
@@ -267,10 +266,10 @@ typedef struct {
unsigned int using_dbreg:1; /* using range restrictions (debug registers) */
unsigned int is_sampling:1; /* true if using a custom format */
unsigned int excl_idle:1; /* exclude idle task in system wide session */
- unsigned int unsecure:1; /* exclude idle task in system wide session */
unsigned int going_zombie:1; /* context is zombie (MASKED+blocking) */
unsigned int trap_reason:2; /* reason for going into pfm_handle_work() */
unsigned int no_msg:1; /* no message sent on overflow */
+ unsigned int can_restart:1; /* allowed to issue a PFM_RESTART */
unsigned int reserved:22;
} pfm_context_flags_t;
@@ -356,10 +355,10 @@ typedef struct pfm_context {
#define ctx_fl_using_dbreg ctx_flags.using_dbreg
#define ctx_fl_is_sampling ctx_flags.is_sampling
#define ctx_fl_excl_idle ctx_flags.excl_idle
-#define ctx_fl_unsecure ctx_flags.unsecure
#define ctx_fl_going_zombie ctx_flags.going_zombie
#define ctx_fl_trap_reason ctx_flags.trap_reason
#define ctx_fl_no_msg ctx_flags.no_msg
+#define ctx_fl_can_restart ctx_flags.can_restart
#define PFM_SET_WORK_PENDING(t, v) do { (t)->thread.pfm_needs_checking = v; } while(0);
#define PFM_GET_WORK_PENDING(t) (t)->thread.pfm_needs_checking
@@ -493,12 +492,11 @@ typedef struct {
typedef struct {
unsigned long pfm_spurious_ovfl_intr_count; /* keep track of spurious ovfl interrupts */
+ unsigned long pfm_replay_ovfl_intr_count; /* keep track of replayed ovfl interrupts */
unsigned long pfm_ovfl_intr_count; /* keep track of ovfl interrupts */
unsigned long pfm_ovfl_intr_cycles; /* cycles spent processing ovfl interrupts */
unsigned long pfm_ovfl_intr_cycles_min; /* min cycles spent processing ovfl interrupts */
unsigned long pfm_ovfl_intr_cycles_max; /* max cycles spent processing ovfl interrupts */
- unsigned long pfm_sysupdt_count;
- unsigned long pfm_sysupdt_cycles;
unsigned long pfm_smpl_handler_calls;
unsigned long pfm_smpl_handler_cycles;
char pad[SMP_CACHE_BYTES] ____cacheline_aligned;
@@ -513,10 +511,8 @@ static pfm_session_t pfm_sessions; /* global sessions information */
static struct proc_dir_entry *perfmon_dir;
static pfm_uuid_t pfm_null_uuid = {0,};
-static spinlock_t pfm_smpl_fmt_lock;
-static pfm_buffer_fmt_t *pfm_buffer_fmt_list;
-#define LOCK_BUF_FMT_LIST() spin_lock(&pfm_smpl_fmt_lock)
-#define UNLOCK_BUF_FMT_LIST() spin_unlock(&pfm_smpl_fmt_lock)
+static spinlock_t pfm_buffer_fmt_lock;
+static LIST_HEAD(pfm_buffer_fmt_list);
/* sysctl() controls */
static pfm_sysctl_t pfm_sysctl;
@@ -544,14 +540,8 @@ static struct vm_operations_struct pfm_vm_ops={
close: pfm_vm_close
};
-#define pfm_wait_task_inactive(t) wait_task_inactive(t)
#define pfm_get_cpu_var(v) __ia64_per_cpu_var(v)
#define pfm_get_cpu_data(a,b) per_cpu(a, b)
-typedef irqreturn_t pfm_irq_handler_t;
-#define PFM_IRQ_HANDLER_RET(v) do { \
- put_cpu_no_resched(); \
- return IRQ_HANDLED; \
- } while(0);
static inline void
pfm_put_task(struct task_struct *task)
@@ -628,7 +618,6 @@ static struct file_system_type pfm_fs_type = {
.get_sb = pfmfs_get_sb,
.kill_sb = kill_anon_super,
};
-
DEFINE_PER_CPU(unsigned long, pfm_syst_info);
DEFINE_PER_CPU(struct task_struct *, pmu_owner);
DEFINE_PER_CPU(pfm_context_t *, pmu_ctx);
@@ -734,12 +723,14 @@ 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)
{
- ctx->ctx_pmds[i].val = val & ~pmu_conf.ovfl_val;
+ unsigned long ovfl_val = pmu_conf.ovfl_val;
+
+ ctx->ctx_pmds[i].val = val & ~ovfl_val;
/*
* writing to unimplemented part is ignore, so we do not need to
* mask off top part
*/
- ia64_set_pmd(i, val & pmu_conf.ovfl_val);
+ ia64_set_pmd(i, val & ovfl_val);
}
static pfm_msg_t *
@@ -870,11 +861,12 @@ pfm_mask_monitoring(struct task_struct *task)
{
pfm_context_t *ctx = PFM_GET_CTX(task);
struct thread_struct *th = &task->thread;
- unsigned long mask, val;
+ unsigned long mask, val, ovfl_mask;
int i;
- DPRINT(("[%d] masking monitoring for [%d]\n", current->pid, task->pid));
+ DPRINT_ovfl(("[%d] masking monitoring for [%d]\n", current->pid, task->pid));
+ 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
@@ -904,14 +896,14 @@ pfm_mask_monitoring(struct task_struct *task)
/*
* we rebuild the full 64 bit value of the counter
*/
- ctx->ctx_pmds[i].val += (val & pmu_conf.ovfl_val);
+ ctx->ctx_pmds[i].val += (val & ovfl_mask);
} else {
ctx->ctx_pmds[i].val = val;
}
- DPRINT(("pmd[%d]=0x%lx hw_pmd=0x%lx\n",
+ DPRINT_ovfl(("pmd[%d]=0x%lx hw_pmd=0x%lx\n",
i,
ctx->ctx_pmds[i].val,
- val & pmu_conf.ovfl_val));
+ val & ovfl_mask));
}
/*
* mask monitoring by setting the privilege level to 0
@@ -926,6 +918,7 @@ pfm_mask_monitoring(struct task_struct *task)
if ((mask & 0x1) == 0UL) continue;
ia64_set_pmc(i, th->pmcs[i] & ~0xfUL);
th->pmcs[i] &= ~0xfUL;
+ DPRINT_ovfl(("pmc[%d]=0x%lx\n", i, th->pmcs[i]));
}
/*
* make all of this visible
@@ -943,11 +936,12 @@ pfm_restore_monitoring(struct task_struct *task)
{
pfm_context_t *ctx = PFM_GET_CTX(task);
struct thread_struct *th = &task->thread;
- unsigned long mask;
+ unsigned long mask, ovfl_mask;
unsigned long psr, val;
int i, is_system;
is_system = ctx->ctx_fl_system;
+ 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);
@@ -989,8 +983,8 @@ pfm_restore_monitoring(struct task_struct *task)
* we split the 64bit value according to
* counter width
*/
- val = ctx->ctx_pmds[i].val & pmu_conf.ovfl_val;
- ctx->ctx_pmds[i].val &= ~pmu_conf.ovfl_val;
+ val = ctx->ctx_pmds[i].val & ovfl_mask;
+ ctx->ctx_pmds[i].val &= ~ovfl_mask;
} else {
val = ctx->ctx_pmds[i].val;
}
@@ -1206,12 +1200,36 @@ pfm_buf_fmt_restart_active(pfm_buffer_fmt_t *fmt, struct task_struct *task, pfm_
return ret;
}
+static pfm_buffer_fmt_t *
+__pfm_find_buffer_fmt(pfm_uuid_t uuid)
+{
+ struct list_head * pos;
+ pfm_buffer_fmt_t * entry;
-
+ list_for_each(pos, &pfm_buffer_fmt_list) {
+ entry = list_entry(pos, pfm_buffer_fmt_t, fmt_list);
+ if (pfm_uuid_cmp(uuid, entry->fmt_uuid) == 0)
+ return entry;
+ }
+ return NULL;
+}
+
+/*
+ * find a buffer format based on its uuid
+ */
+static pfm_buffer_fmt_t *
+pfm_find_buffer_fmt(pfm_uuid_t uuid)
+{
+ pfm_buffer_fmt_t * fmt;
+ spin_lock(&pfm_buffer_fmt_lock);
+ fmt = __pfm_find_buffer_fmt(uuid);
+ spin_unlock(&pfm_buffer_fmt_lock);
+ return fmt;
+}
+
int
pfm_register_buffer_fmt(pfm_buffer_fmt_t *fmt)
{
- pfm_buffer_fmt_t *p;
int ret = 0;
/* some sanity checks */
@@ -1224,80 +1242,44 @@ pfm_register_buffer_fmt(pfm_buffer_fmt_t *fmt)
* XXX: need check validity of fmt_arg_size
*/
- LOCK_BUF_FMT_LIST();
- p = pfm_buffer_fmt_list;
-
+ spin_lock(&pfm_buffer_fmt_lock);
- while (p) {
- if (pfm_uuid_cmp(fmt->fmt_uuid, p->fmt_uuid) == 0) break;
- p = p->fmt_next;
- }
-
- if (p) {
+ if (__pfm_find_buffer_fmt(fmt->fmt_uuid)) {
printk(KERN_ERR "perfmon: duplicate sampling format: %s\n", fmt->fmt_name);
ret = -EBUSY;
- } else {
- fmt->fmt_prev = NULL;
- fmt->fmt_next = pfm_buffer_fmt_list;
- pfm_buffer_fmt_list = fmt;
- printk(KERN_ERR "perfmon: added sampling format %s\n", fmt->fmt_name);
- }
- UNLOCK_BUF_FMT_LIST();
+ goto out;
+ }
+ list_add(&fmt->fmt_list, &pfm_buffer_fmt_list);
+ printk(KERN_INFO "perfmon: added sampling format %s\n", fmt->fmt_name);
- return ret;
+out:
+ spin_unlock(&pfm_buffer_fmt_lock);
+ return ret;
}
int
pfm_unregister_buffer_fmt(pfm_uuid_t uuid)
{
- pfm_buffer_fmt_t *p;
+ pfm_buffer_fmt_t *fmt;
int ret = 0;
- LOCK_BUF_FMT_LIST();
- p = pfm_buffer_fmt_list;
- while (p) {
- if (memcmp(uuid, p->fmt_uuid, sizeof(pfm_uuid_t)) == 0) break;
- p = p->fmt_next;
- }
- if (p) {
- if (p->fmt_prev)
- p->fmt_prev->fmt_next = p->fmt_next;
- else
- pfm_buffer_fmt_list = p->fmt_next;
-
- if (p->fmt_next)
- p->fmt_next->fmt_prev = p->fmt_prev;
+ spin_lock(&pfm_buffer_fmt_lock);
- printk(KERN_ERR "perfmon: removed sampling format: %s\n", p->fmt_name);
- p->fmt_next = p->fmt_prev = NULL;
- } else {
+ fmt = __pfm_find_buffer_fmt(uuid);
+ if (!fmt) {
printk(KERN_ERR "perfmon: cannot unregister format, not found\n");
ret = -EINVAL;
+ goto out;
}
- UNLOCK_BUF_FMT_LIST();
+ list_del_init(&fmt->fmt_list);
+ printk(KERN_INFO "perfmon: removed sampling format: %s\n", fmt->fmt_name);
+out:
+ spin_unlock(&pfm_buffer_fmt_lock);
return ret;
}
-/*
- * find a buffer format based on its uuid
- */
-static pfm_buffer_fmt_t *
-pfm_find_buffer_fmt(pfm_uuid_t uuid, int nolock)
-{
- pfm_buffer_fmt_t *p;
-
- LOCK_BUF_FMT_LIST();
- for (p = pfm_buffer_fmt_list; p ; p = p->fmt_next) {
- if (pfm_uuid_cmp(uuid, p->fmt_uuid) == 0) break;
- }
-
- UNLOCK_BUF_FMT_LIST();
-
- return p;
-}
-
static int
pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned int cpu)
{
@@ -2113,7 +2095,7 @@ pfmfs_delete_dentry(struct dentry *dentry)
return 1;
}
static struct dentry_operations pfmfs_dentry_operations = {
- .d_delete = pfmfs_delete_dentry,
+ .d_delete = pfmfs_delete_dentry,
};
@@ -2420,7 +2402,7 @@ pfm_setup_buffer_fmt(struct task_struct *task, pfm_context_t *ctx, unsigned int
#define PFM_CTXARG_BUF_ARG(a) (pfm_buffer_fmt_t *)(a+1)
/* invoke and lock buffer format, if found */
- fmt = pfm_find_buffer_fmt(arg->ctx_smpl_buf_id, 0);
+ fmt = pfm_find_buffer_fmt(arg->ctx_smpl_buf_id);
if (fmt == NULL) {
DPRINT(("[%d] cannot find buffer format\n", task->pid));
return -EINVAL;
@@ -2528,8 +2510,7 @@ pfm_ctx_getsize(void *arg, size_t *sz)
if (!pfm_uuid_cmp(req->ctx_smpl_buf_id, pfm_null_uuid)) return 0;
- /* no buffer locking here, will be called again */
- fmt = pfm_find_buffer_fmt(req->ctx_smpl_buf_id, 1);
+ fmt = pfm_find_buffer_fmt(req->ctx_smpl_buf_id);
if (fmt == NULL) {
DPRINT(("cannot find buffer format\n"));
return -EINVAL;
@@ -2588,7 +2569,7 @@ pfm_task_incompatible(pfm_context_t *ctx, struct task_struct *task)
/*
* make sure the task is off any CPU
*/
- pfm_wait_task_inactive(task);
+ wait_task_inactive(task);
/* more to come... */
@@ -2679,7 +2660,6 @@ pfm_context_create(pfm_context_t *ctx, void *arg, int count, struct pt_regs *reg
*/
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_unsecure = (ctx_flags & PFM_FL_UNSECURE) ? 1: 0;
ctx->ctx_fl_is_sampling = ctx->ctx_buf_fmt ? 1 : 0; /* assume record() is defined */
ctx->ctx_fl_no_msg = (ctx_flags & PFM_FL_OVFL_NO_MSG) ? 1: 0;
/*
@@ -2705,13 +2685,12 @@ pfm_context_create(pfm_context_t *ctx, void *arg, int count, struct pt_regs *reg
init_waitqueue_head(&ctx->ctx_msgq_wait);
init_waitqueue_head(&ctx->ctx_zombieq);
- DPRINT(("ctx=%p flags=0x%x system=%d notify_block=%d excl_idle=%d unsecure=%d no_msg=%d ctx_fd=%d \n",
+ DPRINT(("ctx=%p flags=0x%x system=%d notify_block=%d excl_idle=%d no_msg=%d ctx_fd=%d \n",
ctx,
ctx_flags,
ctx->ctx_fl_system,
ctx->ctx_fl_block,
ctx->ctx_fl_excl_idle,
- ctx->ctx_fl_unsecure,
ctx->ctx_fl_no_msg,
ctx->ctx_fd));
@@ -2755,14 +2734,12 @@ pfm_new_counter_value (pfm_counter_t *reg, int is_long_reset)
}
static void
-pfm_reset_regs_masked(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag)
+pfm_reset_regs_masked(pfm_context_t *ctx, unsigned long *ovfl_regs, int is_long_reset)
{
unsigned long mask = ovfl_regs[0];
unsigned long reset_others = 0UL;
unsigned long val;
- int i, is_long_reset = (flag == PFM_PMD_LONG_RESET);
-
- DPRINT_ovfl(("ovfl_regs=0x%lx flag=%d\n", ovfl_regs[0], flag));
+ int i;
/*
* now restore reset value on sampling overflowed counters
@@ -2793,19 +2770,17 @@ pfm_reset_regs_masked(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag)
}
static void
-pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int flag)
+pfm_reset_regs(pfm_context_t *ctx, unsigned long *ovfl_regs, int is_long_reset)
{
unsigned long mask = ovfl_regs[0];
unsigned long reset_others = 0UL;
unsigned long val;
- int i, is_long_reset = (flag == PFM_PMD_LONG_RESET);
-
- DPRINT_ovfl(("ovfl_regs=0x%lx flag=%d\n", ovfl_regs[0], flag));
+ int i;
- if (flag == PFM_PMD_NO_RESET) return;
+ DPRINT_ovfl(("ovfl_regs=0x%lx is_long_reset=%d\n", ovfl_regs[0], is_long_reset));
if (ctx->ctx_state == PFM_CTX_MASKED) {
- pfm_reset_regs_masked(ctx, ovfl_regs, flag);
+ pfm_reset_regs_masked(ctx, ovfl_regs, is_long_reset);
return;
}
@@ -3084,7 +3059,7 @@ pfm_write_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
{
struct thread_struct *thread = NULL;
pfarg_reg_t *req = (pfarg_reg_t *)arg;
- unsigned long value, hw_value;
+ unsigned long value, hw_value, ovfl_mask;
unsigned int cnum;
int i, can_access_pmu = 0, state;
int is_counting, is_loaded, is_system;
@@ -3094,6 +3069,7 @@ pfm_write_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;
if (state == PFM_CTX_TERMINATED || state == PFM_CTX_ZOMBIE) return -EINVAL;
@@ -3162,22 +3138,21 @@ pfm_write_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
* when context is load we use the split value
*/
if (is_loaded) {
- hw_value = value & pmu_conf.ovfl_val;
- value = value & ~pmu_conf.ovfl_val;
+ hw_value = value & ovfl_mask;
+ value = value & ~ovfl_mask;
}
-
- /*
- * update sampling periods
- */
- ctx->ctx_pmds[cnum].long_reset = req->reg_long_reset;
- ctx->ctx_pmds[cnum].short_reset = req->reg_short_reset;
-
- /*
- * update randomization parameters
- */
- ctx->ctx_pmds[cnum].seed = req->reg_random_seed;
- ctx->ctx_pmds[cnum].mask = req->reg_random_mask;
}
+ /*
+ * update reset values (not just for counters)
+ */
+ ctx->ctx_pmds[cnum].long_reset = req->reg_long_reset;
+ ctx->ctx_pmds[cnum].short_reset = req->reg_short_reset;
+
+ /*
+ * update randomization parameters (not just for counters)
+ */
+ ctx->ctx_pmds[cnum].seed = req->reg_random_seed;
+ ctx->ctx_pmds[cnum].mask = req->reg_random_mask;
/*
* update context value
@@ -3284,7 +3259,7 @@ static int
pfm_read_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
{
struct thread_struct *thread = NULL;
- unsigned long val = 0UL, lval ;
+ unsigned long val = 0UL, lval, ovfl_mask;
pfarg_reg_t *req = (pfarg_reg_t *)arg;
unsigned int cnum, reg_flags = 0;
int i, can_access_pmu = 0, state;
@@ -3299,6 +3274,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;
if (state == PFM_CTX_ZOMBIE) return -EINVAL;
@@ -3368,7 +3344,7 @@ pfm_read_pmds(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
/*
* XXX: need to check for overflow when loaded
*/
- val &= pmu_conf.ovfl_val;
+ val &= ovfl_mask;
val += ctx->ctx_pmds[cnum].val;
lval = ctx->ctx_pmds[cnum].lval;
@@ -3672,22 +3648,48 @@ pfm_restart(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
*/
ctx->ctx_state = PFM_CTX_LOADED;
+ /*
+ * XXX: not really useful for self monitoring
+ */
+ ctx->ctx_fl_can_restart = 0;
+
return 0;
}
- /* restart another task */
+
+ /*
+ * restart another task
+ */
+
+ /*
+ * When PFM_CTX_MASKED, we cannot issue a restart before the previous
+ * one is seen by the task.
+ */
+ if (state == PFM_CTX_MASKED) {
+ if (ctx->ctx_fl_can_restart == 0) return -EINVAL;
+ /*
+ * will prevent subsequent restart before this one is
+ * seen by other task
+ */
+ ctx->ctx_fl_can_restart = 0;
+ }
/*
- * if blocking, then post the semaphore.
+ * if blocking, then post the semaphore is PFM_CTX_MASKED, i.e.
+ * the task is blocked or on its way to block. That's the normal
+ * restart path. If the monitoring is not masked, then the task
+ * can be actively monitoring and we cannot directly intervene.
+ * Therefore we use the trap mechanism to catch the task and
+ * force it to reset the buffer/reset PMDs.
+ *
* if non-blocking, then we ensure that the task will go into
* pfm_handle_work() before returning to user mode.
+ *
* We cannot explicitely reset another task, it MUST always
* be done by the task itself. This works for system wide because
- * the tool that is controlling the session is doing "self-monitoring".
- *
- * XXX: what if the task never goes back to user?
- *
+ * the tool that is controlling the session is logically doing
+ * "self-monitoring".
*/
- if (CTX_OVFL_NOBLOCK(ctx) == 0) {
+ if (CTX_OVFL_NOBLOCK(ctx) == 0 && state == PFM_CTX_MASKED) {
DPRINT(("unblocking [%d] \n", task->pid));
up(&ctx->ctx_restart_sem);
} else {
@@ -3725,6 +3727,9 @@ pfm_debug(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
return 0;
}
+/*
+ * arg can be NULL and count can be zero for this function
+ */
static int
pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
{
@@ -3783,21 +3788,22 @@ pfm_write_ibr_dbr(int mode, pfm_context_t *ctx, void *arg, int count, struct pt_
/*
* check for debug registers in system wide mode
*
- * We make the reservation even when context is not loaded
- * to make sure we get our slot. Note that the PFM_LOAD_CONTEXT
- * may still fail if the task has DBG_VALID set.
+ * If though a check is done in pfm_context_load(),
+ * we must repeat it here, in case the registers are
+ * written after the context is loaded
*/
- LOCK_PFS();
+ if (is_loaded) {
+ LOCK_PFS();
- if (first_time && is_system) {
- if (pfm_sessions.pfs_ptrace_use_dbregs)
- ret = -EBUSY;
- else
- pfm_sessions.pfs_sys_use_dbregs++;
+ if (first_time && is_system) {
+ if (pfm_sessions.pfs_ptrace_use_dbregs)
+ ret = -EBUSY;
+ else
+ pfm_sessions.pfs_sys_use_dbregs++;
+ }
+ UNLOCK_PFS();
}
- UNLOCK_PFS();
-
if (ret != 0) return ret;
/*
@@ -4158,7 +4164,7 @@ pfm_context_load(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
unsigned long *pmcs_source, *pmds_source;
int the_cpu;
int ret = 0;
- int state, is_system;
+ int state, is_system, set_dbregs = 0;
state = ctx->ctx_state;
is_system = ctx->ctx_fl_system;
@@ -4173,7 +4179,7 @@ pfm_context_load(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
return -EINVAL;
}
- DPRINT(("load_pid [%d]\n", req->load_pid));
+ DPRINT(("load_pid [%d] using_dbreg=%d\n", req->load_pid, ctx->ctx_fl_using_dbreg));
if (CTX_OVFL_NOBLOCK(ctx) == 0 && req->load_pid == current->pid) {
DPRINT(("cannot use blocking mode on self for [%d]\n", current->pid));
@@ -4200,15 +4206,33 @@ pfm_context_load(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
thread = &task->thread;
- ret = -EBUSY;
-
+ ret = 0;
/*
* cannot load a context which is using range restrictions,
* into a task that is being debugged.
*/
- if (ctx->ctx_fl_using_dbreg && (thread->flags & IA64_THREAD_DBG_VALID)) {
- DPRINT(("load_pid [%d] task is debugged, cannot load range restrictions\n", req->load_pid));
- goto error;
+ if (ctx->ctx_fl_using_dbreg) {
+ if (thread->flags & IA64_THREAD_DBG_VALID) {
+ ret = -EBUSY;
+ DPRINT(("load_pid [%d] task is debugged, cannot load range restrictions\n", req->load_pid));
+ goto error;
+ }
+ LOCK_PFS();
+
+ if (is_system) {
+ if (pfm_sessions.pfs_ptrace_use_dbregs) {
+ DPRINT(("cannot load [%d] dbregs in use\n", task->pid));
+ ret = -EBUSY;
+ } else {
+ pfm_sessions.pfs_sys_use_dbregs++;
+ DPRINT(("load [%d] increased sys_use_dbreg=%lu\n", task->pid, pfm_sessions.pfs_sys_use_dbregs));
+ set_dbregs = 1;
+ }
+ }
+
+ UNLOCK_PFS();
+
+ if (ret) goto error;
}
/*
@@ -4228,13 +4252,13 @@ pfm_context_load(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
*/
the_cpu = ctx->ctx_cpu = smp_processor_id();
+ ret = -EBUSY;
/*
* now reserve the session
*/
ret = pfm_reserve_session(current, is_system, the_cpu);
if (ret) goto error;
- ret = -EBUSY;
/*
* task is necessarily stopped at this point.
*
@@ -4342,11 +4366,6 @@ pfm_context_load(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs)
/* initial saved psr (stopped) */
ctx->ctx_saved_psr_up = 0UL;
ia64_psr(regs)->up = ia64_psr(regs)->pp = 0;
-
- if (ctx->ctx_fl_unsecure) {
- ia64_psr(regs)->sp = 0;
- DPRINT(("context unsecured for [%d]\n", task->pid));
- }
}
ret = 0;
@@ -4355,6 +4374,14 @@ error_unres:
if (ret) pfm_unreserve_session(ctx, ctx->ctx_fl_system, the_cpu);
error:
/*
+ * we must undo the dbregs setting (for system-wide)
+ */
+ if (ret && set_dbregs) {
+ LOCK_PFS();
+ pfm_sessions.pfs_sys_use_dbregs--;
+ UNLOCK_PFS();
+ }
+ /*
* release task, there is now a link with the context
*/
if (is_system == 0 && task != current) {
@@ -4455,7 +4482,7 @@ pfm_context_unload(pfm_context_t *ctx, void *arg, int count, struct pt_regs *reg
*/
tregs = task == current ? regs : ia64_task_regs(task);
- if (task == current || ctx->ctx_fl_unsecure) {
+ if (task == current) {
/*
* cancel user level control
*/
@@ -4493,7 +4520,10 @@ pfm_context_unload(pfm_context_t *ctx, void *arg, int count, struct pt_regs *reg
ctx->ctx_task = NULL;
PFM_SET_WORK_PENDING(task, 0);
- ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_NONE;
+
+ ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_NONE;
+ ctx->ctx_fl_can_restart = 0;
+ ctx->ctx_fl_going_zombie = 0;
DPRINT(("disconnected [%d] from context\n", task->pid));
@@ -4686,7 +4716,7 @@ pfm_check_task_state(pfm_context_t *ctx, int cmd, unsigned long flags)
UNPROTECT_CTX(ctx, flags);
- pfm_wait_task_inactive(task);
+ wait_task_inactive(task);
PROTECT_CTX(ctx, flags);
@@ -4725,7 +4755,8 @@ sys_perfmonctl (int fd, int cmd, void *arg, int count, long arg5, long arg6, lon
PFM_CMD_IDX(cmd),
PFM_CMD_IS_VALID(cmd),
PFM_CMD_NARG(cmd),
- PFM_CMD_ARG_SIZE(cmd), count));
+ PFM_CMD_ARG_SIZE(cmd),
+ count));
/*
* check if number of arguments matches what the command expects
@@ -4842,8 +4873,10 @@ pfm_resume_after_ovfl(pfm_context_t *ctx, unsigned long ovfl_regs, struct pt_reg
{
pfm_buffer_fmt_t *fmt = ctx->ctx_buf_fmt;
pfm_ovfl_ctrl_t rst_ctrl;
+ int state;
int ret = 0;
+ state = ctx->ctx_state;
/*
* Unlock sampling buffer and reset index atomically
* XXX: not really needed when blocking
@@ -4853,9 +4886,10 @@ pfm_resume_after_ovfl(pfm_context_t *ctx, unsigned long ovfl_regs, struct pt_reg
rst_ctrl.bits.mask_monitoring = 0;
rst_ctrl.bits.reset_ovfl_pmds = 1;
- /* XXX: check return value */
- if (fmt->fmt_restart)
- ret = (*fmt->fmt_restart)(current, &rst_ctrl, ctx->ctx_smpl_hdr, regs);
+ if (state == PFM_CTX_LOADED)
+ ret = pfm_buf_fmt_restart_active(fmt, current, &rst_ctrl, ctx->ctx_smpl_hdr, regs);
+ else
+ ret = pfm_buf_fmt_restart(fmt, current, &rst_ctrl, ctx->ctx_smpl_hdr, regs);
} else {
rst_ctrl.bits.mask_monitoring = 0;
rst_ctrl.bits.reset_ovfl_pmds = 1;
@@ -4876,7 +4910,6 @@ pfm_resume_after_ovfl(pfm_context_t *ctx, unsigned long ovfl_regs, struct pt_reg
}
}
-
/*
* context MUST BE LOCKED when calling
* can only be called for current
@@ -4954,7 +4987,7 @@ pfm_handle_work(void)
reason = ctx->ctx_fl_trap_reason;
ctx->ctx_fl_trap_reason = PFM_TRAP_REASON_NONE;
- DPRINT(("[%d] reason=%d\n", current->pid, reason));
+ DPRINT(("[%d] reason=%d state=%d\n", current->pid, reason, ctx->ctx_state));
/*
* must be done before we check non-blocking mode
@@ -5085,7 +5118,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
{
pfm_ovfl_arg_t ovfl_arg;
unsigned long mask;
- unsigned long old_val;
+ unsigned long old_val, ovfl_val;
unsigned long ovfl_notify = 0UL, ovfl_pmds = 0UL, smpl_pmds = 0UL;
unsigned long tstamp;
pfm_ovfl_ctrl_t ovfl_ctrl;
@@ -5101,7 +5134,8 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
tstamp = ia64_get_itc();
- mask = pmc0 >> PMU_FIRST_COUNTER;
+ mask = pmc0 >> PMU_FIRST_COUNTER;
+ ovfl_val = pmu_conf.ovfl_val;
DPRINT_ovfl(("pmc0=0x%lx pid=%d iip=0x%lx, %s "
"used_pmds=0x%lx reload_pmcs=0x%lx\n",
@@ -5133,7 +5167,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
* pfm_read_pmds().
*/
old_val = ctx->ctx_pmds[i].val;
- ctx->ctx_pmds[i].val += 1 + pmu_conf.ovfl_val;
+ ctx->ctx_pmds[i].val += 1 + ovfl_val;
/*
* check for overflow condition
@@ -5145,7 +5179,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
DPRINT_ovfl(("ctx_pmd[%d].val=0x%lx old_val=0x%lx pmd=0x%lx ovfl_pmds=0x%lx ovfl_notify=0x%lx smpl_pmds=0x%lx\n",
i, ctx->ctx_pmds[i].val, old_val,
- ia64_get_pmd(i) & pmu_conf.ovfl_val, ovfl_pmds, ovfl_notify, smpl_pmds));
+ ia64_get_pmd(i) & ovfl_val, ovfl_pmds, ovfl_notify, smpl_pmds));
}
/*
@@ -5196,6 +5230,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
for(j=0, k=0; smpl_pmds; j++, smpl_pmds >>=1) {
if ((smpl_pmds & 0x1) == 0) continue;
ovfl_arg.smpl_pmds_values[k++] = PMD_IS_COUNTING(j) ? pfm_read_soft_counter(ctx, j) : ia64_get_pmd(j);
+ DPRINT_ovfl(("smpl_pmd[%d]=pmd%u=0x%lx\n", k-1, j, ovfl_arg.smpl_pmds_values[k-1]));
}
}
@@ -5294,6 +5329,7 @@ pfm_overflow_handler(struct task_struct *task, pfm_context_t *ctx, u64 pmc0, str
if (ovfl_ctrl.bits.mask_monitoring) {
pfm_mask_monitoring(task);
ctx->ctx_state = PFM_CTX_MASKED;
+ ctx->ctx_fl_can_restart = 1;
}
/*
@@ -5376,12 +5412,10 @@ pfm_do_interrupt_handler(int irq, void *arg, struct pt_regs *regs)
*/
/* sanity check */
- if (!ctx) goto report_spurious;
+ if (!ctx) goto report_spurious1;
- if (ctx->ctx_fl_system == 0 && (task->thread.flags & IA64_THREAD_PM_VALID) == 0) {
- printk("perfmon: current [%d] owner = [%d] PMVALID=0 state=%d\n", current->pid, task->pid, ctx->ctx_state);
- goto report_spurious;
- }
+ if (ctx->ctx_fl_system == 0 && (task->thread.flags & IA64_THREAD_PM_VALID) == 0)
+ goto report_spurious2;
PROTECT_CTX_NOPRINT(ctx, flags);
@@ -5400,14 +5434,20 @@ pfm_do_interrupt_handler(int irq, void *arg, struct pt_regs *regs)
return retval;
-report_spurious:
+report_spurious1:
printk(KERN_INFO "perfmon: spurious overflow interrupt on CPU%d: process %d has no PFM context\n",
this_cpu, task->pid);
pfm_unfreeze_pmu();
return -1;
+report_spurious2:
+ printk(KERN_INFO "perfmon: spurious overflow interrupt on CPU%d: process %d, invalid flag\n",
+ this_cpu,
+ task->pid);
+ pfm_unfreeze_pmu();
+ return -1;
}
-static pfm_irq_handler_t
+static irqreturn_t
pfm_interrupt_handler(int irq, void *arg, struct pt_regs *regs)
{
unsigned long start_cycles, total_cycles;
@@ -5436,7 +5476,8 @@ pfm_interrupt_handler(int irq, void *arg, struct pt_regs *regs)
pfm_stats[this_cpu].pfm_ovfl_intr_cycles += total_cycles;
}
- PFM_IRQ_HANDLER_RET();
+ put_cpu_no_resched();
+ return IRQ_HANDLED;
}
@@ -5445,10 +5486,13 @@ static int
pfm_proc_info(char *page)
{
char *p = page;
- pfm_buffer_fmt_t *b;
+ struct list_head * pos;
+ pfm_buffer_fmt_t * entry;
unsigned long psr;
+ int online_cpus = 0;
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, "fastctxsw : %s\n", pfm_sysctl.fastctxsw > 0 ? "Yes": "No");
p += sprintf(p, "ovfl_mask : 0x%lx\n", pmu_conf.ovfl_val);
@@ -5462,17 +5506,17 @@ pfm_proc_info(char *page)
p += sprintf(p, "CPU%-2d smpl handler calls : %lu\n", i, pfm_stats[i].pfm_smpl_handler_calls);
p += sprintf(p, "CPU%-2d smpl handler cycles : %lu\n", i, pfm_stats[i].pfm_smpl_handler_cycles);
p += sprintf(p, "CPU%-2d spurious intrs : %lu\n", i, pfm_stats[i].pfm_spurious_ovfl_intr_count);
- p += sprintf(p, "CPU%-2d sysupdt count : %lu\n", i, pfm_stats[i].pfm_sysupdt_count);
- p += sprintf(p, "CPU%-2d sysupdt cycles : %lu\n", i, pfm_stats[i].pfm_sysupdt_cycles);
+ p += sprintf(p, "CPU%-2d replay intrs : %lu\n", i, pfm_stats[i].pfm_replay_ovfl_intr_count);
p += sprintf(p, "CPU%-2d syst_wide : %d\n" , i, pfm_get_cpu_data(pfm_syst_info, i) & PFM_CPUINFO_SYST_WIDE ? 1 : 0);
p += sprintf(p, "CPU%-2d dcr_pp : %d\n" , i, pfm_get_cpu_data(pfm_syst_info, i) & PFM_CPUINFO_DCR_PP ? 1 : 0);
p += sprintf(p, "CPU%-2d exclude idle : %d\n" , i, pfm_get_cpu_data(pfm_syst_info, i) & PFM_CPUINFO_EXCL_IDLE ? 1 : 0);
p += sprintf(p, "CPU%-2d owner : %d\n" , i, pfm_get_cpu_data(pmu_owner, i) ? pfm_get_cpu_data(pmu_owner, i)->pid: -1);
p += sprintf(p, "CPU%-2d context : %p\n" , i, pfm_get_cpu_data(pmu_ctx, i));
p += sprintf(p, "CPU%-2d activations : %lu\n", i, pfm_get_cpu_data(pmu_activation_number,i));
+ online_cpus++;
}
- if (num_online_cpus() == 1)
+ if (online_cpus == 1)
{
psr = pfm_get_psr();
ia64_srlz_d();
@@ -5485,7 +5529,7 @@ pfm_proc_info(char *page)
}
LOCK_PFS();
- p += sprintf(p, "proc_sessions : %u\n"
+ p += sprintf(p, "proc_sessions : %u\n"
"sys_sessions : %u\n"
"sys_use_dbregs : %u\n"
"ptrace_use_dbregs : %u\n",
@@ -5495,29 +5539,30 @@ pfm_proc_info(char *page)
pfm_sessions.pfs_ptrace_use_dbregs);
UNLOCK_PFS();
- LOCK_BUF_FMT_LIST();
+ spin_lock(&pfm_buffer_fmt_lock);
- for (b = pfm_buffer_fmt_list; b ; b = b->fmt_next) {
+ list_for_each(pos, &pfm_buffer_fmt_list) {
+ entry = list_entry(pos, pfm_buffer_fmt_t, fmt_list);
p += sprintf(p, "format : %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x %s\n",
- b->fmt_uuid[0],
- b->fmt_uuid[1],
- b->fmt_uuid[2],
- b->fmt_uuid[3],
- b->fmt_uuid[4],
- b->fmt_uuid[5],
- b->fmt_uuid[6],
- b->fmt_uuid[7],
- b->fmt_uuid[8],
- b->fmt_uuid[9],
- b->fmt_uuid[10],
- b->fmt_uuid[11],
- b->fmt_uuid[12],
- b->fmt_uuid[13],
- b->fmt_uuid[14],
- b->fmt_uuid[15],
- b->fmt_name);
- }
- UNLOCK_BUF_FMT_LIST();
+ entry->fmt_uuid[0],
+ entry->fmt_uuid[1],
+ entry->fmt_uuid[2],
+ entry->fmt_uuid[3],
+ entry->fmt_uuid[4],
+ entry->fmt_uuid[5],
+ entry->fmt_uuid[6],
+ entry->fmt_uuid[7],
+ entry->fmt_uuid[8],
+ entry->fmt_uuid[9],
+ entry->fmt_uuid[10],
+ entry->fmt_uuid[11],
+ entry->fmt_uuid[12],
+ entry->fmt_uuid[13],
+ entry->fmt_uuid[14],
+ entry->fmt_uuid[15],
+ entry->fmt_name);
+ }
+ spin_unlock(&pfm_buffer_fmt_lock);
return p - page;
}
@@ -5546,7 +5591,7 @@ perfmon_read_entry(char *page, char **start, off_t off, int count, int *eof, voi
* local_cpu_data->pfm_syst_info
*/
void
-pfm_do_syst_wide_update_task(struct task_struct *task, unsigned long info, int is_ctxswin)
+pfm_syst_wide_update_task(struct task_struct *task, unsigned long info, int is_ctxswin)
{
struct pt_regs *regs;
unsigned long dcr;
@@ -5591,21 +5636,10 @@ pfm_do_syst_wide_update_task(struct task_struct *task, unsigned long info, int i
}
}
-void
-pfm_syst_wide_update_task(struct task_struct *task, unsigned long info, int is_ctxswin)
-{
- unsigned long start, end;
-
- pfm_stats[smp_processor_id()].pfm_sysupdt_count++;
- start = ia64_get_itc();
-
- pfm_do_syst_wide_update_task(task, info, is_ctxswin);
-
- end = ia64_get_itc();
- pfm_stats[smp_processor_id()].pfm_sysupdt_cycles += end-start;
-}
-
#ifdef CONFIG_SMP
+/*
+ * in 2.6, interrupts are masked when we come here and the runqueue lock is held
+ */
void
pfm_save_regs(struct task_struct *task)
{
@@ -5706,14 +5740,11 @@ pfm_save_regs(struct task_struct *task)
/*
* unfreeze PMU if had pending overflows
*/
- if (t->pmcs[0] & ~1UL) pfm_unfreeze_pmu();
+ if (t->pmcs[0] & ~0x1UL) pfm_unfreeze_pmu();
/*
- * finally, unmask interrupts and allow context
- * access.
- * Any pended overflow interrupt may be delivered
- * here and will be treated as spurious because we
- * have have no PMU owner anymore.
+ * finally, allow context access.
+ * interrupts will still be masked after this call.
*/
pfm_unprotect_ctx_ctxsw(ctx, flags);
@@ -5726,10 +5757,6 @@ save_error:
}
#else /* !CONFIG_SMP */
-
-/*
- * in 2.5, interrupts are masked when we come here
- */
void
pfm_save_regs(struct task_struct *task)
{
@@ -5836,6 +5863,9 @@ pfm_lazy_save_regs (struct task_struct *task)
#endif /* CONFIG_SMP */
#ifdef CONFIG_SMP
+/*
+ * in 2.6, interrupts are masked when we come here and the runqueue lock is held
+ */
void
pfm_load_regs (struct task_struct *task)
{
@@ -5959,20 +5989,24 @@ pfm_load_regs (struct task_struct *task)
* was saved.
*/
if (unlikely(PMC0_HAS_OVFL(t->pmcs[0]))) {
- struct pt_regs *regs = ia64_task_regs(task);
- pfm_overflow_handler(task, ctx, t->pmcs[0], regs);
+ /*
+ * reload pmc0 with the overflow information
+ * On McKinley PMU, this will trigger a PMU interrupt
+ */
+ ia64_set_pmc(0, t->pmcs[0]);
+ ia64_srlz_d();
+ t->pmcs[0] = 0UL;
+#ifndef CONFIG_MCKINLEY
+ /*
+ * will replay the PMU interrupt
+ */
+ DRPINT(("perfmon: resend irq for [%d]\n", task->pid));
+ hw_resend_irq(NULL, IA64_PERFMON_VECTOR);
+#endif
+ pfm_stats[smp_processor_id()].pfm_replay_ovfl_intr_count++;
}
/*
- * we clear PMC0, to ensure that any in flight interrupt
- * will not be attributed to the new context we are installing
- * because the actual overflow has been processed above already.
- * No real effect until we unmask interrupts at the end of the
- * function.
- */
- pfm_unfreeze_pmu();
-
- /*
* we just did a reload, so we reset the partial reload fields
*/
ctx->ctx_reload_pmcs[0] = 0UL;
@@ -5990,13 +6024,15 @@ pfm_load_regs (struct task_struct *task)
SET_ACTIVATION(ctx);
/*
- * establish new ownership. Interrupts
- * are still masked at this point.
+ * establish new ownership.
*/
SET_PMU_OWNER(task, ctx);
/*
- * restore the psr.up bit
+ * restore the psr.up bit. measurement
+ * is active again.
+ * no PMU interrupt can happen at this point
+ * because we still have interrupts disabled.
*/
if (likely(psr_up)) pfm_set_psr_up();
@@ -6091,42 +6127,39 @@ pfm_load_regs (struct task_struct *task)
pfm_restore_pmcs(t->pmcs, pmc_mask);
/*
- * Check for pending overflow when state was last saved.
- * invoked handler is overflow status bits set.
- *
- * Any PMU overflow in flight at this point, will still
- * be treated as spurious because we have no declared
- * owner. Note that the first level interrupt handler
- * DOES NOT TOUCH any PMC except PMC0 for which we have
- * a copy already.
+ * check for pending overflow at the time the state
+ * was saved.
*/
if (unlikely(PMC0_HAS_OVFL(t->pmcs[0]))) {
- struct pt_regs *regs = ia64_task_regs(task);
- pfm_overflow_handler(task, ctx, t->pmcs[0], regs);
- }
+ /*
+ * reload pmc0 with the overflow information
+ * On McKinley PMU, this will trigger a PMU interrupt
+ */
+ ia64_set_pmc(0, t->pmcs[0]);
+ ia64_srlz_d();
- /*
- * we clear PMC0, to ensure that any in flight interrupt
- * will not be attributed to the new context we are installing
- * because the actual overflow has been processed above already.
- *
- * This is an atomic operation.
- */
- pfm_unfreeze_pmu();
+ t->pmcs[0] = 0UL;
+
+#ifndef CONFIG_MCKINLEY
+ /*
+ * will replay the PMU interrupt
+ */
+ DRPINT(("perfmon: resend irq for [%d]\n", task->pid));
+ hw_resend_irq(NULL, IA64_PERFMON_VECTOR);
+#endif
+ pfm_stats[smp_processor_id()].pfm_replay_ovfl_intr_count++;
+ }
/*
- * establish new ownership. If there was an in-flight
- * overflow interrupt, it will be treated as spurious
- * before and after the call, because no overflow
- * status bit can possibly be set. No new overflow
- * can be generated because, at this point, psr.up
- * is still cleared.
+ * establish new ownership.
*/
SET_PMU_OWNER(task, ctx);
/*
- * restore the psr. This is the point at which
- * new overflow interrupts can be generated again.
+ * restore the psr.up bit. measurement
+ * is active again.
+ * no PMU interrupt can happen at this point
+ * because we still have interrupts disabled.
*/
if (likely(psr_up)) pfm_set_psr_up();
}
@@ -6139,7 +6172,7 @@ static void
pfm_flush_pmds(struct task_struct *task, pfm_context_t *ctx)
{
u64 pmc0;
- unsigned long mask2, val, pmd_val;
+ unsigned long mask2, val, pmd_val, ovfl_val;
int i, can_access_pmu = 0;
int is_self;
@@ -6187,7 +6220,7 @@ pfm_flush_pmds(struct task_struct *task, pfm_context_t *ctx)
*/
task->thread.pmcs[0] &= ~0x1;
}
-
+ ovfl_val = pmu_conf.ovfl_val;
/*
* we save all the used pmds
* we take care of overflows for counting PMDs
@@ -6210,12 +6243,12 @@ pfm_flush_pmds(struct task_struct *task, pfm_context_t *ctx)
task->pid,
i,
ctx->ctx_pmds[i].val,
- val & pmu_conf.ovfl_val));
+ val & ovfl_val));
/*
* we rebuild the full 64 bit value of the counter
*/
- val = ctx->ctx_pmds[i].val + (val & pmu_conf.ovfl_val);
+ val = ctx->ctx_pmds[i].val + (val & ovfl_val);
/*
* now everything is in ctx_pmds[] and we need
@@ -6228,7 +6261,7 @@ pfm_flush_pmds(struct task_struct *task, pfm_context_t *ctx)
* take care of overflow inline
*/
if (pmc0 & (1UL << i)) {
- val += 1 + pmu_conf.ovfl_val;
+ val += 1 + ovfl_val;
DPRINT(("[%d] pmd[%d] overflowed\n", task->pid, i));
}
}
@@ -6338,7 +6371,7 @@ pfm_init(void)
* initialize all our spinlocks
*/
spin_lock_init(&pfm_sessions.pfs_lock);
- spin_lock_init(&pfm_smpl_fmt_lock);
+ spin_lock_init(&pfm_buffer_fmt_lock);
init_pfm_fs();
@@ -6352,6 +6385,9 @@ pfm_init(void)
__initcall(pfm_init);
+/*
+ * this function is called before pfm_init()
+ */
void
pfm_init_percpu (void)
{
@@ -6364,7 +6400,6 @@ pfm_init_percpu (void)
pfm_clear_psr_pp();
pfm_clear_psr_up();
-
if (smp_processor_id() == 0)
register_percpu_irq(IA64_PERFMON_VECTOR, &perfmon_irqaction);
diff --git a/arch/ia64/kernel/perfmon_itanium.h b/arch/ia64/kernel/perfmon_itanium.h
index 05f927444110..af81ba44b280 100644
--- a/arch/ia64/kernel/perfmon_itanium.h
+++ b/arch/ia64/kernel/perfmon_itanium.h
@@ -81,6 +81,8 @@ pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnu
*/
if (cnum == 13 && ((*val & 0x1) == 0UL) && ctx->ctx_fl_using_dbreg == 0) {
+ DPRINT(("pmc[%d]=0x%lx has active pmc13.ta cleared, clearing ibr\n", cnum, *val));
+
/* don't mix debug with perfmon */
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
@@ -98,6 +100,8 @@ pfm_ita_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnu
*/
if (cnum == 11 && ((*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));
+
/* don't mix debug with perfmon */
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
diff --git a/arch/ia64/kernel/perfmon_mckinley.h b/arch/ia64/kernel/perfmon_mckinley.h
index 2e67421055c0..28818def428b 100644
--- a/arch/ia64/kernel/perfmon_mckinley.h
+++ b/arch/ia64/kernel/perfmon_mckinley.h
@@ -109,10 +109,20 @@ pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnu
if (ctx == NULL) return -EINVAL;
/*
- * we must clear the debug registers if any pmc13.ena_dbrpX bit is enabled
- * before they are written (fl_using_dbreg==0) to avoid picking up stale information.
+ * we must clear the debug registers if pmc13 has a value which enable
+ * memory pipeline event constraints. In this case we need to clear the
+ * the debug registers if they have not yet been accessed. This is required
+ * to avoid picking stale state.
+ * PMC13 is "active" if:
+ * 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.
*/
- if (cnum == 13 && (*val & (0xfUL << 45)) && ctx->ctx_fl_using_dbreg == 0) {
+ if (cnum == 13 && ((*val & 0x18181818UL) != 0x18181818UL) && ctx->ctx_fl_using_dbreg == 0) {
+
+ DPRINT(("pmc[%d]=0x%lx has active pmc13 settings, clearing dbr\n", cnum, *val));
/* don't mix debug with perfmon */
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
@@ -128,7 +138,9 @@ pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnu
* we must clear the (instruction) debug registers if any pmc14.ibrpX bit is enabled
* before they are (fl_using_dbreg==0) to avoid picking up stale information.
*/
- if (cnum == 14 && ((*val & 0x2222) != 0x2222) && ctx->ctx_fl_using_dbreg == 0) {
+ if (cnum == 14 && ((*val & 0x2222UL) != 0x2222UL) && ctx->ctx_fl_using_dbreg == 0) {
+
+ DPRINT(("pmc[%d]=0x%lx has active pmc14 settings, clearing ibr\n", cnum, *val));
/* don't mix debug with perfmon */
if (task && (task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL;
@@ -170,7 +182,7 @@ pfm_mck_pmc_check(struct task_struct *task, pfm_context_t *ctx, unsigned int cnu
&& ((((val14>>1) & 0x3) == 0x2 || ((val14>>1) & 0x3) == 0x0)
||(((val14>>4) & 0x3) == 0x2 || ((val14>>4) & 0x3) == 0x0));
- if (ret) printk("perfmon: failure check_case1\n");
+ if (ret) DPRINT((KERN_DEBUG "perfmon: failure check_case1\n"));
}
return ret ? -EINVAL : 0;
diff --git a/include/asm-ia64/perfmon.h b/include/asm-ia64/perfmon.h
index b8e81aa3bffa..ac497dc3fb15 100644
--- a/include/asm-ia64/perfmon.h
+++ b/include/asm-ia64/perfmon.h
@@ -38,7 +38,6 @@
*/
#define PFM_FL_NOTIFY_BLOCK 0x01 /* block task on user level notifications */
#define PFM_FL_SYSTEM_WIDE 0x02 /* create a system wide context */
-#define PFM_FL_UNSECURE 0x04 /* allow unsecure monitoring for non self-monitoring task */
#define PFM_FL_OVFL_NO_MSG 0x80 /* do not post overflow/end messages for notification */
/*
@@ -162,8 +161,6 @@ typedef union {
*/
#define PFM_VERSION_MAJ 2U
#define PFM_VERSION_MIN 0U
-#define PFM_SMPL_HDR_VERSION_MAJ 2U
-#define PFM_SMPL_HDR_VERSION_MIN 0U
#define PFM_VERSION (((PFM_VERSION_MAJ&0xffff)<<16)|(PFM_VERSION_MIN & 0xffff))
#define PFM_VERSION_MAJOR(x) (((x)>>16) & 0xffff)
#define PFM_VERSION_MINOR(x) ((x) & 0xffff)
@@ -194,9 +191,8 @@ extern void pfm_handle_work(void);
/*
* Reset PMD register flags
*/
-#define PFM_PMD_NO_RESET 0
+#define PFM_PMD_SHORT_RESET 0
#define PFM_PMD_LONG_RESET 1
-#define PFM_PMD_SHORT_RESET 2
typedef union {
unsigned int val;
@@ -223,7 +219,7 @@ typedef struct {
} pfm_ovfl_arg_t;
-typedef struct _pfm_buffer_fmt_t {
+typedef struct {
char *fmt_name;
pfm_uuid_t fmt_uuid;
size_t fmt_arg_size;
@@ -237,8 +233,7 @@ typedef struct _pfm_buffer_fmt_t {
int (*fmt_restart_active)(struct task_struct *task, pfm_ovfl_ctrl_t *ctrl, void *buf, struct pt_regs *regs);
int (*fmt_exit)(struct task_struct *task, void *buf, struct pt_regs *regs);
- struct _pfm_buffer_fmt_t *fmt_next;
- struct _pfm_buffer_fmt_t *fmt_prev;
+ struct list_head fmt_list;
} pfm_buffer_fmt_t;
extern int pfm_register_buffer_fmt(pfm_buffer_fmt_t *fmt);