📄 perfmon.c
字号:
* new value of pmc[0]. if 0x0 then unfreeze, else keep frozen */unsigned longupdate_counters (struct task_struct *task, u64 pmc0, struct pt_regs *regs){ unsigned long mask, i, cnum; struct thread_struct *th; pfm_context_t *ctx; unsigned long bv = 0; int my_cpu = smp_processor_id(); int ret = 1, buffer_is_full = 0; int ovfl_has_long_recovery, can_notify, need_reset_pmd16=0; struct siginfo si; /* * It is never safe to access the task for which the overflow interrupt is destinated * using the current variable as the interrupt may occur in the middle of a context switch * where current does not hold the task that is running yet. * * For monitoring, however, we do need to get access to the task which caused the overflow * to account for overflow on the counters. * * We accomplish this by maintaining a current owner of the PMU per CPU. During context * switch the ownership is changed in a way such that the reflected owner is always the * valid one, i.e. the one that caused the interrupt. */ if (task == NULL) { DBprintk((" owners[%d]=NULL\n", my_cpu)); return 0x1; } th = &task->thread; ctx = th->pfm_context; /* * XXX: debug test * Don't think this could happen given upfront tests */ if ((th->flags & IA64_THREAD_PM_VALID) == 0 && ctx->ctx_fl_system == 0) { printk("perfmon: Spurious overflow interrupt: process %d not using perfmon\n", task->pid); return 0x1; } if (!ctx) { printk("perfmon: Spurious overflow interrupt: process %d has no PFM context\n", task->pid); return 0; } /* * sanity test. Should never happen */ if ((pmc0 & 0x1 )== 0) { printk("perfmon: pid %d pmc0=0x%lx assumption error for freeze bit\n", task->pid, pmc0); return 0x0; } mask = pmc0 >> PMU_FIRST_COUNTER; DBprintk(("pmc0=0x%lx pid=%d owner=%d iip=0x%lx, ctx is in %s mode used_pmds=0x%lx used_pmcs=0x%lx\n", pmc0, task->pid, PMU_OWNER()->pid, regs->cr_iip, CTX_OVFL_NOBLOCK(ctx) ? "NO-BLOCK" : "BLOCK", ctx->ctx_used_pmds[0], ctx->ctx_used_pmcs[0])); /* * XXX: need to record sample only when an EAR/BTB has overflowed */ if (CTX_HAS_SMPL(ctx)) { pfm_smpl_buffer_desc_t *psb = ctx->ctx_smpl_buf; unsigned long *e, m, idx=0; perfmon_smpl_entry_t *h; int j; idx = ia64_fetch_and_add(1, &psb->psb_index); DBprintk((" recording index=%ld entries=%ld\n", idx, psb->psb_entries)); /* * XXX: there is a small chance that we could run out on index before resetting * but index is unsigned long, so it will take some time..... * We use > instead of == because fetch_and_add() is off by one (see below) * * This case can happen in non-blocking mode or with multiple processes. * For non-blocking, we need to reload and continue. */ if (idx > psb->psb_entries) { buffer_is_full = 1; goto reload_pmds; } /* first entry is really entry 0, not 1 caused by fetch_and_add */ idx--; h = (perfmon_smpl_entry_t *)(((char *)psb->psb_addr) + idx*(psb->psb_entry_size)); h->pid = task->pid; h->cpu = my_cpu; h->rate = 0; h->ip = regs ? regs->cr_iip : 0x0; /* where did the fault happened */ h->regs = mask; /* which registers overflowed */ /* guaranteed to monotonically increase on each cpu */ h->stamp = perfmon_get_stamp(); e = (unsigned long *)(h+1); /* * selectively store PMDs in increasing index number */ for (j=0, m = ctx->ctx_smpl_regs; m; m >>=1, j++) { if (m & 0x1) { if (PMD_IS_COUNTER(j)) *e = ctx->ctx_pmds[j-PMU_FIRST_COUNTER].val + (ia64_get_pmd(j) & pmu_conf.perf_ovfl_val); else { *e = ia64_get_pmd(j); /* slow */ } DBprintk((" e=%p pmd%d =0x%lx\n", (void *)e, j, *e)); e++; } } /* * make the new entry visible to user, needs to be atomic */ ia64_fetch_and_add(1, &psb->psb_hdr->hdr_count); DBprintk((" index=%ld entries=%ld hdr_count=%ld\n", idx, psb->psb_entries, psb->psb_hdr->hdr_count)); /* * sampling buffer full ? */ if (idx == (psb->psb_entries-1)) { /* * will cause notification, cannot be 0 */ bv = mask << PMU_FIRST_COUNTER; buffer_is_full = 1; DBprintk((" sampling buffer full must notify bv=0x%lx\n", bv)); /* * we do not reload here, when context is blocking */ if (!CTX_OVFL_NOBLOCK(ctx)) goto no_reload; /* * here, we have a full buffer but we are in non-blocking mode * so we need to reload overflowed PMDs with sampling reset values * and restart right away. */ } /* FALL THROUGH */ }reload_pmds: /* * in the case of a non-blocking context, we reload * with the ovfl_rval when no user notification is taking place (short recovery) * otherwise when the buffer is full which requires user interaction) then we use * smpl_rval which is the long_recovery path (disturbance introduce by user execution). * * XXX: implies that when buffer is full then there is always notification. */ ovfl_has_long_recovery = CTX_OVFL_NOBLOCK(ctx) && buffer_is_full; /* * XXX: CTX_HAS_SMPL() should really be something like CTX_HAS_SMPL() and is activated,i.e., * one of the PMC is configured for EAR/BTB. * * When sampling, we can only notify when the sampling buffer is full. */ can_notify = CTX_HAS_SMPL(ctx) == 0 && ctx->ctx_notify_task; DBprintk((" ovfl_has_long_recovery=%d can_notify=%d\n", ovfl_has_long_recovery, can_notify)); for (i = 0, cnum = PMU_FIRST_COUNTER; mask ; cnum++, i++, mask >>= 1) { if ((mask & 0x1) == 0) continue; DBprintk((" PMD[%ld] overflowed pmd=0x%lx pmod.val=0x%lx\n", cnum, ia64_get_pmd(cnum), ctx->ctx_pmds[i].val)); /* * Because we sometimes (EARS/BTB) reset to a specific value, we cannot simply use * val to count the number of times we overflowed. Otherwise we would loose the current value * in the PMD (which can be >0). So to make sure we don't loose * the residual counts we set val to contain full 64bits value of the counter. * * XXX: is this needed for EARS/BTB ? */ ctx->ctx_pmds[i].val += 1 + pmu_conf.perf_ovfl_val + (ia64_get_pmd(cnum) & pmu_conf.perf_ovfl_val); /* slow */ DBprintk((" pmod[%ld].val=0x%lx pmd=0x%lx\n", i, ctx->ctx_pmds[i].val, ia64_get_pmd(cnum)&pmu_conf.perf_ovfl_val)); if (can_notify && PMD_OVFL_NOTIFY(ctx, i)) { DBprintk((" CPU%d should notify task %p with signal %d\n", my_cpu, ctx->ctx_notify_task, ctx->ctx_notify_sig)); bv |= 1 << i; } else { DBprintk((" CPU%d PMD[%ld] overflow, no notification\n", my_cpu, cnum)); /* * In case no notification is requested, we reload the reset value right away * otherwise we wait until the notify_pid process has been called and has * has finished processing data. Check out pfm_overflow_notify() */ /* writes to upper part are ignored, so this is safe */ if (ovfl_has_long_recovery) { DBprintk((" CPU%d PMD[%ld] reload with smpl_val=%lx\n", my_cpu, cnum,ctx->ctx_pmds[i].smpl_rval)); ia64_set_pmd(cnum, ctx->ctx_pmds[i].smpl_rval); } else { DBprintk((" CPU%d PMD[%ld] reload with ovfl_val=%lx\n", my_cpu, cnum,ctx->ctx_pmds[i].smpl_rval)); ia64_set_pmd(cnum, ctx->ctx_pmds[i].ovfl_rval); } } if (cnum == ctx->ctx_btb_counter) need_reset_pmd16=1; } /* * In case of BTB overflow we need to reset the BTB index. */ if (need_reset_pmd16) { DBprintk(("reset PMD16\n")); ia64_set_pmd(16, 0); }no_reload: /* * some counters overflowed, but they did not require * user notification, so after having reloaded them above * we simply restart */ if (!bv) return 0x0; ctx->ctx_ovfl_regs = bv; /* keep track of what to reset when unblocking */ /* * Now we know that: * - we have some counters which overflowed (contains in bv) * - someone has asked to be notified on overflow. */ /* * If the notification task is still present, then notify_task is non * null. It is clean by that task if it ever exits before we do. */ if (ctx->ctx_notify_task) { si.si_errno = 0; si.si_addr = NULL; si.si_pid = task->pid; /* who is sending */ si.si_signo = ctx->ctx_notify_sig; /* is SIGPROF */ si.si_code = PROF_OVFL; /* goes to user */ si.si_pfm_ovfl = bv; /* * when the target of the signal is not ourself, we have to be more * careful. The notify_task may being cleared by the target task itself * in release_thread(). We must ensure mutual exclusion here such that * the signal is delivered (even to a dying task) safely. */ if (ctx->ctx_notify_task != current) { /* * grab the notification lock for this task */ spin_lock(&ctx->ctx_notify_lock); /* * now notify_task cannot be modified until we're done * if NULL, they it got modified while we were in the handler */ if (ctx->ctx_notify_task == NULL) { spin_unlock(&ctx->ctx_notify_lock); goto lost_notify; } /* * required by send_sig_info() to make sure the target * task does not disappear on us. */ read_lock(&tasklist_lock); } /* * in this case, we don't stop the task, we let it go on. It will * necessarily go to the signal handler (if any) when it goes back to * user mode. */ DBprintk((" %d sending %d notification to %d\n", task->pid, si.si_signo, ctx->ctx_notify_task->pid)); /* * this call is safe in an interrupt handler, so does read_lock() on tasklist_lock */ ret = send_sig_info(ctx->ctx_notify_sig, &si, ctx->ctx_notify_task); if (ret != 0) printk(" send_sig_info(process %d, SIGPROF)=%d\n", ctx->ctx_notify_task->pid, ret); /* * now undo the protections in order */ if (ctx->ctx_notify_task != current) { read_unlock(&tasklist_lock); spin_unlock(&ctx->ctx_notify_lock); } /* * if we block set the pfm_must_block bit * when in block mode, we can effectively block only when the notified * task is not self, otherwise we would deadlock. * in this configuration, the notification is sent, the task will not * block on the way back to user mode, but the PMU will be kept frozen * until PFM_RESTART. * Note that here there is still a race condition with notify_task * possibly being nullified behind our back, but this is fine because * it can only be changed to NULL which by construction, can only be * done when notify_task != current. So if it was already different * before, changing it to NULL will still maintain this invariant. * Of course, when it is equal to current it cannot change at this point. */ if (!CTX_OVFL_NOBLOCK(ctx) && ctx->ctx_notify_task != current) { th->pfm_must_block = 1; /* will cause blocking */ } } else {lost_notify: DBprintk((" notification task has disappeared !\n")); /* * for a non-blocking context, we make sure we do not fall into the pfm_overflow_notify() * trap. Also in the case of a blocking context with lost notify process, then we do not * want to block either (even though it is interruptible). In this case, the PMU will be kept * frozen and the process will run to completion without monitoring enabled. * * Of course, we cannot loose notify process when self-monitoring. */ th->pfm_must_block = 0; } /* * if we block, we keep the PMU frozen. If non-blocking we restart. * in the case of non-blocking were the notify process is lost, we also * restart. */ if (!CTX_OVFL_NOBLOCK(ctx)) ctx->ctx_fl_frozen = 1; else ctx->ctx_fl_frozen = 0; DBprintk((" reload pmc0=0x%x must_block=%ld\n", ctx->ctx_fl_frozen ? 0x1 : 0x0, th->pfm_must_block)); return ctx->ctx_fl_frozen ? 0x1 : 0x0;}static voidperfmon_interrupt (int irq, void *arg, struct pt_regs *regs){ u64 pmc0; struct task_struct *ta; pmc0 = ia64_get_pmc(0); /* slow */ /* * if we have some pending bits set * assumes : if any PM[0].bit[63-1] is set, then PMC[0].fr = 1 */ if ((pmc0 & ~0x1) && (ta=PMU_OWNER())) { /* assumes, PMC[0].fr = 1 at this point */ pmc0 = update_counters(ta, pmc0, regs); /* * if pmu_frozen = 0 * pmc0 = 0 and we resume monitoring right away * else * pmc0 = 0x1 frozen but all pending bits are cleared */ ia64_set_pmc(0, pmc0); ia64_srlz_d(); } else { printk("perfmon: Spurious PMU overflow interrupt: pmc0=0x%lx owner=%p\n", pmc0, (void *)PMU_OWNER()); }}/* for debug only */static intperfmon_proc_info(char *page){ char *p = page; u64 pmc0 = ia64_get_pmc(0); int i; p += sprintf(p, "CPU%d.pmc[0]=%lx\nPerfmon debug: %s\n", smp_processor_id(), pmc0, pfm_debug ? "On" : "Off"); p += sprintf(p, "proc_sessions=%lu sys_sessions=%lu\n", pfs_info.pfs_proc_sessions, pfs_info.pfs_sys_session); for(i=0; i < NR_CPUS; i++) { if (cpu_is_online(i)) { p += sprintf(p, "CPU%d.pmu_owner: %-6d\n", i, pmu_owners[i].owner ? pmu_owners[i].owner->pid: -1); } } return p - page;}/* for debug only */static intperfmon_read_entry(char *page, char **start, off_t off, int count, int *eof, void *data){ int len = perfmon_proc_info(page); if (len <= off+count) *eof = 1; *start = page + off; len -= off; if (len>count) len = count; if (len<0) len = 0; return len;}static struct irqaction perfmon_irqaction = { handler: perfmon_interrupt, flags: SA_INTERRUPT, name: "perfmon"};void __initperfmon_init (void){ pal_perf_mon_info_u_t pm_info; s64 status; register_percpu_irq(IA64_PERFMON_VECTOR, &perfmon_irqaction); ia64_set_pmv(IA64_PERFMON_VECTOR); ia64_srlz_d(); pmu_conf.pfm_is_disabled = 1; printk("perfmon: version %s (sampling format v%d)\n", PFM_VERSION, PFM_SMPL_HDR_VERSION); printk("perfmon: Interrupt vectored to %u\n", IA64_PERFMON_VECTOR); if ((status=ia64_pal_perf_mon_info(pmu_conf.impl_regs, &pm_info)) != 0) { printk("perfmon: PAL call failed (%ld)\n", status); return; } pmu_conf.perf_ovfl_val = (1L << pm_info.pal_perf_mon_info_s.width) - 1; pmu_conf.max_counters = pm_info.pal_perf_mon_info_s.generic; pmu_conf.num_pmcs = find_num_pm_regs(pmu_conf.impl_regs); pmu_conf.num_pmds = find_num_pm_regs(&pmu_conf.impl_regs[4]); printk("perfmon: %d bits counters (max value 0x%lx)\n", pm_info.pal_perf_mon_info_s.width, pmu_conf.perf_ovfl_val); printk("perfmon: %ld PMC/PMD pairs, %ld PMCs, %ld PMDs\n", pmu_conf.max_counters, pmu_conf.num_pmcs, pmu_conf.num_pmds); /* sanity check */ if (pmu_conf.num_pmds >= IA64_NUM_PMD_REGS || pmu_conf.num_pmcs >= IA64_NUM_PMC_REGS) { printk(KERN_ERR "perfmon: ERROR not enough PMC/PMD storage in kernel, perfmon is DISABLED\n"); return; /* no need to continue anyway */ } /* we are all set */ pmu_conf.pfm_is_disabled = 0; /* * Insert the tasklet in the list.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -