📄 perfmon.c
字号:
/* * Code to configure and utilize the ppc64 pmc hardware. Generally, the * following functions are supported: * 1. Kernel profile based on decrementer ticks * 2. Kernel profile based on PMC execution cycles * 3. Trace based on arbitrary PMC counter * 4. Timeslice data capture of arbitrary collections of PMCs * * Copyright (C) 2002 David Engebretsen <engebret@us.ibm.com> */#include <asm/proc_fs.h>#include <asm/paca.h>#include <asm/iSeries/ItLpPaca.h>#include <asm/iSeries/ItLpQueue.h>#include <asm/processor.h>#include <linux/proc_fs.h>#include <linux/spinlock.h>#include <asm/pmc.h>#include <asm/uaccess.h>#include <asm/naca.h>#include <asm/perfmon.h>#include <asm/iSeries/HvCall.h>#include <asm/hvcall.h>extern char _stext[], _etext[], _end[];struct perfmon_base_struct perfmon_base = {0, 0, 0, 0, 0, 0, 0, PMC_STATE_INITIAL};int alloc_perf_buffer(int size);int free_perf_buffer(void);int clear_buffers(void);void pmc_stop(void *data);void pmc_clear(void *data);void pmc_start(void *data);void pmc_touch_bolted(void *data);void dump_pmc_struct(struct perfmon_struct *perfdata);void dump_hardware_pmc_struct(void *perfdata);int decr_profile(void *data);int pmc_profile(struct perfmon_struct *perfdata);int pmc_set_general(struct perfmon_struct *perfdata);int pmc_set_user_general(struct perfmon_struct *perfdata);void pmc_configure(void *data);int pmc_timeslice_enable(struct perfmon_struct *perfdata);int pmc_timeslice_disable(struct perfmon_struct *perfdata);int pmc_timeslice_set(struct perfmon_struct *perfdata);void pmc_dump_timeslice(struct perfmon_struct *perfdata);void pmc_trace_rec_type(unsigned long type);void pmc_timeslice_data_collect(void *data);int pmc_timeslice_tick(void);int perfmon_buffer_ctl(void *data);int perfmon_dump_ctl(void *data);int perfmon_profile_ctl(void *data);int perfmon_trace_ctl(void *data);int perfmon_timeslice_ctl(void *data);#define PMC_MAX_CPUS 48#define PMC_MAX_COUNTERS 8#define PMC_SLICES_STAR 64#define PMC_SLICES_GP 28#define PMC_SLICES_MAX 64#define PMC_TICK_FACTOR 10int pmc_timeslice_enabled = 0, pmc_timeslice_top = 0;int pmc_tick_count[PMC_MAX_CPUS], pmc_timeslice[PMC_MAX_CPUS];unsigned long pmc_timeslice_data[PMC_SLICES_MAX*PMC_MAX_COUNTERS*PMC_MAX_CPUS];/* * DRENG: todo: * add api to add config entries (entry, values), and bump pmc_timeslice_top * value * add api to get data from kernel (entry, values) */unsigned long pmc_timeslice_config[PMC_SLICES_MAX * 5];static spinlock_t pmc_lock = SPIN_LOCK_UNLOCKED;asmlinkage intsys_perfmonctl (int cmd, void *data) { struct perfmon_struct *pdata; int err; printk("sys_perfmonctl: cmd = 0x%x\n", cmd); pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER); err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct)); switch(cmd) { case PMC_CMD_BUFFER: perfmon_buffer_ctl(data); break; case PMC_CMD_DUMP: perfmon_dump_ctl(data); break; case PMC_CMD_DECR_PROFILE: /* NIA time sampling */ decr_profile(data); break; case PMC_CMD_PROFILE: perfmon_profile_ctl(pdata); break; case PMC_CMD_TRACE: perfmon_trace_ctl(pdata); break; case PMC_CMD_TIMESLICE: perfmon_timeslice_ctl(pdata); break;#if 0 case PMC_OP_TIMESLICE: pmc_enable_timeslice(pdata); break; case PMC_OP_DUMP_TIMESLICE: pmc_dump_timeslice(pdata); smp_call_function(pmc_dump_timeslice, (void *)pdata, 0, 1); break;#endif default: printk("Perfmon: Unknown command\n"); break; } kfree(pdata); return 0;}int perfmon_buffer_ctl(void *data) { struct perfmon_struct *pdata; int err; pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER); err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct)); switch(pdata->header.subcmd) { case PMC_SUBCMD_BUFFER_ALLOC: alloc_perf_buffer(0); break; case PMC_SUBCMD_BUFFER_FREE: free_perf_buffer(); break; case PMC_SUBCMD_BUFFER_CLEAR: clear_buffers(); break; default: return(-1); } return(0); }int alloc_perf_buffer(int size) { int i; printk("Perfmon: allocate buffer\n"); if(perfmon_base.state == PMC_STATE_INITIAL) { perfmon_base.profile_length = (((unsigned long) &_etext - (unsigned long) &_stext) >> 2) * sizeof(int); perfmon_base.profile_buffer = (unsigned long)btmalloc(perfmon_base.profile_length); perfmon_base.trace_length = 1024*1024*16; perfmon_base.trace_buffer = (unsigned long)btmalloc(perfmon_base.trace_length); perfmon_base.timeslice_length = PMC_SLICES_MAX*PMC_MAX_COUNTERS*PMC_MAX_CPUS; perfmon_base.timeslice_buffer = (unsigned long)pmc_timeslice_data; if(perfmon_base.profile_buffer && perfmon_base.trace_buffer) { memset((char *)perfmon_base.profile_buffer, 0, perfmon_base.profile_length); printk("Profile buffer created at address 0x%lx of length 0x%lx\n", perfmon_base.profile_buffer, perfmon_base.profile_length); } else { printk("Profile buffer creation failed\n"); return 0; } /* Fault in the first bolted segment - it then remains in the stab for all time */ pmc_touch_bolted(NULL); smp_call_function(pmc_touch_bolted, (void *)NULL, 0, 1); for (i=0; i<MAX_PACAS; ++i) { paca[i].prof_shift = 2; paca[i].prof_len = perfmon_base.profile_length; paca[i].prof_buffer = (unsigned *)(perfmon_base.profile_buffer); paca[i].prof_stext = (unsigned *)&_stext; paca[i].prof_etext = (unsigned *)&_etext; mb(); } perfmon_base.state = PMC_STATE_READY; } return 0;}int free_perf_buffer() { printk("Perfmon: free buffer\n"); if(perfmon_base.state == PMC_STATE_INITIAL) { printk("Perfmon: free buffer failed - no buffer was allocated.\n"); return -1; } btfree((void *)perfmon_base.profile_buffer); btfree((void *)perfmon_base.trace_buffer); perfmon_base.profile_length = 0; perfmon_base.profile_buffer = 0; perfmon_base.trace_buffer = 0; perfmon_base.trace_length = 0; perfmon_base.trace_end = 0; perfmon_base.state = PMC_STATE_INITIAL; return(0); }int clear_buffers() { int i, j; if(perfmon_base.state == PMC_STATE_INITIAL) { printk("Perfmon: clear buffer failed - no buffer was allocated.\n"); return -1; } printk("Perfmon: clear buffer\n"); /* Stop counters on all [PMC_MAX_CPUS]processors -- blocking */ pmc_stop(NULL); smp_call_function(pmc_stop, (void *)NULL, 0, 1); /* Clear the buffers */ memset((char *)perfmon_base.profile_buffer, 0, perfmon_base.profile_length); memset((char *)perfmon_base.trace_buffer, 0, perfmon_base.trace_length); memset((char *)perfmon_base.timeslice_buffer, 0, perfmon_base.timeslice_length); /* Reset the trace buffer point */ perfmon_base.trace_end = 0; for (i=0; i<MAX_PACAS; ++i) { for(j=0; j<8; j++) { paca[i].pmcc[j] = 0; } }#if 0 /* Reset the timeslice data */ for(i=0; i<(PMC_MAX_CPUS*PMC_MAX_COUNTERS*PMC_SLICES_MAX); i++) { pmc_timeslice_data[i] = 0; }#endif /* Restart counters on all processors -- blocking */ pmc_start(NULL); smp_call_function(pmc_start, (void *)NULL, 0, 1); return(0); }void pmc_stop(void *data) { /* Freeze all counters, leave everything else alone */ mtspr(MMCR0, mfspr( MMCR0 ) | 0x80000000);}void pmc_clear(void *data) { mtspr(PMC1, 0); mtspr(PMC2, 0); mtspr(PMC3, 0); mtspr(PMC4, 0); mtspr(PMC5, 0); mtspr(PMC6, 0); mtspr(PMC7, 0); mtspr(PMC8, 0); mtspr(MMCR0, 0); mtspr(MMCR1, 0); mtspr(MMCRA, 0);}void pmc_start(void *data) { /* Free all counters, leave everything else alone */ mtspr(MMCR0, mfspr( MMCR0 ) & 0x7fffffff);}void pmc_touch_bolted(void *data) { volatile int touch; /* Hack to fault the buffer into the segment table */ touch = *((int *)(perfmon_base.profile_buffer));}int perfmon_dump_ctl(void *data) { struct perfmon_struct *pdata; int err; pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER); err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct)); switch(pdata->header.subcmd) { case PMC_SUBCMD_DUMP_COUNTERS: dump_pmc_struct(pdata); // copy_to_user(data, pdata, sizeof(struct perfmon_struct)); break; case PMC_SUBCMD_DUMP_HARDWARE: dump_hardware_pmc_struct(pdata); smp_call_function(dump_hardware_pmc_struct, (void *)pdata, 0, 1); break; } return(0); }void dump_pmc_struct(struct perfmon_struct *perfdata) { unsigned int cpu = perfdata->vdata.pmc_info.cpu, i; if(cpu > MAX_PACAS) return; printk("PMC Control Mode: 0x%lx\n", perfmon_base.state); printk("PMC[1 - 2] = 0x%16.16lx 0x%16.16lx\n", paca[cpu].pmcc[0], paca[cpu].pmcc[1]); printk("PMC[3 - 4] = 0x%16.16lx 0x%16.16lx\n", paca[cpu].pmcc[2], paca[cpu].pmcc[3]); printk("PMC[5 - 6] = 0x%16.16lx 0x%16.16lx\n", paca[cpu].pmcc[4], paca[cpu].pmcc[5]); printk("PMC[7 - 8] = 0x%16.16lx 0x%16.16lx\n", paca[cpu].pmcc[6], paca[cpu].pmcc[7]); perfdata->vdata.pmc_info.mode = perfmon_base.state; for(i = 0; i < 11; i++) perfdata->vdata.pmc_info.pmc_base[i] = paca[cpu].pmc[i]; for(i = 0; i < 8; i++) perfdata->vdata.pmc_info.pmc_cumulative[i] = paca[cpu].pmcc[i];}void dump_hardware_pmc_struct(void *perfdata) { unsigned int cpu = smp_processor_id(); printk("PMC[%2.2d][1 - 4] = 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", cpu, (u32) mfspr(PMC1),(u32) mfspr(PMC2),(u32) mfspr(PMC3),(u32) mfspr(PMC4)); printk("PMC[%2.2d][5 - 8] = 0x%8.8x 0x%8.8x 0x%8.8x 0x%8.8x\n", cpu, (u32) mfspr(PMC5),(u32) mfspr(PMC6),(u32) mfspr(PMC7),(u32) mfspr(PMC8)); printk("MMCR[%2.2d][0,1,A] = 0x%8.8x 0x%8.8x 0x%8.8x\n", cpu, (u32) mfspr(MMCR0),(u32) mfspr(MMCR1),(u32) mfspr(MMCRA));}int decr_profile(void *data){ int i; printk("Perfmon: NIA decrementer profile\n"); if(perfmon_base.state == PMC_STATE_INITIAL) { printk("Perfmon: failed - no buffer was allocated.\n"); return -1; } /* Stop counters on all processors -- blocking */ pmc_stop(NULL); smp_call_function(pmc_stop, (void *)NULL, 0, 1); for (i=0; i<MAX_PACAS; ++i) { paca[i].prof_mode = PMC_STATE_DECR_PROFILE; } perfmon_base.state = PMC_STATE_DECR_PROFILE; mb(); return 0;}int perfmon_profile_ctl(void *data) { struct perfmon_struct *pdata; int err; pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER); err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct)); switch(pdata->header.subcmd) { case PMC_SUBCMD_PROFILE_CYCLE: pmc_profile(pdata); break; default: return(-1); } return(0); }int perfmon_trace_ctl(void *data) { struct perfmon_struct *pdata; int err; pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER); err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct)); pmc_set_general(pdata); #if 0 /* Reimplement at some point ... */ pmc_set_user_general(pdata); #endif return(0); }int perfmon_timeslice_ctl(void *data) { struct perfmon_struct *pdata; int err; pdata = kmalloc(sizeof(struct perfmon_struct), GFP_USER); err = __copy_from_user(pdata, data, sizeof(struct perfmon_struct)); switch(pdata->header.subcmd) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -