⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 perfmon.c

📁 上传linux-jx2410的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 *	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 + -