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

📄 perfmon.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * This file contains the code to configure and read/write the ia64 performance * monitoring stuff. * * Originaly Written by Ganesh Venkitachalam, IBM Corp. * Modifications by David Mosberger-Tang, Hewlett-Packard Co. * Modifications by Stephane Eranian, Hewlett-Packard Co. * Copyright (C) 1999 Ganesh Venkitachalam <venkitac@us.ibm.com> * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com> * Copyright (C) 2000 Stephane Eranian <eranian@hpl.hp.com> */#include <linux/config.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/smp_lock.h>#include <linux/proc_fs.h>#include <asm/errno.h>#include <asm/hw_irq.h>#include <asm/processor.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/pal.h>/* Long blurb on how this works:  * We set dcr.pp, psr.pp, and the appropriate pmc control values with * this.  Notice that we go about modifying _each_ task's pt_regs to * set cr_ipsr.pp.  This will start counting when "current" does an * _rfi_. Also, since each task's cr_ipsr.pp, and cr_ipsr is inherited * across forks, we do _not_ need additional code on context * switches. On stopping of the counters we dont need to go about * changing every task's cr_ipsr back to where it wuz, because we can * just set pmc[0]=1. But we do it anyways becuase we will probably * add thread specific accounting later. * * The obvious problem with this is that on SMP systems, it is a bit * of work (when someone wants to do it:-)) - it would be easier if we * just added code to the context-switch path, but if we wanted to support * per-thread accounting, the context-switch path might be long unless  * we introduce a flag in the task_struct. Right now, the following code  * will NOT work correctly on MP (for more than one reason:-)). * * The short answer is that to make this work on SMP,  we would need  * to lock the run queue to ensure no context switches, send  * an IPI to each processor, and in that IPI handler, set processor regs, * and just modify the psr bit of only the _current_ thread, since we have  * modified the psr bit correctly in the kernel stack for every process  * which is not running. Also, we need pmd arrays per-processor, and  * the READ_PMD command will need to get values off of other processors.  * IPIs are the answer, irrespective of what the question is. Might  * crash on SMP systems without the lock_kernel(). */#ifdef CONFIG_PERFMON#define MAX_PERF_COUNTER	4	/* true for Itanium, at least */#define PMU_FIRST_COUNTER	4	/* first generic counter */#define PFM_WRITE_PMCS		0xa0#define PFM_WRITE_PMDS		0xa1#define PFM_READ_PMDS		0xa2#define PFM_STOP		0xa3#define PFM_START		0xa4#define PFM_ENABLE		0xa5	/* unfreeze only */#define PFM_DISABLE		0xa6	/* freeze only *//*  * Those 2 are just meant for debugging. I considered using sysctl() for * that but it is a little bit too pervasive. This solution is at least * self-contained. */#define PFM_DEBUG_ON		0xe0	#define PFM_DEBUG_OFF		0xe1#ifdef CONFIG_SMP#define cpu_is_online(i) (cpu_online_map & (1UL << i))#else#define cpu_is_online(i)	1#endif#define PMC_IS_IMPL(i)		(pmu_conf.impl_regs[i>>6] & (1<< (i&~(64-1))))#define PMD_IS_IMPL(i)  	(pmu_conf.impl_regs[4+(i>>6)] & (1<< (i&~(64-1))))#define PMD_IS_COUNTER(i)	(i>=PMU_FIRST_COUNTER && i < (PMU_FIRST_COUNTER+pmu_conf.max_counters))#define PMC_IS_COUNTER(i)	(i>=PMU_FIRST_COUNTER && i < (PMU_FIRST_COUNTER+pmu_conf.max_counters))/* * this structure needs to be enhanced */typedef struct {	unsigned long	pfr_reg_num;	/* which register */	unsigned long	pfr_reg_value;	/* configuration (PMC) or initial value (PMD) */	unsigned long	pfr_reg_reset;	/* reset value on overflow (PMD) */	void		*pfr_smpl_buf;	/* pointer to user buffer for EAR/BTB */	unsigned long	pfr_smpl_size;	/* size of user buffer for EAR/BTB */	pid_t		pfr_notify_pid;	/* process to notify */	int		pfr_notify_sig;	/* signal for notification, 0=no notification */} perfmon_req_t;#if 0typedef struct {	unsigned long pmu_reg_data;	/* generic PMD register */	unsigned long pmu_reg_num;	/* which register number */} perfmon_reg_t; #endif/* * This structure is initialize at boot time and contains * a description of the PMU main characteristic as indicated * by PAL */typedef struct {	unsigned long perf_ovfl_val;	/* overflow value for generic counters   */	unsigned long max_counters;	/* upper limit on counter pair (PMC/PMD) */	unsigned long impl_regs[16];	/* buffer used to hold implememted PMC/PMD mask */} pmu_config_t;static pmu_config_t pmu_conf;/* for debug only */static unsigned long pfm_debug=1;	/* 0= nodebug, >0= debug output on */#define DBprintk(a)	{\	if (pfm_debug >0) { printk a; } \}/* * could optimize to avoid cache line conflicts in SMP */static struct task_struct *pmu_owners[NR_CPUS];static intdo_perfmonctl (struct task_struct *task, int cmd, int flags, perfmon_req_t *req, int count, struct pt_regs *regs){        perfmon_req_t tmp;        int i;        switch (cmd) {		case PFM_WRITE_PMCS:          			/* we don't quite support this right now */			if (task != current) return -EINVAL;			if (!access_ok(VERIFY_READ, req, sizeof(struct perfmon_req_t)*count)) return -EFAULT;			for (i = 0; i < count; i++, req++) {				copy_from_user(&tmp, req, sizeof(tmp));				/* XXX needs to check validity of the data maybe */				if (!PMC_IS_IMPL(tmp.pfr_reg_num)) {					DBprintk((__FUNCTION__ " invalid pmc[%ld]\n", tmp.pfr_reg_num));					return -EINVAL;				}				/* XXX: for counters, need to some checks */				if (PMC_IS_COUNTER(tmp.pfr_reg_num)) {					current->thread.pmu_counters[tmp.pfr_reg_num - PMU_FIRST_COUNTER].sig = tmp.pfr_notify_sig;					current->thread.pmu_counters[tmp.pfr_reg_num - PMU_FIRST_COUNTER].pid = tmp.pfr_notify_pid;					DBprintk((__FUNCTION__" setting PMC[%ld] send sig %d to %d\n",tmp.pfr_reg_num, tmp.pfr_notify_sig, tmp.pfr_notify_pid));				}				ia64_set_pmc(tmp.pfr_reg_num, tmp.pfr_reg_value);				DBprintk((__FUNCTION__" setting PMC[%ld]=0x%lx\n", tmp.pfr_reg_num, tmp.pfr_reg_value));			}			/*			 * we have to set this here event hough we haven't necessarily started monitoring			 * because we may be context switched out			 */			current->thread.flags |= IA64_THREAD_PM_VALID;                	break;		case PFM_WRITE_PMDS:			/* we don't quite support this right now */			if (task != current) return -EINVAL;			if (!access_ok(VERIFY_READ, req, sizeof(struct perfmon_req_t)*count)) return -EFAULT;			for (i = 0; i < count; i++, req++) {				copy_from_user(&tmp, req, sizeof(tmp));				if (!PMD_IS_IMPL(tmp.pfr_reg_num)) return -EINVAL;				/* update virtualized (64bits) counter */				if (PMD_IS_COUNTER(tmp.pfr_reg_num)) {					current->thread.pmu_counters[tmp.pfr_reg_num - PMU_FIRST_COUNTER].val  = tmp.pfr_reg_value & ~pmu_conf.perf_ovfl_val;					current->thread.pmu_counters[tmp.pfr_reg_num - PMU_FIRST_COUNTER].rval = tmp.pfr_reg_reset;				}				/* writes to unimplemented part is ignored, so this is safe */				ia64_set_pmd(tmp.pfr_reg_num, tmp.pfr_reg_value);				/* to go away */				ia64_srlz_d();				DBprintk((__FUNCTION__" setting PMD[%ld]:  pmod.val=0x%lx pmd=0x%lx rval=0x%lx\n", tmp.pfr_reg_num, current->thread.pmu_counters[tmp.pfr_reg_num - PMU_FIRST_COUNTER].val, ia64_get_pmd(tmp.pfr_reg_num),current->thread.pmu_counters[tmp.pfr_reg_num - PMU_FIRST_COUNTER].rval));			}			/*			 * we have to set this here event hough we haven't necessarily started monitoring			 * because we may be context switched out			 */			current->thread.flags |= IA64_THREAD_PM_VALID;                	break;		case PFM_START:			/* we don't quite support this right now */			if (task != current) return -EINVAL;			pmu_owners[smp_processor_id()] = current;			/* will start monitoring right after rfi */			ia64_psr(regs)->up = 1;			/* 		 	 * mark the state as valid.		 	 * this will trigger save/restore at context switch		 	 */			current->thread.flags |= IA64_THREAD_PM_VALID;			ia64_set_pmc(0, 0);                	break;		case PFM_ENABLE:			/* we don't quite support this right now */			if (task != current) return -EINVAL;			pmu_owners[smp_processor_id()] = current;			/* 		 	 * mark the state as valid.		 	 * this will trigger save/restore at context switch		 	 */			current->thread.flags |= IA64_THREAD_PM_VALID;			/* simply unfreeze */			ia64_set_pmc(0, 0);			break;		case PFM_DISABLE:			/* we don't quite support this right now */			if (task != current) return -EINVAL;			/* simply unfreeze */			ia64_set_pmc(0, 1);			ia64_srlz_d();			break;	        case PFM_READ_PMDS:			if (!access_ok(VERIFY_READ, req, sizeof(struct perfmon_req_t)*count)) return -EFAULT;			if (!access_ok(VERIFY_WRITE, req, sizeof(struct perfmon_req_t)*count)) return -EFAULT;		/* This looks shady, but IMHO this will work fine. This is  		 * the sequence that I could come up with to avoid races		 * with the interrupt handler. See explanation in the 		 * following comment.		 */#if 0/* irrelevant with user monitors */		local_irq_save(flags);		__asm__ __volatile__("rsm psr.pp\n");		dcr = ia64_get_dcr();		dcr &= ~IA64_DCR_PP;		ia64_set_dcr(dcr);		local_irq_restore(flags);#endif		/*		 * We cannot write to pmc[0] to stop counting here, as		 * that particular instruction might cause an overflow		 * and the mask in pmc[0] might get lost. I'm _not_ 		 * sure of the hardware behavior here. So we stop		 * counting by psr.pp = 0. And we reset dcr.pp to		 * prevent an interrupt from mucking up psr.pp in the		 * meanwhile. Perfmon interrupts are pended, hence the		 * above code should be ok if one of the above instructions 		 * caused overflows, i.e the interrupt should get serviced		 * when we re-enabled interrupts. When I muck with dcr, 		 * is the irq_save/restore needed?		 */		for (i = 0; i < count; i++, req++) {			unsigned long val=0;			copy_from_user(&tmp, req, sizeof(tmp));			if (!PMD_IS_IMPL(tmp.pfr_reg_num)) return -EINVAL;			if (PMD_IS_COUNTER(tmp.pfr_reg_num)) {				if (task == current){					val = ia64_get_pmd(tmp.pfr_reg_num) & pmu_conf.perf_ovfl_val;				} else {					val = task->thread.pmd[tmp.pfr_reg_num - PMU_FIRST_COUNTER] & pmu_conf.perf_ovfl_val;				}				val += task->thread.pmu_counters[tmp.pfr_reg_num - PMU_FIRST_COUNTER].val;			} else {				/* for now */				if (task != current) return -EINVAL;				val = ia64_get_pmd(tmp.pfr_reg_num);			}			tmp.pfr_reg_value = val;DBprintk((__FUNCTION__" reading PMD[%ld]=0x%lx\n", tmp.pfr_reg_num, val));			if (copy_to_user(req, &tmp, sizeof(tmp))) return -EFAULT;		}#if 0/* irrelevant with user monitors */		local_irq_save(flags);		__asm__ __volatile__("ssm psr.pp");		dcr = ia64_get_dcr();		dcr |= IA64_DCR_PP;		ia64_set_dcr(dcr);		local_irq_restore(flags);#endif                break;	      case PFM_STOP:		/* we don't quite support this right now */		if (task != current) return -EINVAL;		ia64_set_pmc(0, 1);		ia64_srlz_d();		ia64_psr(regs)->up = 0;		current->thread.flags &= ~IA64_THREAD_PM_VALID;		pmu_owners[smp_processor_id()] = NULL;#if 0/* irrelevant with user monitors */		local_irq_save(flags);		dcr = ia64_get_dcr();		dcr &= ~IA64_DCR_PP;		ia64_set_dcr(dcr);		local_irq_restore(flags);		ia64_psr(regs)->up = 0;#endif		break;	      case PFM_DEBUG_ON:			printk(__FUNCTION__" debuggin on\n");			pfm_debug = 1;			break;	      case PFM_DEBUG_OFF:			printk(__FUNCTION__" debuggin off\n");			pfm_debug = 0;			break;

⌨️ 快捷键说明

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