#define GET_PMU_OWNER() pfm_get_cpu_var(pmu_owner)
#define GET_PMU_CTX() pfm_get_cpu_var(pmu_ctx)
-#define LOCK_PFS() spin_lock(&pfm_sessions.pfs_lock)
-#define UNLOCK_PFS() spin_unlock(&pfm_sessions.pfs_lock)
+#define LOCK_PFS(g) spin_lock_irqsave(&pfm_sessions.pfs_lock, g)
+#define UNLOCK_PFS(g) spin_unlock_irqrestore(&pfm_sessions.pfs_lock, g)
#define PFM_REG_RETFLAG_SET(flags, val) do { flags &= ~PFM_REG_RETFL_MASK; flags |= (val); } while(0)
.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);
static void pfm_lazy_save_regs (struct task_struct *ta);
#endif
+void dump_pmu_state(const char *);
+
/*
* the HP simulator must be first because
* CONFIG_IA64_HP_SIM is independent of CONFIG_MCKINLEY or CONFIG_ITANIUM
static int
pfm_reserve_session(struct task_struct *task, int is_syswide, unsigned int cpu)
{
+ unsigned long flags;
/*
* validy checks on cpu_mask have been done upstream
*/
- LOCK_PFS();
+ LOCK_PFS(flags);
DPRINT(("in sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n",
pfm_sessions.pfs_sys_sessions,
is_syswide,
cpu));
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
return 0;
pfm_sessions.pfs_sys_session[cpu]->pid,
smp_processor_id()));
abort:
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
return -EBUSY;
static int
pfm_unreserve_session(pfm_context_t *ctx, int is_syswide, unsigned int cpu)
{
-
+ unsigned long flags;
/*
* validy checks on cpu_mask have been done upstream
*/
- LOCK_PFS();
+ LOCK_PFS(flags);
DPRINT(("in sys_sessions=%u task_sessions=%u dbregs=%u syswide=%d cpu=%u\n",
pfm_sessions.pfs_sys_sessions,
is_syswide,
cpu));
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
return 0;
}
}
/*
- * context is locked when coming here
+ * context is locked when coming here and interrupts are disabled
*/
static inline int
pfm_do_fasync(int fd, struct file *filp, pfm_context_t *ctx, int on)
* even if the task itself is in the middle of being ctxsw out.
*/
static int pfm_context_unload(pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs);
+
static int
pfm_close(struct inode *inode, struct file *filp)
{
int free_possible = 1;
int state, is_system;
- { u64 psr = pfm_get_psr();
- BUG_ON((psr & IA64_PSR_I) == 0UL);
- }
-
DPRINT(("pfm_close called private=%p\n", filp->private_data));
if (!inode) {
}
if (PFM_IS_FILE(filp) == 0) {
- printk(KERN_ERR "perfmon: pfm_close: bad magic [%d]\n", current->pid);
+ DPRINT(("bad magic for [%d]\n", current->pid));
return -EBADF;
}
printk(KERN_ERR "perfmon: pfm_close: NULL ctx [%d]\n", current->pid);
return -EBADF;
}
+ /*
+ * remove our file from the async queue, if we use this mode.
+ * This can be done without the context being protected. We come
+ * here when the context has become unreacheable by other tasks.
+ *
+ * We may still have active monitoring at this point and we may
+ * end up in pfm_overflow_handler(). However, fasync_helper()
+ * operates with interrupts disabled and it cleans up the
+ * queue. If the PMU handler is called prior to entering
+ * fasync_helper() then it will send a signal. If it is
+ * invoked after, it will find an empty queue and no
+ * signal will be sent. In both case, we are safe
+ */
+ if (filp->f_flags & FASYNC) {
+ DPRINT(("[%d] cleaning up async_queue=%p\n", current->pid, ctx->ctx_async_queue));
+ pfm_do_fasync (-1, filp, ctx, 0);
+ }
PROTECT_CTX(ctx, flags);
task = PFM_CTX_TASK(ctx);
- /*
- * remove our file from the async queue, if we use it
- */
- if (filp->f_flags & FASYNC) {
- DPRINT(("[%d] before async_queue=%p\n", current->pid, ctx->ctx_async_queue));
- pfm_do_fasync (-1, filp, ctx, 0);
- DPRINT(("[%d] after async_queue=%p\n", current->pid, ctx->ctx_async_queue));
- }
+ regs = ia64_task_regs(task);
- DPRINT(("[%d] ctx_state=%d\n", current->pid, state));
+ DPRINT(("[%d] ctx_state=%d is_current=%d\n",
+ current->pid, state,
+ task == current ? 1 : 0));
if (state == PFM_CTX_UNLOADED || state == PFM_CTX_TERMINATED) {
goto doit;
}
- regs = ia64_task_regs(task);
-
/*
* context still loaded/masked and self monitoring,
* we stop/unload and we destroy right here
ctx->ctx_state = PFM_CTX_TERMINATED;
- DPRINT(("[%d] ctx_state=%d\n", current->pid, state));
+ DPRINT(("[%d] ctx_state=%d\n", current->pid, ctx->ctx_state));
}
goto doit;
}
-
/*
* The task is currently blocked or will block after an overflow.
* we must force it to wakeup to get out of the
pfm_use_debug_registers(struct task_struct *task)
{
pfm_context_t *ctx = task->thread.pfm_context;
+ unsigned long flags;
int ret = 0;
if (pmu_conf.use_rr_dbregs == 0) return 0;
*/
if (ctx && ctx->ctx_fl_using_dbreg == 1) return -1;
- LOCK_PFS();
+ LOCK_PFS(flags);
/*
* We cannot allow setting breakpoints when system wide monitoring
pfm_sessions.pfs_sys_use_dbregs,
task->pid, ret));
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
return ret;
}
int
pfm_release_debug_registers(struct task_struct *task)
{
+ unsigned long flags;
int ret;
if (pmu_conf.use_rr_dbregs == 0) return 0;
- LOCK_PFS();
+ LOCK_PFS(flags);
if (pfm_sessions.pfs_ptrace_use_dbregs == 0) {
printk(KERN_ERR "perfmon: invalid release for [%d] ptrace_use_dbregs=0\n", task->pid);
ret = -1;
pfm_sessions.pfs_ptrace_use_dbregs--;
ret = 0;
}
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
return ret;
}
memset(pfm_stats, 0, sizeof(pfm_stats));
for(m=0; m < NR_CPUS; m++) pfm_stats[m].pfm_ovfl_intr_cycles_min = ~0UL;
}
-
return 0;
}
{
struct thread_struct *thread = NULL;
pfarg_dbreg_t *req = (pfarg_dbreg_t *)arg;
+ unsigned long flags;
dbreg_t dbreg;
unsigned int rnum;
int first_time;
* written after the context is loaded
*/
if (is_loaded) {
- LOCK_PFS();
+ LOCK_PFS(flags);
if (first_time && is_system) {
if (pfm_sessions.pfs_ptrace_use_dbregs)
else
pfm_sessions.pfs_sys_use_dbregs++;
}
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
}
if (ret != 0) return ret;
* in case it was our first attempt, we undo the global modifications
*/
if (first_time) {
- LOCK_PFS();
+ LOCK_PFS(flags);
if (ctx->ctx_fl_system) {
pfm_sessions.pfs_sys_use_dbregs--;
}
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
ctx->ctx_fl_using_dbreg = 0;
}
/*
DPRINT(("[%d] should be running on CPU%d\n", current->pid, ctx->ctx_cpu));
return -EBUSY;
}
-
+ DPRINT(("current [%d] task [%d] ctx_state=%d is_system=%d\n",
+ current->pid,
+ PFM_CTX_TASK(ctx)->pid,
+ state,
+ is_system));
/*
* in system mode, we need to update the PMU directly
* and the user level state of the caller, which may not
struct task_struct *task;
struct thread_struct *thread;
struct pfm_context_t *old;
+ unsigned long flags;
#ifndef CONFIG_SMP
struct task_struct *owner_task = NULL;
#endif
DPRINT(("load_pid [%d] task is debugged, cannot load range restrictions\n", req->load_pid));
goto error;
}
- LOCK_PFS();
+ LOCK_PFS(flags);
if (is_system) {
if (pfm_sessions.pfs_ptrace_use_dbregs) {
}
}
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
if (ret) goto error;
}
* we must undo the dbregs setting (for system-wide)
*/
if (ret && set_dbregs) {
- LOCK_PFS();
+ LOCK_PFS(flags);
pfm_sessions.pfs_sys_use_dbregs--;
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
}
/*
* release task, there is now a link with the context
printk(KERN_ERR "perfmon: pfm_exit_thread [%d] unexpected state=%d\n", task->pid, state);
break;
}
+ UNPROTECT_CTX(ctx, flags);
+
{ u64 psr = pfm_get_psr();
BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP));
BUG_ON(GET_PMU_OWNER());
+ BUG_ON(ia64_psr(regs)->up);
+ BUG_ON(ia64_psr(regs)->pp);
}
- UNPROTECT_CTX(ctx, flags);
/*
* All memory free operations (especially for vmalloc'ed memory)
char *p = page;
struct list_head * pos;
pfm_buffer_fmt_t * entry;
- unsigned long psr;
+ unsigned long psr, flags;
int online_cpus = 0;
int i;
}
}
- LOCK_PFS();
+ LOCK_PFS(flags);
p += sprintf(p, "proc_sessions : %u\n"
"sys_sessions : %u\n"
"sys_use_dbregs : %u\n"
pfm_sessions.pfs_sys_sessions,
pfm_sessions.pfs_sys_use_dbregs,
pfm_sessions.pfs_ptrace_use_dbregs);
- UNLOCK_PFS();
+ UNLOCK_PFS(flags);
spin_lock(&pfm_buffer_fmt_lock);
*/
ctx->ctx_saved_psr_up = psr & IA64_PSR_UP;
- { u64 foo = pfm_get_psr();
- BUG_ON(foo & ((IA64_PSR_UP|IA64_PSR_PP)));
- }
-
/*
* release ownership of this PMU.
* PM interrupts are masked, so nothing
*/
psr = pfm_get_psr();
+ BUG_ON(foo & (IA64_PSR_I));
+
/*
* stop monitoring:
* This is the last instruction which may generate an overflow
*/
ctx->ctx_saved_psr_up = psr & IA64_PSR_UP;
-#if 1
- { u64 foo = pfm_get_psr();
- BUG_ON(foo & (IA64_PSR_I));
- BUG_ON(foo & ((IA64_PSR_UP|IA64_PSR_PP)));
- }
-#endif
return;
save_error:
printk(KERN_ERR "perfmon: pfm_save_regs CPU%d [%d] NULL context PM_VALID=%ld\n",
struct thread_struct *t;
unsigned long flags;
-#if 1
- { u64 foo = pfm_get_psr();
- BUG_ON(foo & IA64_PSR_UP);
+ { u64 psr = pfm_get_psr();
+ BUG_ON(psr & IA64_PSR_UP);
}
-#endif
ctx = PFM_GET_CTX(task);
t = &task->thread;
/*
* unfreeze PMU if had pending overflows
*/
- if (t->pmcs[0] & ~1UL) pfm_unfreeze_pmu();
+ if (t->pmcs[0] & ~0x1UL) pfm_unfreeze_pmu();
/*
* now get can unmask PMU interrupts, they will
flags = pfm_protect_ctx_ctxsw(ctx);
psr = pfm_get_psr();
-#if 1
BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP));
BUG_ON(psr & IA64_PSR_I);
-#endif
if (unlikely(ctx->ctx_state == PFM_CTX_ZOMBIE)) {
struct pt_regs *regs = ia64_task_regs(task);
t = &task->thread;
psr = pfm_get_psr();
-#if 1
BUG_ON(psr & (IA64_PSR_UP|IA64_PSR_PP));
BUG_ON(psr & IA64_PSR_I);
-#endif
/*
* we restore ALL the debug registers to avoid picking up
/*
* clear whatever overflow status bits there were
*/
- task->thread.pmcs[0] &= ~0x1;
+ task->thread.pmcs[0] = 0;
}
ovfl_val = pmu_conf.ovfl_val;
/*
pfm_clear_psr_pp();
pfm_clear_psr_up();
+ /*
+ * we run with the PMU not frozen at all times
+ */
+ pfm_unfreeze_pmu();
+
if (smp_processor_id() == 0)
register_percpu_irq(IA64_PERFMON_VECTOR, &perfmon_irqaction);
if (PMD_IS_IMPL(i) == 0) continue;
ia64_set_pmd(i, 0UL);
}
-
- /*
- * we run with the PMU not frozen at all times
- */
- pfm_unfreeze_pmu();
}
/*
* used for debug purposes only
*/
void
-dump_pmu_state(void)
+dump_pmu_state(const char *from)
{
struct task_struct *task;
struct thread_struct *t;
+ struct pt_regs *regs;
pfm_context_t *ctx;
- unsigned long psr;
- int i;
+ unsigned long psr, dcr, info, flags;
+ int i, this_cpu;
+
+ local_irq_save(flags);
- printk("current [%d] %s\n", current->pid, current->comm);
+ this_cpu = smp_processor_id();
+ regs = ia64_task_regs(current);
+ info = PFM_CPUINFO_GET();
+ dcr = ia64_getreg(_IA64_REG_CR_DCR);
+
+ if (info == 0 && ia64_psr(regs)->pp == 0 && (dcr & IA64_DCR_PP) == 0) {
+ local_irq_restore(flags);
+ return;
+ }
+
+ printk("CPU%d from %s() current [%d] iip=0x%lx %s\n",
+ this_cpu,
+ from,
+ current->pid,
+ regs->cr_iip,
+ current->comm);
task = GET_PMU_OWNER();
ctx = GET_PMU_CTX();
- printk("owner [%d] ctx=%p\n", task ? task->pid : -1, ctx);
+ printk("->CPU%d owner [%d] ctx=%p\n", this_cpu, task ? task->pid : -1, ctx);
psr = pfm_get_psr();
- printk("psr.pp=%ld psr.up=%ld\n", (psr >> IA64_PSR_PP_BIT) &0x1UL, (psr >> IA64_PSR_PP_BIT)&0x1UL);
+ printk("->CPU%d pmc0=0x%lx psr.pp=%d psr.up=%d dcr.pp=%d syst_info=0x%lx user_psr.up=%d user_psr.pp=%d\n",
+ this_cpu,
+ ia64_get_pmc(0),
+ psr & IA64_PSR_PP ? 1 : 0,
+ psr & IA64_PSR_UP ? 1 : 0,
+ dcr & IA64_DCR_PP ? 1 : 0,
+ info,
+ ia64_psr(regs)->up,
+ ia64_psr(regs)->pp);
+
+ ia64_psr(regs)->up = 0;
+ ia64_psr(regs)->pp = 0;
t = ¤t->thread;
for (i=1; PMC_IS_LAST(i) == 0; i++) {
if (PMC_IS_IMPL(i) == 0) continue;
- printk("pmc[%d]=0x%lx tpmc=0x%lx\n", i, ia64_get_pmc(i), t->pmcs[i]);
+ printk("->CPU%d pmc[%d]=0x%lx thread_pmc[%d]=0x%lx\n", this_cpu, i, ia64_get_pmc(i), i, t->pmcs[i]);
}
for (i=1; PMD_IS_LAST(i) == 0; i++) {
if (PMD_IS_IMPL(i) == 0) continue;
- printk("pmd[%d]=0x%lx tpmd=0x%lx\n", i, ia64_get_pmd(i), t->pmds[i]);
+ printk("->CPU%d pmd[%d]=0x%lx thread_pmd[%d]=0x%lx\n", this_cpu, i, ia64_get_pmd(i), i, t->pmds[i]);
}
+
if (ctx) {
- printk("ctx_state=%d vaddr=%p addr=%p fd=%d ctx_task=[%d] saved_psr_up=0x%lx\n",
+ printk("->CPU%d ctx_state=%d vaddr=%p addr=%p fd=%d ctx_task=[%d] saved_psr_up=0x%lx\n",
+ this_cpu,
ctx->ctx_state,
ctx->ctx_smpl_vaddr,
ctx->ctx_smpl_hdr,
ctx->ctx_msgq_tail,
ctx->ctx_saved_psr_up);
}
+ local_irq_restore(flags);
}
/*
PFM_SET_WORK_PENDING(task, 0);
/*
- * restore default psr settings
+ * the psr bits are already set properly in copy_threads()
*/
- ia64_psr(regs)->pp = ia64_psr(regs)->up = 0;
- ia64_psr(regs)->sp = 1;
}
#else /* !CONFIG_PERFMON */
asmlinkage long