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

📄 perfmon.c

📁 上传linux-jx2410的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * Allocates the sampling buffer and remaps it into caller's address space */static intpfm_smpl_buffer_alloc(pfm_context_t *ctx, unsigned long which_pmds, unsigned long entries, void **user_addr){	struct mm_struct *mm = current->mm;	struct vm_area_struct *vma;	unsigned long addr, size, regcount;	void *smpl_buf;	pfm_smpl_buffer_desc_t *psb;	regcount = pfm_smpl_entry_size(&which_pmds, 1);	/* note that regcount might be 0, in this case only the header for each	 * entry will be recorded.	 */	/*	 * 1 buffer hdr and for each entry a header + regcount PMDs to save	 */	size = PAGE_ALIGN(  sizeof(perfmon_smpl_hdr_t)			  + entries * (sizeof(perfmon_smpl_entry_t) + regcount*sizeof(u64)));	/*	 * check requested size to avoid Denial-of-service attacks	 * XXX: may have to refine this test	 */	if (size > current->rlim[RLIMIT_MEMLOCK].rlim_cur) return -EAGAIN;	/* find some free area in address space */	addr = get_unmapped_area(NULL, 0, size, 0, MAP_PRIVATE);	if (!addr) goto no_addr;	DBprintk((" entries=%ld aligned size=%ld, unmapped @0x%lx\n", entries, size, addr));	/* allocate vma */	vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);	if (!vma) goto no_vma;	/* XXX: see rvmalloc() for page alignment problem */	smpl_buf = rvmalloc(size);	if (smpl_buf == NULL) goto no_buffer;	DBprintk((" smpl_buf @%p\n", smpl_buf));	if (pfm_remap_buffer((unsigned long)smpl_buf, addr, size)) goto cant_remap;	/* allocate sampling buffer descriptor now */	psb = vmalloc(sizeof(*psb));	if (psb == NULL) goto no_buffer_desc;	/* start with something clean */	memset(smpl_buf, 0x0, size);	psb->psb_hdr	 = smpl_buf;	psb->psb_addr    = (char *)smpl_buf+sizeof(perfmon_smpl_hdr_t); /* first entry */	psb->psb_size    = size; /* aligned size */	psb->psb_index   = 0;	psb->psb_entries = entries;	atomic_set(&psb->psb_refcnt, 1);	psb->psb_entry_size = sizeof(perfmon_smpl_entry_t) + regcount*sizeof(u64);	DBprintk((" psb @%p entry_size=%ld hdr=%p addr=%p\n", (void *)psb,psb->psb_entry_size, (void *)psb->psb_hdr, (void *)psb->psb_addr));	/* initialize some of the fields of header */	psb->psb_hdr->hdr_version    = PFM_SMPL_HDR_VERSION;	psb->psb_hdr->hdr_entry_size = sizeof(perfmon_smpl_entry_t)+regcount*sizeof(u64);	psb->psb_hdr->hdr_pmds	     = which_pmds;	/* store which PMDS to record */	ctx->ctx_smpl_regs = which_pmds;	/* link to perfmon context */	ctx->ctx_smpl_buf  = psb;	/*	 * initialize the vma for the sampling buffer	 */	vma->vm_mm	  = mm;	vma->vm_start	  = addr;	vma->vm_end	  = addr + size;	vma->vm_flags	  = VM_READ|VM_MAYREAD;	vma->vm_page_prot = PAGE_READONLY; /* XXX may need to change */	vma->vm_ops	  = NULL;	vma->vm_pgoff	  = 0;	vma->vm_file	  = NULL;	vma->vm_raend	  = 0;	vma->vm_private_data = ctx;	/* link to pfm_context(not yet used) */	/*	 * now insert the vma in the vm list for the process	 */	insert_vm_struct(mm, vma);	mm->total_vm  += size >> PAGE_SHIFT;	/*	 * that's the address returned to the user	 */	*user_addr = (void *)addr;	return 0;	/* outlined error handling */no_addr:	DBprintk(("Cannot find unmapped area for size %ld\n", size));	return -ENOMEM;no_vma:	DBprintk(("Cannot allocate vma\n"));	return -ENOMEM;cant_remap:	DBprintk(("Can't remap buffer\n"));	rvfree(smpl_buf, size);no_buffer:	DBprintk(("Can't allocate sampling buffer\n"));	kmem_cache_free(vm_area_cachep, vma);	return -ENOMEM;no_buffer_desc:	DBprintk(("Can't allocate sampling buffer descriptor\n"));	kmem_cache_free(vm_area_cachep, vma);	rvfree(smpl_buf, size);	return -ENOMEM;}static intpfx_is_sane(pfreq_context_t *pfx){	int ctx_flags;	/* valid signal */	//if (pfx->notify_sig < 1 || pfx->notify_sig >= _NSIG) return -EINVAL;	if (pfx->notify_sig !=0 && pfx->notify_sig != SIGPROF) return -EINVAL;	/* cannot send to process 1, 0 means do not notify */	if (pfx->notify_pid < 0 || pfx->notify_pid == 1) return -EINVAL;	ctx_flags = pfx->flags;	if (ctx_flags & PFM_FL_SYSTEM_WIDE) {#ifdef CONFIG_SMP		if (smp_num_cpus > 1) {			printk("perfmon: system wide monitoring on SMP not yet supported\n");			return -EINVAL;		}#endif		if ((ctx_flags & PFM_FL_SMPL_OVFL_NOBLOCK) == 0) {			printk("perfmon: system wide monitoring cannot use blocking notification mode\n");			return -EINVAL;		}	}	/* probably more to add here */	return 0;}static intpfm_context_create(int flags, perfmon_req_t *req){	pfm_context_t *ctx;	struct task_struct *task = NULL;	perfmon_req_t tmp;	void *uaddr = NULL;	int ret;	int ctx_flags;	pid_t pid;	/* to go away */	if (flags) {		printk("perfmon: use context flags instead of perfmon() flags. Obsoleted API\n");	}	if (copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT;	ret = pfx_is_sane(&tmp.pfr_ctx);	if (ret < 0) return ret;	ctx_flags = tmp.pfr_ctx.flags;	if (ctx_flags & PFM_FL_SYSTEM_WIDE) {		/*		 * XXX: This is not AT ALL SMP safe		 */		if (pfs_info.pfs_proc_sessions > 0) return -EBUSY;		if (pfs_info.pfs_sys_session > 0) return -EBUSY;		pfs_info.pfs_sys_session = 1;	} else if (pfs_info.pfs_sys_session >0) {		/* no per-process monitoring while there is a system wide session */		return -EBUSY;	} else		pfs_info.pfs_proc_sessions++;	ctx = pfm_context_alloc();	if (!ctx) goto error;	/* record the creator (debug only) */	ctx->ctx_creator = current;	pid = tmp.pfr_ctx.notify_pid;	spin_lock_init(&ctx->ctx_notify_lock);	if (pid == current->pid) {		ctx->ctx_notify_task = task = current;		current->thread.pfm_context = ctx;		atomic_set(&current->thread.pfm_notifiers_check, 1);	} else if (pid!=0) {		read_lock(&tasklist_lock);		task = find_task_by_pid(pid);		if (task) {			/*		 	 * record who to notify		 	 */			ctx->ctx_notify_task = task;			/* 		 	 * make visible		 	 * must be done inside critical section		 	 *		 	 * if the initialization does not go through it is still		 	 * okay because child will do the scan for nothing which		 	 * won't hurt.		 	 */			current->thread.pfm_context = ctx;			/*			 * will cause task to check on exit for monitored			 * processes that would notify it. see release_thread()			 * Note: the scan MUST be done in release thread, once the			 * task has been detached from the tasklist otherwise you are			 * exposed to race conditions.			 */			atomic_add(1, &task->thread.pfm_notifiers_check);		}		read_unlock(&tasklist_lock);	}	/*	 * notification process does not exist	 */	if (pid != 0 && task == NULL) {		ret = -EINVAL;		goto buffer_error;	}	ctx->ctx_notify_sig = SIGPROF;	/* siginfo imposes a fixed signal */	if (tmp.pfr_ctx.smpl_entries) {		DBprintk((" sampling entries=%ld\n",tmp.pfr_ctx.smpl_entries));		ret = pfm_smpl_buffer_alloc(ctx, tmp.pfr_ctx.smpl_regs, 						 tmp.pfr_ctx.smpl_entries, &uaddr);		if (ret<0) goto buffer_error;		tmp.pfr_ctx.smpl_vaddr = uaddr;	}	/* initialization of context's flags */	ctx->ctx_fl_inherit  = ctx_flags & PFM_FL_INHERIT_MASK;	ctx->ctx_fl_noblock  = (ctx_flags & PFM_FL_SMPL_OVFL_NOBLOCK) ? 1 : 0;	ctx->ctx_fl_system   = (ctx_flags & PFM_FL_SYSTEM_WIDE) ? 1: 0;	ctx->ctx_fl_exclintr = (ctx_flags & PFM_FL_EXCL_INTR) ? 1: 0;	ctx->ctx_fl_frozen   = 0;	/* 	 * Keep track of the pmds we want to sample	 * XXX: may be we don't need to save/restore the DEAR/IEAR pmds	 * but we do need the BTB for sure. This is because of a hardware	 * buffer of 1 only for non-BTB pmds.	 */	ctx->ctx_used_pmds[0] = tmp.pfr_ctx.smpl_regs;	ctx->ctx_used_pmcs[0] = 1; /* always save/restore PMC[0] */	sema_init(&ctx->ctx_restart_sem, 0); /* init this semaphore to locked */	if (copy_to_user(req, &tmp, sizeof(tmp))) {		ret = -EFAULT;		goto buffer_error;	}	DBprintk((" context=%p, pid=%d notify_sig %d notify_task=%p\n",(void *)ctx, current->pid, ctx->ctx_notify_sig, ctx->ctx_notify_task));	DBprintk((" context=%p, pid=%d flags=0x%x inherit=%d noblock=%d system=%d\n",(void *)ctx, current->pid, ctx_flags, ctx->ctx_fl_inherit, ctx->ctx_fl_noblock, ctx->ctx_fl_system));	/*	 * when no notification is required, we can make this visible at the last moment	 */	if (pid == 0) current->thread.pfm_context = ctx;	/*	 * by default, we always include interrupts for system wide	 * DCR.pp is set by default to zero by kernel  in cpu_init()	 */	if (ctx->ctx_fl_system) {		if (ctx->ctx_fl_exclintr == 0) {			unsigned long dcr = ia64_get_dcr();			ia64_set_dcr(dcr|IA64_DCR_PP);			/*			* keep track of the kernel default value			 */			pfs_info.pfs_dfl_dcr = dcr;			DBprintk((" dcr.pp is set\n"));		}	} 	return 0;buffer_error:	pfm_context_free(ctx);error:	/*	 * undo session reservation	 */	if (ctx_flags & PFM_FL_SYSTEM_WIDE) {		pfs_info.pfs_sys_session = 0;	} else {		pfs_info.pfs_proc_sessions--;	}	return ret;}static voidpfm_reset_regs(pfm_context_t *ctx){	unsigned long mask = ctx->ctx_ovfl_regs;	int i, cnum;	DBprintk((" ovfl_regs=0x%lx\n", mask));	/*	 * now restore reset value on sampling overflowed counters	 */	for(i=0, cnum=PMU_FIRST_COUNTER; i < pmu_conf.max_counters; i++, cnum++, mask >>= 1) {		if (mask & 0x1) {			DBprintk((" reseting PMD[%d]=%lx\n", cnum, ctx->ctx_pmds[i].smpl_rval & pmu_conf.perf_ovfl_val));			/* upper part is ignored on rval */			ia64_set_pmd(cnum, ctx->ctx_pmds[i].smpl_rval);			/*			 * we must reset BTB index (clears pmd16.full to make			 * sure we do not report the same branches twice.			 * The non-blocking case in handled in update_counters()			 */			if (cnum == ctx->ctx_btb_counter) {				DBprintk(("reseting PMD16\n"));				ia64_set_pmd(16, 0);			}		}	}	/* just in case ! */	ctx->ctx_ovfl_regs = 0;}static intpfm_write_pmcs(struct task_struct *ta, perfmon_req_t *req, int count){	struct thread_struct *th = &ta->thread;	pfm_context_t *ctx = th->pfm_context;	perfmon_req_t tmp;	unsigned long cnum;	int i;	/* XXX: ctx locking may be required here */	for (i = 0; i < count; i++, req++) {		if (copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT;		cnum = tmp.pfr_reg.reg_num;		/* XXX needs to check validity of the data maybe */		if (!PMC_IS_IMPL(cnum)) {			DBprintk((" invalid pmc[%ld]\n", cnum));			return -EINVAL;		}		if (PMC_IS_COUNTER(cnum)) {			/*			 * we keep track of EARS/BTB to speed up sampling later			 */			if (PMC_IS_DEAR(&tmp.pfr_reg.reg_value)) {				ctx->ctx_dear_counter = cnum;			} else if (PMC_IS_IEAR(&tmp.pfr_reg.reg_value)) {				ctx->ctx_iear_counter = cnum;			} else if (PMC_IS_BTB(&tmp.pfr_reg.reg_value)) {				ctx->ctx_btb_counter = cnum;			}#if 0			if (tmp.pfr_reg.reg_flags & PFM_REGFL_OVFL_NOTIFY)				ctx->ctx_pmds[cnum - PMU_FIRST_COUNTER].flags |= PFM_REGFL_OVFL_NOTIFY;#endif		}		/* keep track of what we use */		CTX_USED_PMC(ctx, cnum);		ia64_set_pmc(cnum, tmp.pfr_reg.reg_value);		DBprintk((" setting PMC[%ld]=0x%lx flags=0x%x used_pmcs=0%lx\n", cnum, tmp.pfr_reg.reg_value, ctx->ctx_pmds[cnum - PMU_FIRST_COUNTER].flags, ctx->ctx_used_pmcs[0]));	}	/*	 * we have to set this here event hough we haven't necessarily started monitoring	 * because we may be context switched out	 */	if (ctx->ctx_fl_system==0) th->flags |= IA64_THREAD_PM_VALID;	return 0;}static intpfm_write_pmds(struct task_struct *ta, perfmon_req_t *req, int count){	struct thread_struct *th = &ta->thread;	pfm_context_t *ctx = th->pfm_context;	perfmon_req_t tmp;	unsigned long cnum;	int i;	/* XXX: ctx locking may be required here */	for (i = 0; i < count; i++, req++) {		int k;		if (copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT;		cnum = tmp.pfr_reg.reg_num;		k = cnum - PMU_FIRST_COUNTER;		if (!PMD_IS_IMPL(cnum)) return -EINVAL;		/* update virtualized (64bits) counter */		if (PMD_IS_COUNTER(cnum)) {			ctx->ctx_pmds[k].ival = tmp.pfr_reg.reg_value;			ctx->ctx_pmds[k].val  = tmp.pfr_reg.reg_value & ~pmu_conf.perf_ovfl_val;			ctx->ctx_pmds[k].smpl_rval = tmp.pfr_reg.reg_smpl_reset;			ctx->ctx_pmds[k].ovfl_rval = tmp.pfr_reg.reg_ovfl_reset;			if (tmp.pfr_reg.reg_flags & PFM_REGFL_OVFL_NOTIFY)				ctx->ctx_pmds[cnum - PMU_FIRST_COUNTER].flags |= PFM_REGFL_OVFL_NOTIFY;		}		/* keep track of what we use */		CTX_USED_PMD(ctx, cnum);		/* writes to unimplemented part is ignored, so this is safe */		ia64_set_pmd(cnum, tmp.pfr_reg.reg_value);		/* to go away */		ia64_srlz_d();		DBprintk((" setting PMD[%ld]:  ovfl_notify=%d pmd.val=0x%lx pmd.ovfl_rval=0x%lx pmd.smpl_rval=0x%lx pmd=%lx used_pmds=0%lx\n",					cnum,					PMD_OVFL_NOTIFY(ctx, cnum - PMU_FIRST_COUNTER),					ctx->ctx_pmds[k].val,					ctx->ctx_pmds[k].ovfl_rval,					ctx->ctx_pmds[k].smpl_rval,					ia64_get_pmd(cnum) & pmu_conf.perf_ovfl_val,					ctx->ctx_used_pmds[0]));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -