/* * proc_pmc.c * Copyright (C) 2001 Mike Corrigan & Dave Engebretsen IBM Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Change Activity: * 2001 : mikec : Created * 2001/06/05 : engebret : Software event count support. * 2001/08/03 : trautman : Added PCI Flight Recorder * End Change Activity */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* pci Flight Recorder AHT */ extern void proc_pciFr_init(struct proc_dir_entry *proc_ppc64_root); static int proc_pmc_control_mode = 0; struct proc_dir_entry *proc_ppc64_root = NULL; static struct proc_dir_entry *proc_ppc64_pmc_root = NULL; static struct proc_dir_entry *proc_ppc64_pmc_system_root = NULL; static struct proc_dir_entry *proc_ppc64_pmc_cpu_root[NR_CPUS] = {NULL, }; spinlock_t proc_ppc64_lock; static int proc_ppc64_page_read(char *page, char **start, off_t off, int count, int *eof, void *data); static void proc_ppc64_create_paca(int num, struct proc_dir_entry *paca_dir); void proc_ppc64_create_smt(void); int proc_ppc64_pmc_find_file(void *data); int proc_ppc64_pmc_read(char *page, char **start, off_t off, int count, int *eof, char *buffer); int proc_ppc64_pmc_stab_read(char *page, char **start, off_t off, int count, int *eof, void *data); int proc_ppc64_pmc_htab_read(char *page, char **start, off_t off, int count, int *eof, void *data); int proc_ppc64_pmc_profile_read(char *page, char **start, off_t off, int count, int *eof, void *data); int proc_ppc64_pmc_profile_read(char *page, char **start, off_t off, int count, int *eof, void *data); int proc_ppc64_pmc_hw_read(char *page, char **start, off_t off, int count, int *eof, void *data); static struct proc_dir_entry *pmc_proc_root = NULL; int proc_get_lpevents( char *page, char **start, off_t off, int count, int *eof, void *data); int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data); int proc_get_titanTod( char *page, char **start, off_t off, int count, int *eof, void *data); int proc_pmc_get_control( char *page, char **start, off_t off, int count, int *eof, void *data); int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data); int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data); static loff_t nacamap_seek( struct file *file, loff_t off, int whence); static ssize_t nacamap_read( struct file *file, char *buf, size_t nbytes, loff_t *ppos); static int nacamap_mmap( struct file *file, struct vm_area_struct *vma ); static struct file_operations nacamap_fops = { llseek: nacamap_seek, read: nacamap_read, mmap: nacamap_mmap }; static ssize_t read_profile(struct file *file, char *buf, size_t count, loff_t *ppos); static ssize_t write_profile(struct file * file, const char * buf, size_t count, loff_t *ppos); static ssize_t read_trace(struct file *file, char *buf, size_t count, loff_t *ppos); static ssize_t write_trace(struct file * file, const char * buf, size_t count, loff_t *ppos); static ssize_t read_timeslice(struct file *file, char *buf, size_t count, loff_t *ppos); static ssize_t write_timeslice(struct file * file, const char * buf, size_t count, loff_t *ppos); static struct file_operations proc_profile_operations = { read: read_profile, write: write_profile, }; static struct file_operations proc_trace_operations = { read: read_trace, write: write_trace, }; static struct file_operations proc_timeslice_operations = { read: read_timeslice, write: write_timeslice, }; extern struct perfmon_base_struct perfmon_base; void proc_ppc64_init(void) { unsigned long i; struct proc_dir_entry *ent = NULL; char buf[256]; printk("proc_ppc64: Creating /proc/ppc64/pmc\n"); /* * Create the root, system, and cpu directories as follows: * /proc/ppc64/pmc/system * /proc/ppc64/pmc/cpu0 */ spin_lock(&proc_ppc64_lock); if (proc_ppc64_root == NULL) { proc_ppc64_root = proc_mkdir("ppc64", 0); if (!proc_ppc64_root) { spin_unlock(&proc_ppc64_lock); return; } } spin_unlock(&proc_ppc64_lock); ent = create_proc_entry("naca", S_IFREG|S_IRUGO, proc_ppc64_root); if ( ent ) { ent->nlink = 1; ent->data = naca; ent->size = 4096; ent->proc_fops = &nacamap_fops; } ent = create_proc_entry("systemcfg", S_IFREG|S_IRUGO, proc_ppc64_root); if ( ent ) { ent->nlink = 1; ent->data = systemcfg; ent->size = 4096; ent->proc_fops = &nacamap_fops; } /* /proc/ppc64/paca/XX -- raw paca contents. Only readable to root */ ent = proc_mkdir("paca", proc_ppc64_root); if (ent) { for (i = 0; i < systemcfg->processorCount; i++) proc_ppc64_create_paca(i, ent); } /* Placeholder for rtas interfaces. */ if (rtas_proc_dir == NULL) { rtas_proc_dir = proc_mkdir("rtas", proc_ppc64_root); } proc_ppc64_create_smt(); /* Create the /proc/ppc64/pcifr for the Pci Flight Recorder. */ proc_pciFr_init(proc_ppc64_root); proc_ppc64_pmc_root = proc_mkdir("pmc", proc_ppc64_root); proc_ppc64_pmc_system_root = proc_mkdir("system", proc_ppc64_pmc_root); for (i = 0; i < systemcfg->processorCount; i++) { sprintf(buf, "cpu%ld", i); proc_ppc64_pmc_cpu_root[i] = proc_mkdir(buf, proc_ppc64_pmc_root); } /* Create directories for the software counters. */ for (i = 0; i < systemcfg->processorCount; i++) { ent = create_proc_entry("stab", S_IRUGO | S_IWUSR, proc_ppc64_pmc_cpu_root[i]); if (ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; ent->read_proc = (void *)proc_ppc64_pmc_stab_read; ent->write_proc = NULL; } ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, proc_ppc64_pmc_cpu_root[i]); if (ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; ent->read_proc = (void *)proc_ppc64_pmc_htab_read; ent->write_proc = NULL; } } ent = create_proc_entry("stab", S_IRUGO | S_IWUSR, proc_ppc64_pmc_system_root); if (ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_system_root; ent->read_proc = (void *)proc_ppc64_pmc_stab_read; ent->write_proc = NULL; } ent = create_proc_entry("htab", S_IRUGO | S_IWUSR, proc_ppc64_pmc_system_root); if (ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_system_root; ent->read_proc = (void *)proc_ppc64_pmc_htab_read; ent->write_proc = NULL; } ent = create_proc_entry("profile", S_IWUSR | S_IRUGO, proc_ppc64_pmc_system_root); if (ent) { ent->nlink = 1; ent->proc_fops = &proc_profile_operations; /* ent->size = (1+prof_len) * sizeof(unsigned int); */ } ent = create_proc_entry("trace", S_IWUSR | S_IRUGO, proc_ppc64_pmc_system_root); if (ent) { ent->nlink = 1; ent->proc_fops = &proc_trace_operations; /* ent->size = (1+prof_len) * sizeof(unsigned int); */ } ent = create_proc_entry("timeslice", S_IWUSR | S_IRUGO, proc_ppc64_pmc_system_root); if (ent) { ent->nlink = 1; ent->proc_fops = &proc_timeslice_operations; } /* Create directories for the hardware counters. */ for (i = 0; i < systemcfg->processorCount; i++) { ent = create_proc_entry("hardware", S_IRUGO | S_IWUSR, proc_ppc64_pmc_cpu_root[i]); if (ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_cpu_root[i]; ent->read_proc = (void *)proc_ppc64_pmc_hw_read; ent->write_proc = NULL; } } ent = create_proc_entry("hardware", S_IRUGO | S_IWUSR, proc_ppc64_pmc_system_root); if (ent) { ent->nlink = 1; ent->data = (void *)proc_ppc64_pmc_system_root; ent->read_proc = (void *)proc_ppc64_pmc_hw_read; ent->write_proc = NULL; } } /* Read a page of raw data. "data" points to the start addr. * Intended as a proc read function. */ static int proc_ppc64_page_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = PAGE_SIZE - off; char *p = (char *)data; if (len > count) len = count; if (len <= 0) return 0; /* Rely on a "hack" in fs/proc/generic.c. * If we could return a ptr to our own data this would be * trivial (currently *start must be either an offset, or * point into the given page). */ memcpy(page, p+off, len); *start = (char *)len; return len; } /* NOTE: since paca data is always in flux the values will never be a consistant set. * In theory it could be made consistent if we made the corresponding cpu * copy the page for us (via an IPI). Probably not worth it. * */ static void proc_ppc64_create_paca(int num, struct proc_dir_entry *paca_dir) { struct proc_dir_entry *ent; struct paca_struct *lpaca = paca + num; char buf[16]; sprintf(buf, "%02x", num); ent = create_proc_read_entry(buf, S_IRUSR, paca_dir, proc_ppc64_page_read, lpaca); } /* * Find the requested 'file' given a proc token. * * Inputs: void * data: proc token * Output: int : (0, ..., +N) = CPU number. * -1 = System. */ int proc_ppc64_pmc_find_file(void *data) { int i; if ((unsigned long)data == (unsigned long) proc_ppc64_pmc_system_root) { return(-1); } else { for (i = 0; i < systemcfg->processorCount; i++) { if ((unsigned long)data == (unsigned long)proc_ppc64_pmc_cpu_root[i]) { return(i); } } } /* On error, just default to a type of system. */ printk("proc_ppc64_pmc_find_file: failed to find file token.\n"); return(-1); } int proc_ppc64_pmc_read(char *page, char **start, off_t off, int count, int *eof, char *buffer) { int buffer_size, n; if (count < 0) return 0; if (buffer == NULL) { *eof = 1; return 0; } /* Check for read beyond EOF */ buffer_size = n = strlen(buffer); if (off >= buffer_size) { *eof = 1; return 0; } if (n > (buffer_size - off)) n = buffer_size - off; /* Never return more than was requested */ if (n > count) { n = count; } else { *eof = 1; } memcpy(page, buffer + off, n); *start = page; return n; } int proc_ppc64_pmc_stab_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int n, file; char *buffer = NULL; if (count < 0) return 0; spin_lock(&proc_ppc64_lock); /* Figure out which file is being request. */ file = proc_ppc64_pmc_find_file(data); /* Update the counters and the text buffer representation. */ buffer = ppc64_pmc_stab(file); /* Put the data into the requestor's buffer. */ n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); spin_unlock(&proc_ppc64_lock); return n; } int proc_ppc64_pmc_htab_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int n, file; char *buffer = NULL; if (count < 0) return 0; spin_lock(&proc_ppc64_lock); /* Figure out which file is being request. */ file = proc_ppc64_pmc_find_file(data); /* Update the counters and the text buffer representation. */ buffer = ppc64_pmc_htab(file); /* Put the data into the requestor's buffer. */ n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); spin_unlock(&proc_ppc64_lock); return n; } static ssize_t read_profile(struct file *file, char *buf, size_t count, loff_t *ppos) { unsigned long p = *ppos; ssize_t read; char * pnt; unsigned int sample_step = 4; if (p >= (perfmon_base.profile_length+1)) return 0; if (count > (perfmon_base.profile_length+1) - p) count = (perfmon_base.profile_length+1) - p; read = 0; while (p < sizeof(unsigned int) && count > 0) { put_user(*((char *)(&sample_step)+p),buf); buf++; p++; count--; read++; } pnt = (char *)(perfmon_base.profile_buffer) + p - sizeof(unsigned int); copy_to_user(buf,(void *)pnt,count); p += count; read += count; *ppos = p; return read; } static ssize_t write_profile(struct file * file, const char * buf, size_t count, loff_t *ppos) { return(0); } static ssize_t read_trace(struct file *file, char *buf, size_t count, loff_t *ppos) { unsigned long p = *ppos; char * pnt; if (p >= (perfmon_base.trace_length)) return 0; if (count > (perfmon_base.trace_length) - p) count = (perfmon_base.trace_length) - p; pnt = (char *)(perfmon_base.trace_buffer) + p; copy_to_user(buf,(void *)pnt,count); p += count; *ppos = p; return count; } static ssize_t write_trace(struct file * file, const char * buf, size_t count, loff_t *ppos) { return(0); } static ssize_t read_timeslice(struct file *file, char *buf, size_t count, loff_t *ppos) { unsigned long p = *ppos; ssize_t read; char * pnt; if (p >= (perfmon_base.timeslice_length)) return 0; if (count > (perfmon_base.timeslice_length) - p) count = (perfmon_base.timeslice_length) - p; pnt = (char *)(perfmon_base.timeslice_buffer) + p; copy_to_user(buf,(void *)pnt,count); *ppos = p + count; return count; } static ssize_t write_timeslice(struct file * file, const char * buf, size_t count, loff_t *ppos) { return(0); } int proc_ppc64_pmc_hw_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int n, file; char *buffer = NULL; if (count < 0) return 0; spin_lock(&proc_ppc64_lock); /* Figure out which file is being request. */ file = proc_ppc64_pmc_find_file(data); /* Update the counters and the text buffer representation. */ buffer = ppc64_pmc_hw(file); /* Put the data into the requestor's buffer. */ n = proc_ppc64_pmc_read(page, start, off, count, eof, buffer); spin_unlock(&proc_ppc64_lock); return n; } /* * DRENG the remainder of these functions still need work ... */ void pmc_proc_init(struct proc_dir_entry *iSeries_proc) { struct proc_dir_entry *ent = NULL; ent = create_proc_entry("lpevents", S_IFREG|S_IRUGO, iSeries_proc); if (!ent) return; ent->nlink = 1; ent->data = (void *)0; ent->read_proc = proc_get_lpevents; ent->write_proc = proc_reset_lpevents; ent = create_proc_entry("titanTod", S_IFREG|S_IRUGO, iSeries_proc); if (!ent) return; ent->nlink = 1; ent->data = (void *)0; ent->size = 0; ent->read_proc = proc_get_titanTod; ent->write_proc = NULL; pmc_proc_root = proc_mkdir("pmc", iSeries_proc); if (!pmc_proc_root) return; ent = create_proc_entry("control", S_IFREG|S_IRUSR|S_IWUSR, pmc_proc_root); if (!ent) return; ent->nlink = 1; ent->data = (void *)0; ent->read_proc = proc_pmc_get_control; ent->write_proc = proc_pmc_set_control; } static int pmc_calc_metrics( char *page, char **start, off_t off, int count, int *eof, int len) { if ( len <= off+count) *eof = 1; *start = page+off; len -= off; if ( len > count ) len = count; if ( len < 0 ) len = 0; return len; } static char * lpEventTypes[9] = { "Hypervisor\t\t", "Machine Facilities\t", "Session Manager\t", "SPD I/O\t\t", "Virtual Bus\t\t", "PCI I/O\t\t", "RIO I/O\t\t", "Virtual Lan\t\t", "Virtual I/O\t\t" }; int proc_get_lpevents (char *page, char **start, off_t off, int count, int *eof, void *data) { unsigned i; int len = 0; len += sprintf( page+len, "LpEventQueue 0\n" ); len += sprintf( page+len, " events processed:\t%lu\n", (unsigned long)xItLpQueue.xLpIntCount ); for (i=0; i<9; ++i) { len += sprintf( page+len, " %s %10lu\n", lpEventTypes[i], (unsigned long)xItLpQueue.xLpIntCountByType[i] ); } len += sprintf( page+len, "\n events processed by processor:\n" ); for (i=0; iprocessorCount; ++i) { len += sprintf( page+len, " CPU%02d %10u\n", i, paca[i].lpEvent_count ); } return pmc_calc_metrics( page, start, off, count, eof, len ); } int proc_reset_lpevents( struct file *file, const char *buffer, unsigned long count, void *data ) { return count; } static unsigned long startTitan = 0; static unsigned long startTb = 0; int proc_get_titanTod (char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; unsigned long tb0, titan_tod; tb0 = get_tb(); titan_tod = HvCallXm_loadTod(); len += sprintf( page+len, "Titan\n" ); len += sprintf( page+len, " time base = %016lx\n", tb0 ); len += sprintf( page+len, " titan tod = %016lx\n", titan_tod ); len += sprintf( page+len, " xProcFreq = %016x\n", xIoHriProcessorVpd[0].xProcFreq ); len += sprintf( page+len, " xTimeBaseFreq = %016x\n", xIoHriProcessorVpd[0].xTimeBaseFreq ); len += sprintf( page+len, " tb_ticks_per_jiffy = %lu\n", tb_ticks_per_jiffy ); len += sprintf( page+len, " tb_ticks_per_usec = %lu\n", tb_ticks_per_usec ); if ( !startTitan ) { startTitan = titan_tod; startTb = tb0; } else { unsigned long titan_usec = (titan_tod - startTitan) >> 12; unsigned long tb_ticks = (tb0 - startTb); unsigned long titan_jiffies = titan_usec / (1000000/HZ); unsigned long titan_jiff_usec = titan_jiffies * (1000000/HZ); unsigned long titan_jiff_rem_usec = titan_usec - titan_jiff_usec; unsigned long tb_jiffies = tb_ticks / tb_ticks_per_jiffy; unsigned long tb_jiff_ticks = tb_jiffies * tb_ticks_per_jiffy; unsigned long tb_jiff_rem_ticks = tb_ticks - tb_jiff_ticks; unsigned long tb_jiff_rem_usec = tb_jiff_rem_ticks / tb_ticks_per_usec; unsigned long new_tb_ticks_per_jiffy = (tb_ticks * (1000000/HZ))/titan_usec; len += sprintf( page+len, " titan elapsed = %lu uSec\n", titan_usec); len += sprintf( page+len, " tb elapsed = %lu ticks\n", tb_ticks); len += sprintf( page+len, " titan jiffies = %lu.%04lu \n", titan_jiffies, titan_jiff_rem_usec ); len += sprintf( page+len, " tb jiffies = %lu.%04lu\n", tb_jiffies, tb_jiff_rem_usec ); len += sprintf( page+len, " new tb_ticks_per_jiffy = %lu\n", new_tb_ticks_per_jiffy ); } return pmc_calc_metrics( page, start, off, count, eof, len ); } int proc_pmc_get_control (char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; if ( proc_pmc_control_mode == PMC_CONTROL_CPI ) { unsigned long mach_cycles = mfspr( PMC5 ); unsigned long inst_complete = mfspr( PMC4 ); unsigned long inst_dispatch = mfspr( PMC3 ); unsigned long thread_active_run = mfspr( PMC1 ); unsigned long thread_active = mfspr( PMC2 ); unsigned long cpi = 0; unsigned long cpithou = 0; unsigned long remain; if ( inst_complete ) { cpi = thread_active_run / inst_complete; remain = thread_active_run % inst_complete; if ( inst_complete > 1000000 ) cpithou = remain / ( inst_complete / 1000 ); else cpithou = ( remain * 1000 ) / inst_complete; } len += sprintf( page+len, "PMC CPI Mode\nRaw Counts\n" ); len += sprintf( page+len, "machine cycles : %12lu\n", mach_cycles ); len += sprintf( page+len, "thread active cycles : %12lu\n\n", thread_active ); len += sprintf( page+len, "instructions completed : %12lu\n", inst_complete ); len += sprintf( page+len, "instructions dispatched : %12lu\n", inst_dispatch ); len += sprintf( page+len, "thread active run cycles : %12lu\n", thread_active_run ); len += sprintf( page+len, "thread active run cycles/instructions completed\n" ); len += sprintf( page+len, "CPI = %lu.%03lu\n", cpi, cpithou ); } else if ( proc_pmc_control_mode == PMC_CONTROL_TLB ) { len += sprintf( page+len, "PMC TLB Mode\n" ); len += sprintf( page+len, "I-miss count : %12lu\n", mfspr( PMC1 ) ); len += sprintf( page+len, "I-miss latency : %12lu\n", mfspr( PMC2 ) ); len += sprintf( page+len, "D-miss count : %12lu\n", mfspr( PMC3 ) ); len += sprintf( page+len, "D-miss latency : %12lu\n", mfspr( PMC4 ) ); len += sprintf( page+len, "IERAT miss count : %12lu\n", mfspr( PMC5 ) ); len += sprintf( page+len, "D-reference count : %12lu\n", mfspr( PMC6 ) ); len += sprintf( page+len, "miss PTEs searched : %12lu\n", mfspr( PMC7 ) ); len += sprintf( page+len, "miss >8 PTEs searched : %12lu\n", mfspr( PMC8 ) ); } /* IMPLEMENT ME */ return pmc_calc_metrics( page, start, off, count, eof, len ); } unsigned long proc_pmc_conv_int( const char *buf, unsigned count ) { const char * p; char b0, b1; unsigned v, multiplier, mult, i; unsigned long val; multiplier = 10; p = buf; if ( count >= 3 ) { b0 = buf[0]; b1 = buf[1]; if ( ( b0 == '0' ) && ( ( b1 == 'x' ) || ( b1 == 'X' ) ) ) { p = buf + 2; count -= 2; multiplier = 16; } } val = 0; for ( i=0; i= '0' ) && ( b0 <= '9' ) ) v = b0 - '0'; else if ( multiplier == 16 ) { if ( ( b0 >= 'a' ) && ( b0 <= 'f' ) ) v = b0 - 'a' + 10; else if ( ( b0 >= 'A' ) && ( b0 <= 'F' ) ) v = b0 - 'A' + 10; else mult = 1; } else mult = 1; val *= mult; val += v; } return val; } static inline void proc_pmc_stop(void) { /* Freeze all counters, leave everything else alone */ mtspr( MMCR0, mfspr( MMCR0 ) | 0x80000000 ); } static inline void proc_pmc_start(void) { /* Unfreeze all counters, leave everything else alone */ mtspr( MMCR0, mfspr( MMCR0 ) & ~0x80000000 ); } static inline void proc_pmc_reset(void) { /* Clear all the PMCs to zeros * Assume a "stop" has already frozen the counters * Clear all the PMCs */ mtspr( PMC1, 0 ); mtspr( PMC2, 0 ); mtspr( PMC3, 0 ); mtspr( PMC4, 0 ); mtspr( PMC5, 0 ); mtspr( PMC6, 0 ); mtspr( PMC7, 0 ); mtspr( PMC8, 0 ); } static inline void proc_pmc_cpi(void) { /* Configure the PMC registers to count cycles and instructions */ /* so we can compute cpi */ /* * MMCRA[30] = 1 Don't count in wait state (CTRL[31]=0) * MMCR0[6] = 1 Freeze counters when any overflow * MMCR0[19:25] = 0x01 PMC1 counts Thread Active Run Cycles * MMCR0[26:31] = 0x05 PMC2 counts Thread Active Cycles * MMCR1[0:4] = 0x07 PMC3 counts Instructions Dispatched * MMCR1[5:9] = 0x03 PMC4 counts Instructions Completed * MMCR1[10:14] = 0x06 PMC5 counts Machine Cycles * */ proc_pmc_control_mode = PMC_CONTROL_CPI; /* Indicate to hypervisor that we are using the PMCs */ get_paca()->xLpPacaPtr->xPMCRegsInUse = 1; /* Freeze all counters */ mtspr( MMCR0, 0x80000000 ); mtspr( MMCR1, 0x00000000 ); /* Clear all the PMCs */ mtspr( PMC1, 0 ); mtspr( PMC2, 0 ); mtspr( PMC3, 0 ); mtspr( PMC4, 0 ); mtspr( PMC5, 0 ); mtspr( PMC6, 0 ); mtspr( PMC7, 0 ); mtspr( PMC8, 0 ); /* Freeze counters in Wait State (CTRL[31]=0) */ mtspr( MMCRA, 0x00000002 ); /* PMC3<-0x07, PMC4<-0x03, PMC5<-0x06 */ mtspr( MMCR1, 0x38cc0000 ); mb(); /* PMC1<-0x01, PMC2<-0x05 * Start all counters */ mtspr( MMCR0, 0x02000045 ); } static inline void proc_pmc_tlb(void) { /* Configure the PMC registers to count tlb misses */ /* * MMCR0[6] = 1 Freeze counters when any overflow * MMCR0[19:25] = 0x55 Group count * PMC1 counts I misses * PMC2 counts I miss duration (latency) * PMC3 counts D misses * PMC4 counts D miss duration (latency) * PMC5 counts IERAT misses * PMC6 counts D references (including PMC7) * PMC7 counts miss PTEs searched * PMC8 counts miss >8 PTEs searched * */ proc_pmc_control_mode = PMC_CONTROL_TLB; /* Indicate to hypervisor that we are using the PMCs */ get_paca()->xLpPacaPtr->xPMCRegsInUse = 1; /* Freeze all counters */ mtspr( MMCR0, 0x80000000 ); mtspr( MMCR1, 0x00000000 ); /* Clear all the PMCs */ 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( MMCRA, 0x00000000 ); mb(); /* PMC1<-0x55 * Start all counters */ mtspr( MMCR0, 0x02001540 ); } int proc_pmc_set_control( struct file *file, const char *buffer, unsigned long count, void *data ) { char stkbuf[10]; if (count > 9) count = 9; if (copy_from_user (stkbuf, buffer, count)) return -EFAULT; stkbuf[count] = 0; if ( ! strncmp( stkbuf, "stop", 4 ) ) proc_pmc_stop(); else if ( ! strncmp( stkbuf, "start", 5 ) ) proc_pmc_start(); else if ( ! strncmp( stkbuf, "reset", 5 ) ) proc_pmc_reset(); else if ( ! strncmp( stkbuf, "cpi", 3 ) ) proc_pmc_cpi(); else if ( ! strncmp( stkbuf, "tlb", 3 ) ) proc_pmc_tlb(); /* IMPLEMENT ME */ return count; } int proc_pmc_set_mmcr0( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); v = v & ~0x04000000; /* Don't allow interrupts for now */ if ( v & ~0x80000000 ) /* Inform hypervisor we are using PMCs */ get_paca()->xLpPacaPtr->xPMCRegsInUse = 1; else get_paca()->xLpPacaPtr->xPMCRegsInUse = 0; mtspr( MMCR0, v ); return count; } int proc_pmc_set_mmcr1( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); mtspr( MMCR1, v ); return count; } int proc_pmc_set_mmcra( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); v = v & ~0x00008000; /* Don't allow interrupts for now */ mtspr( MMCRA, v ); return count; } int proc_pmc_set_pmc1( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); mtspr( PMC1, v ); return count; } int proc_pmc_set_pmc2( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); mtspr( PMC2, v ); return count; } int proc_pmc_set_pmc3( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); mtspr( PMC3, v ); return count; } int proc_pmc_set_pmc4( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); mtspr( PMC4, v ); return count; } int proc_pmc_set_pmc5( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); mtspr( PMC5, v ); return count; } int proc_pmc_set_pmc6( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); mtspr( PMC6, v ); return count; } int proc_pmc_set_pmc7( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); mtspr( PMC7, v ); return count; } int proc_pmc_set_pmc8( struct file *file, const char *buffer, unsigned long count, void *data ) { unsigned long v; v = proc_pmc_conv_int( buffer, count ); mtspr( PMC8, v ); return count; } static loff_t nacamap_seek( struct file *file, loff_t off, int whence) { loff_t new; struct proc_dir_entry *dp; dp = file->f_dentry->d_inode->u.generic_ip; switch(whence) { case 0: new = off; break; case 1: new = file->f_pos + off; break; case 2: new = dp->size + off; break; default: return -EINVAL; } if ( new < 0 || new > dp->size ) return -EINVAL; return (file->f_pos = new); } static ssize_t nacamap_read( struct file *file, char *buf, size_t nbytes, loff_t *ppos) { unsigned pos = *ppos; struct proc_dir_entry *dp; dp = file->f_dentry->d_inode->u.generic_ip; if ( pos >= dp->size ) return 0; if ( nbytes >= dp->size ) nbytes = dp->size; if ( pos + nbytes > dp->size ) nbytes = dp->size - pos; copy_to_user( buf, (char *)dp->data + pos, nbytes ); *ppos = pos + nbytes; return nbytes; } static int nacamap_mmap( struct file *file, struct vm_area_struct *vma ) { struct proc_dir_entry *dp; dp = file->f_dentry->d_inode->u.generic_ip; vma->vm_flags |= VM_SHM | VM_LOCKED; if ((vma->vm_end - vma->vm_start) > dp->size) return -EINVAL; remap_page_range( vma->vm_start, __pa(dp->data), dp->size, vma->vm_page_prot ); return 0; } static int proc_ppc64_smt_snooze_read(char *page, char **start, off_t off, int count, int *eof, void *data) { if (naca->smt_snooze_delay) return sprintf(page, "%lu\n", naca->smt_snooze_delay); else return sprintf(page, "disabled\n"); } static int proc_ppc64_smt_snooze_write(struct file* file, const char *buffer, unsigned long count, void *data) { unsigned long val; char val_string[22]; if (!capable(CAP_SYS_ADMIN)) return -EACCES; if (count > sizeof(val_string) - 1) return -EINVAL; if (copy_from_user(val_string, buffer, count)) return -EFAULT; val_string[count] = '\0'; if (val_string[0] == '0' && (val_string[1] == '\n' || val_string[1] == '\0')) { naca->smt_snooze_delay = 0; return count; } val = simple_strtoul(val_string, NULL, 10); if (val != 0) naca->smt_snooze_delay = val; else return -EINVAL; return count; } static int proc_ppc64_smt_state_read(char *page, char **start, off_t off, int count, int *eof, void *data) { switch(naca->smt_state) { case SMT_OFF: return sprintf(page, "off\n"); break; case SMT_ON: return sprintf(page, "on\n"); break; case SMT_DYNAMIC: return sprintf(page, "dynamic\n"); break; default: return sprintf(page, "unknown\n"); break; } } void proc_ppc64_create_smt(void) { struct proc_dir_entry *ent_snooze = create_proc_entry("smt-snooze-delay", S_IRUGO | S_IWUSR, proc_ppc64_root); struct proc_dir_entry *ent_enabled = create_proc_entry("smt-enabled", S_IRUGO | S_IWUSR, proc_ppc64_root); if (ent_snooze) { ent_snooze->nlink = 1; ent_snooze->data = NULL; ent_snooze->read_proc = (void *)proc_ppc64_smt_snooze_read; ent_snooze->write_proc = (void *)proc_ppc64_smt_snooze_write; } if (ent_enabled) { ent_enabled->nlink = 1; ent_enabled->data = NULL; ent_enabled->read_proc = (void *)proc_ppc64_smt_state_read; ent_enabled->write_proc = NULL; } }