📄 perfmon.c
字号:
/* * 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 + -