📄 perfmon.c
字号:
/* * This file implements the perfmon subsystem which is used * to program the IA-64 Performance Monitoring Unit (PMU). * * Originaly Written by Ganesh Venkitachalam, IBM Corp. * Copyright (C) 1999 Ganesh Venkitachalam <venkitac@us.ibm.com> * * Modifications by Stephane Eranian, Hewlett-Packard Co. * Modifications by David Mosberger-Tang, Hewlett-Packard Co. * * Copyright (C) 1999-2003 Hewlett Packard Co * Stephane Eranian <eranian@hpl.hp.com> * David Mosberger-Tang <davidm@hpl.hp.com> */#include <linux/config.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/smp_lock.h>#include <linux/proc_fs.h>#include <linux/init.h>#include <linux/vmalloc.h>#include <linux/wrapper.h>#include <linux/mm.h>#include <linux/sysctl.h>#include <asm/bitops.h>#include <asm/errno.h>#include <asm/page.h>#include <asm/perfmon.h>#include <asm/processor.h>#include <asm/signal.h>#include <asm/system.h>#include <asm/uaccess.h>#include <asm/delay.h> /* for ia64_get_itc() */#ifdef CONFIG_PERFMON/* * For PMUs which rely on the debug registers for some features, you must * you must enable the following flag to activate the support for * accessing the registers via the perfmonctl() interface. */#if defined(CONFIG_ITANIUM) || defined(CONFIG_MCKINLEY)#define PFM_PMU_USES_DBR 1#endif/* * perfmon context states */#define PFM_CTX_DISABLED 0#define PFM_CTX_ENABLED 1/* * Reset register flags */#define PFM_PMD_LONG_RESET 1#define PFM_PMD_SHORT_RESET 2/* * Misc macros and definitions */#define PMU_FIRST_COUNTER 4#define PMU_MAX_PMCS 256#define PMU_MAX_PMDS 256/* * type of a PMU register (bitmask). * bitmask structure: * bit0 : register implemented * bit1 : end marker * bit2-3 : reserved * bit4-7 : register type * bit8-31: reserved */#define PFM_REG_IMPL 0x1 /* register implemented */#define PFM_REG_END 0x2 /* end marker */#define PFM_REG_MONITOR (0x1<<4|PFM_REG_IMPL) /* a PMC with a pmc.pm field only */#define PFM_REG_COUNTING (0x2<<4|PFM_REG_IMPL) /* a PMC with a pmc.pm AND pmc.oi, a PMD used as a counter */#define PFM_REG_CONTROL (0x3<<4|PFM_REG_IMPL) /* PMU control register */#define PFM_REG_CONFIG (0x4<<4|PFM_REG_IMPL) /* refine configuration */#define PFM_REG_BUFFER (0x5<<4|PFM_REG_IMPL) /* PMD used as buffer */#define PMC_IS_LAST(i) (pmu_conf.pmc_desc[i].type & PFM_REG_END)#define PMD_IS_LAST(i) (pmu_conf.pmd_desc[i].type & PFM_REG_END)#define PFM_IS_DISABLED() pmu_conf.disabled#define PMC_OVFL_NOTIFY(ctx, i) ((ctx)->ctx_soft_pmds[i].flags & PFM_REGFL_OVFL_NOTIFY)#define PFM_FL_INHERIT_MASK (PFM_FL_INHERIT_NONE|PFM_FL_INHERIT_ONCE|PFM_FL_INHERIT_ALL)/* i assume unsigned */#define PMC_IS_IMPL(i) (i< PMU_MAX_PMCS && (pmu_conf.pmc_desc[i].type & PFM_REG_IMPL))#define PMD_IS_IMPL(i) (i< PMU_MAX_PMDS && (pmu_conf.pmd_desc[i].type & PFM_REG_IMPL))/* XXX: these three assume that register i is implemented */#define PMD_IS_COUNTING(i) (pmu_conf.pmd_desc[i].type == PFM_REG_COUNTING)#define PMC_IS_COUNTING(i) (pmu_conf.pmc_desc[i].type == PFM_REG_COUNTING)#define PMC_IS_MONITOR(i) (pmu_conf.pmc_desc[i].type == PFM_REG_MONITOR)#define PMC_DFL_VAL(i) pmu_conf.pmc_desc[i].default_value#define PMC_RSVD_MASK(i) pmu_conf.pmc_desc[i].reserved_mask#define PMD_PMD_DEP(i) pmu_conf.pmd_desc[i].dep_pmd[0]#define PMC_PMD_DEP(i) pmu_conf.pmc_desc[i].dep_pmd[0]/* k assume unsigned */#define IBR_IS_IMPL(k) (k<pmu_conf.num_ibrs)#define DBR_IS_IMPL(k) (k<pmu_conf.num_dbrs)#define CTX_IS_ENABLED(c) ((c)->ctx_flags.state == PFM_CTX_ENABLED)#define CTX_OVFL_NOBLOCK(c) ((c)->ctx_fl_block == 0)#define CTX_INHERIT_MODE(c) ((c)->ctx_fl_inherit)#define CTX_HAS_SMPL(c) ((c)->ctx_psb != NULL)/* XXX: does not support more than 64 PMDs */#define CTX_USED_PMD(ctx, mask) (ctx)->ctx_used_pmds[0] |= (mask)#define CTX_IS_USED_PMD(ctx, c) (((ctx)->ctx_used_pmds[0] & (1UL << (c))) != 0UL)#define CTX_USED_IBR(ctx,n) (ctx)->ctx_used_ibrs[(n)>>6] |= 1UL<< ((n) % 64)#define CTX_USED_DBR(ctx,n) (ctx)->ctx_used_dbrs[(n)>>6] |= 1UL<< ((n) % 64)#define CTX_USES_DBREGS(ctx) (((pfm_context_t *)(ctx))->ctx_fl_using_dbreg==1)#define LOCK_CTX(ctx) spin_lock(&(ctx)->ctx_lock)#define UNLOCK_CTX(ctx) spin_unlock(&(ctx)->ctx_lock)#define SET_PMU_OWNER(t) do { pmu_owners[smp_processor_id()].owner = (t); } while(0)#define PMU_OWNER() pmu_owners[smp_processor_id()].owner#define LOCK_PFS() spin_lock(&pfm_sessions.pfs_lock)#define UNLOCK_PFS() spin_unlock(&pfm_sessions.pfs_lock)#define PFM_REG_RETFLAG_SET(flags, val) do { flags &= ~PFM_REG_RETFL_MASK; flags |= (val); } while(0)#define PFM_CPUINFO_CLEAR(v) local_cpu_data->pfm_syst_info &= ~(v)#define PFM_CPUINFO_SET(v) local_cpu_data->pfm_syst_info |= (v)#ifdef CONFIG_SMP#define cpu_is_online(i) (cpu_online_map & (1UL << i))#else#define cpu_is_online(i) (i==0)#endif/* * debugging */#define DBprintk(a) \ do { \ if (pfm_sysctl.debug >0) { printk("%s.%d: CPU%d ", __FUNCTION__, __LINE__, smp_processor_id()); printk a; } \ } while (0)#define DBprintk_ovfl(a) \ do { \ if (pfm_sysctl.debug > 0 && pfm_sysctl.debug_ovfl >0) { printk("%s.%d: CPU%d ", __FUNCTION__, __LINE__, smp_processor_id()); printk a; } \ } while (0)/* * Architected PMC structure */typedef struct { unsigned long pmc_plm:4; /* privilege level mask */ unsigned long pmc_ev:1; /* external visibility */ unsigned long pmc_oi:1; /* overflow interrupt */ unsigned long pmc_pm:1; /* privileged monitor */ unsigned long pmc_ig1:1; /* reserved */ unsigned long pmc_es:8; /* event select */ unsigned long pmc_ig2:48; /* reserved */} pfm_monitor_t;/* * There is one such data structure per perfmon context. It is used to describe the * sampling buffer. It is to be shared among siblings whereas the pfm_context * is not. * Therefore we maintain a refcnt which is incremented on fork(). * This buffer is private to the kernel only the actual sampling buffer * including its header are exposed to the user. This construct allows us to * export the buffer read-write, if needed, without worrying about security * problems. */typedef struct _pfm_smpl_buffer_desc { spinlock_t psb_lock; /* protection lock */ unsigned long psb_refcnt; /* how many users for the buffer */ int psb_flags; /* bitvector of flags (not yet used) */ void *psb_addr; /* points to location of first entry */ unsigned long psb_entries; /* maximum number of entries */ unsigned long psb_size; /* aligned size of buffer */ unsigned long psb_index; /* next free entry slot XXX: must use the one in buffer */ unsigned long psb_entry_size; /* size of each entry including entry header */ perfmon_smpl_hdr_t *psb_hdr; /* points to sampling buffer header */ struct _pfm_smpl_buffer_desc *psb_next; /* next psb, used for rvfreeing of psb_hdr */} pfm_smpl_buffer_desc_t;/* * psb_flags */#define PSB_HAS_VMA 0x1 /* a virtual mapping for the buffer exists */#define LOCK_PSB(p) spin_lock(&(p)->psb_lock)#define UNLOCK_PSB(p) spin_unlock(&(p)->psb_lock)/* * 64-bit software counter structure */typedef struct { u64 val; /* virtual 64bit counter value */ u64 lval; /* last value */ u64 long_reset; /* reset value on sampling overflow */ u64 short_reset;/* reset value on overflow */ u64 reset_pmds[4]; /* which other pmds to reset when this counter overflows */ u64 seed; /* seed for random-number generator */ u64 mask; /* mask for random-number generator */ unsigned int flags; /* notify/do not notify */} pfm_counter_t;/* * perfmon context. One per process, is cloned on fork() depending on * inheritance flags */typedef struct { unsigned int state:1; /* 0=disabled, 1=enabled */ unsigned int inherit:2; /* inherit mode */ unsigned int block:1; /* when 1, task will blocked on user notifications */ unsigned int system:1; /* do system wide monitoring */ unsigned int frozen:1; /* pmu must be kept frozen on ctxsw in */ unsigned int protected:1; /* allow access to creator of context only */ unsigned int using_dbreg:1; /* using range restrictions (debug registers) */ unsigned int excl_idle:1; /* exclude idle task in system wide session */ unsigned int reserved:23;} pfm_context_flags_t;/* * perfmon context: encapsulates all the state of a monitoring session * XXX: probably need to change layout */typedef struct pfm_context { pfm_smpl_buffer_desc_t *ctx_psb; /* sampling buffer, if any */ unsigned long ctx_smpl_vaddr; /* user level virtual address of smpl buffer */ spinlock_t ctx_lock; pfm_context_flags_t ctx_flags; /* block/noblock */ struct task_struct *ctx_notify_task; /* who to notify on overflow */ struct task_struct *ctx_owner; /* pid of creator (debug) */ unsigned long ctx_ovfl_regs[4]; /* which registers overflowed (notification) */ unsigned long ctx_smpl_regs[4]; /* which registers to record on overflow */ struct semaphore ctx_restart_sem; /* use for blocking notification mode */ unsigned long ctx_used_pmds[4]; /* bitmask of PMD used */ unsigned long ctx_reload_pmds[4]; /* bitmask of PMD to reload on ctxsw */ unsigned long ctx_used_pmcs[4]; /* bitmask PMC used by context */ unsigned long ctx_reload_pmcs[4]; /* bitmask of PMC to reload on ctxsw */ unsigned long ctx_used_ibrs[4]; /* bitmask of used IBR (speedup ctxsw) */ unsigned long ctx_used_dbrs[4]; /* bitmask of used DBR (speedup ctxsw) */ pfm_counter_t ctx_soft_pmds[IA64_NUM_PMD_REGS]; /* XXX: size should be dynamic */ u64 ctx_saved_psr; /* copy of psr used for lazy ctxsw */ unsigned long ctx_saved_cpus_allowed; /* copy of the task cpus_allowed (system wide) */ unsigned int ctx_cpu; /* cpu to which perfmon is applied (system wide) */ atomic_t ctx_saving_in_progress; /* flag indicating actual save in progress */ atomic_t ctx_is_busy; /* context accessed by overflow handler */ atomic_t ctx_last_cpu; /* CPU id of current or last CPU used */} pfm_context_t;#define ctx_fl_inherit ctx_flags.inherit#define ctx_fl_block ctx_flags.block#define ctx_fl_system ctx_flags.system#define ctx_fl_frozen ctx_flags.frozen#define ctx_fl_protected ctx_flags.protected#define ctx_fl_using_dbreg ctx_flags.using_dbreg#define ctx_fl_excl_idle ctx_flags.excl_idle/* * global information about all sessions * mostly used to synchronize between system wide and per-process */typedef struct { spinlock_t pfs_lock; /* lock the structure */ unsigned int pfs_task_sessions; /* number of per task sessions */ unsigned int pfs_sys_sessions; /* number of per system wide sessions */ unsigned int pfs_sys_use_dbregs; /* incremented when a system wide session uses debug regs */ unsigned int pfs_ptrace_use_dbregs; /* incremented when a process uses debug regs */ struct task_struct *pfs_sys_session[NR_CPUS]; /* point to task owning a system-wide session */} pfm_session_t;/* * information about a PMC or PMD. * dep_pmd[]: a bitmask of dependent PMD registers * dep_pmc[]: a bitmask of dependent PMC registers */typedef struct { unsigned int type; int pm_pos; unsigned long default_value; /* power-on default value */ unsigned long reserved_mask; /* bitmask of reserved bits */ int (*read_check)(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs); int (*write_check)(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs); unsigned long dep_pmd[4]; unsigned long dep_pmc[4];} pfm_reg_desc_t;/* assume cnum is a valid monitor */#define PMC_PM(cnum, val) (((val) >> (pmu_conf.pmc_desc[cnum].pm_pos)) & 0x1)#define PMC_WR_FUNC(cnum) (pmu_conf.pmc_desc[cnum].write_check)#define PMD_WR_FUNC(cnum) (pmu_conf.pmd_desc[cnum].write_check)#define PMD_RD_FUNC(cnum) (pmu_conf.pmd_desc[cnum].read_check)/* * This structure is initialized at boot time and contains * a description of the PMU main characteristics. */typedef struct { unsigned int disabled; /* indicates if perfmon is working properly */ unsigned long ovfl_val; /* overflow value for generic counters */ unsigned long impl_pmcs[4]; /* bitmask of implemented PMCS */ unsigned long impl_pmds[4]; /* bitmask of implemented PMDS */ unsigned int num_pmcs; /* number of implemented PMCS */ unsigned int num_pmds; /* number of implemented PMDS */ unsigned int num_ibrs; /* number of implemented IBRS */ unsigned int num_dbrs; /* number of implemented DBRS */ unsigned int num_counters; /* number of PMD/PMC counters */ pfm_reg_desc_t *pmc_desc; /* detailed PMC register dependencies descriptions */ pfm_reg_desc_t *pmd_desc; /* detailed PMD register dependencies descriptions */} pmu_config_t;/* * structure used to pass argument to/from remote CPU * using IPI to check and possibly save the PMU context on SMP systems. * * not used in UP kernels */typedef struct { struct task_struct *task; /* which task we are interested in */ int retval; /* return value of the call: 0=you can proceed, 1=need to wait for completion */} pfm_smp_ipi_arg_t;/* * perfmon command descriptions */typedef struct { int (*cmd_func)(struct task_struct *task, pfm_context_t *ctx, void *arg, int count, struct pt_regs *regs); int cmd_flags; unsigned int cmd_narg; size_t cmd_argsize;} pfm_cmd_desc_t;#define PFM_CMD_PID 0x1 /* command requires pid argument */#define PFM_CMD_ARG_READ 0x2 /* command must read argument(s) */#define PFM_CMD_ARG_RW 0x4 /* command must read/write argument(s) */#define PFM_CMD_CTX 0x8 /* command needs a perfmon context */#define PFM_CMD_NOCHK 0x10 /* command does not need to check task's state */#define PFM_CMD_IDX(cmd) (cmd)#define PFM_CMD_IS_VALID(cmd) ((PFM_CMD_IDX(cmd) >= 0) && (PFM_CMD_IDX(cmd) < PFM_CMD_COUNT) \ && pfm_cmd_tab[PFM_CMD_IDX(cmd)].cmd_func != NULL)#define PFM_CMD_USE_PID(cmd) ((pfm_cmd_tab[PFM_CMD_IDX(cmd)].cmd_flags & PFM_CMD_PID) != 0)#define PFM_CMD_READ_ARG(cmd) ((pfm_cmd_tab[PFM_CMD_IDX(cmd)].cmd_flags & PFM_CMD_ARG_READ) != 0)#define PFM_CMD_RW_ARG(cmd) ((pfm_cmd_tab[PFM_CMD_IDX(cmd)].cmd_flags & PFM_CMD_ARG_RW) != 0)#define PFM_CMD_USE_CTX(cmd) ((pfm_cmd_tab[PFM_CMD_IDX(cmd)].cmd_flags & PFM_CMD_CTX) != 0)#define PFM_CMD_CHK(cmd) ((pfm_cmd_tab[PFM_CMD_IDX(cmd)].cmd_flags & PFM_CMD_NOCHK) == 0)#define PFM_CMD_ARG_MANY -1 /* cannot be zero */#define PFM_CMD_NARG(cmd) (pfm_cmd_tab[PFM_CMD_IDX(cmd)].cmd_narg)#define PFM_CMD_ARG_SIZE(cmd) (pfm_cmd_tab[PFM_CMD_IDX(cmd)].cmd_argsize)typedef struct { int debug; /* turn on/off debugging via syslog */ int debug_ovfl; /* turn on/off debug printk in overflow handler */ int fastctxsw; /* turn on/off fast (unsecure) ctxsw */} pfm_sysctl_t;typedef struct { unsigned long pfm_spurious_ovfl_intr_count; /* keep track of spurious ovfl interrupts */ unsigned long pfm_ovfl_intr_count; /* keep track of ovfl interrupts */ unsigned long pfm_recorded_samples_count; unsigned long pfm_full_smpl_buffer_count; /* how many times the sampling buffer was full */ char pad[SMP_CACHE_BYTES] ____cacheline_aligned;} pfm_stats_t;/* * perfmon internal variables */static pfm_session_t pfm_sessions; /* global sessions information */static struct proc_dir_entry *perfmon_dir; /* for debug only */static pfm_stats_t pfm_stats[NR_CPUS];static pfm_intr_handler_desc_t *pfm_alternate_intr_handler;/* sysctl() controls */static pfm_sysctl_t pfm_sysctl;static ctl_table pfm_ctl_table[]={ {1, "debug", &pfm_sysctl.debug, sizeof(int), 0666, NULL, &proc_dointvec, NULL,}, {2, "debug_ovfl", &pfm_sysctl.debug_ovfl, sizeof(int), 0666, NULL, &proc_dointvec, NULL,}, {3, "fastctxsw", &pfm_sysctl.fastctxsw, sizeof(int), 0600, NULL, &proc_dointvec, NULL,}, { 0, },};static ctl_table pfm_sysctl_dir[] = { {1, "perfmon", NULL, 0, 0755, pfm_ctl_table, }, {0,},};static ctl_table pfm_sysctl_root[] = { {1, "kernel", NULL, 0, 0755, pfm_sysctl_dir, }, {0,},};static struct ctl_table_header *pfm_sysctl_header;static void pfm_vm_close(struct vm_area_struct * area);static struct vm_operations_struct pfm_vm_ops={ .close = pfm_vm_close};/* * keep track of task owning the PMU per CPU. */static struct { struct task_struct *owner; char pad[SMP_CACHE_BYTES] ____cacheline_aligned;} pmu_owners[NR_CPUS];/* * forward declarations */static void pfm_reset_pmu(struct task_struct *);#ifdef CONFIG_SMPstatic void pfm_fetch_regs(int cpu, struct task_struct *task, pfm_context_t *ctx);#endifstatic void pfm_lazy_save_regs (struct task_struct *ta);#if defined(CONFIG_ITANIUM)#include "perfmon_itanium.h"#elif defined(CONFIG_MCKINLEY)#include "perfmon_mckinley.h"#else#include "perfmon_generic.h"#endifstatic inline voidpfm_clear_psr_pp(void){ __asm__ __volatile__ ("rsm psr.pp;; srlz.i;;"::: "memory");}static inline voidpfm_set_psr_pp(void){ __asm__ __volatile__ ("ssm psr.pp;; srlz.i;;"::: "memory");}static inline voidpfm_clear_psr_up(void){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -