diff -urN linux+devfs/Documentation/Configure.help linux/Documentation/Configure.help --- linux+devfs/Documentation/Configure.help Fri Jan 22 12:04:31 1999 +++ linux/Documentation/Configure.help Fri Jan 22 17:13:42 1999 @@ -8406,6 +8406,48 @@ See Documentation/mtrr.txt for more information. +MSR access from user space +CONFIG_MSR + This option allows you to manipulate any Model-Specific Register + (MSR) in your processor from user space. Normally, MSRs are only + accessible from kernel space. This option makes access easy for + administrators and people wanting to experiment. You need to enable + CONFIG_DEVFS_FS later in the configuration. + A separate user space library is available which provides a + programming interface which simulates the kernel space interface to + MSRs. This should allow for easy development of MSR manipulation + code in user space which can then be directly incorporated into a + kernel space device driver. The driver creates the /dev/cpu/msr + device file. + + It is safe to include this driver: it does not affect the system + unless /dev/cpu/msr is manipulated. + You can build this driver as a module. + + See Documentation/msr.txt for more information. + + If you are only interested in performance monitoring, see the + CONFIG_PERFMON option instead: it creates a simpler and safer + interface. + +Performance monitoring interface +CONFIG_PERFMON + This option allows you to manipulate the performance monitoring + facilities in your processor from user space. You can use these + facilities to analyse how your programme is behaving (i.e. count + cache miss events). You need to enable CONFIG_DEVFS_FS later in the + configuration. + + A separate user space library is available which provides a + convenient programming interface. The driver creates the + /dev/cpu/perfmon%u device files (one file per performance counter). + + It is safe to include this driver: it does not affect the system + unless /dev/cpu/perfmon%u is manipulated. + You can build this driver as a module. + + See Documentation/perfmon.txt for more information. + Main CPU frequency, only for DEC alpha machine CONFIG_FT_ALPHA_CLOCK On some DEC Alpha machines the CPU clock frequency cannot be diff -urN linux+devfs/Documentation/msr.txt linux/Documentation/msr.txt --- linux+devfs/Documentation/msr.txt Thu Jan 1 10:00:00 1970 +++ linux/Documentation/msr.txt Fri Jan 22 17:38:41 1999 @@ -0,0 +1,32 @@ +MSR (Model-Specific Register) control +21 Jan 1999 +Richard Gooch + + +MSRs provide access to internal processor functions and allow you to +control the behaviour of the processor. While the set of MSRs is +highly model-dependent, there is a reasonably generic interface which +may be used to read and write these registers. The CONFIG_MSR option +allows you to compile the device driver which provides access to these +registers from user space. The driver creates the /dev/cpu/msr device +file. By using ioctl() functions on this device you can read and write +specified MSRs. + +A separate user space library is available from: +http://www.atnf.csiro.au/~rgooch/linux/ +which mimics the MSR interface available in kernel space. This makes +it very easy to write code which manipulates MSRs, as all code +development can be done from userspace. Once the code has been +developed and tested, it can be incorporated into a kernel device +driver with very few changes. Apart from being more convenient, it is +also safer, as you can write your code without worring about +generating an Oops, a Panic or generally messing with kernel data +structures. + +There is one caveat, however: if you incorrectly programme a MSR, the +behaviour of the processor is undefined. At best, your programme will +fail as you try to write to an undefined MSR. At worst, you could +trigger an internal fault and cause the processor to cook itself. That +said, chances are the worst will not happen, because modern processors +should not do anything harmful to themselves, no matter how they are +programmed. But you have been warned: carry on at your own risk. diff -urN linux+devfs/Documentation/perfmon.txt linux/Documentation/perfmon.txt --- linux+devfs/Documentation/perfmon.txt Thu Jan 1 10:00:00 1970 +++ linux/Documentation/perfmon.txt Thu Jan 21 22:19:21 1999 @@ -0,0 +1,51 @@ +Performance monitoring interface control +21 Jan 1999 +Richard Gooch + + +Many processors provide special Performance Monitoring Counters (PMCs) +for the purposes of system or application tuning. You can usually +count mundane events such as the number of floating point multiplies, +to more exotic events such as the number of cache misses when reading +data. If you have a programme which is not running as fast as you +expected, performance monitoring can provide useful insights. The +CONFIG_PERFMON option allows you to compile the device driver which +provides a slightly simplified interface to these features. The driver +creates the /dev/cpu/perfmon%u device files (one file per performance +counter). + +In general, you can also gain access to these facilities using the +interface created by CONFIG_MSR, but that interface is harder to use +and buggy programmes using it are more likely to crash your machine. + +Because this is a cruel world, the kinds of events you can monitor +vary greatly from one model processor to another. This interface tries +to make no assumptions about the kinds of events supported, it just +provides a generic interface for selecting which events are to be +counted and easy access to the counters themselves. It is up to the +programmer to know which event codes are available for different model +processors. + +If you read one of the device files, the current counter value is +output in ASCII hexadecimal format. A set of ioctl() operations allows +you to select which event a counter will count. + +The counters can operate in one of two modes: "global" (or basic) and +"virtual" (or per-process). In global mode, the PMCs count events for +all processes on the system. On a system which is idle except for the +application you wish to monitor, this is the easiest mode of +operation. On a busy system, you may wish to consider enabling virtual +mode. In this mode, each process (or thread) has it's own set of +private (virtual) PMCs, which are saved and restored across context +switches. Naturally, when you read the device files you only see the +PMCs for the reading process and not an aggregate of the whole +system. If you wish to inspect the PMCs of another process, you need +to use the ptrace(2) interface. Note that the same type of event is +counted for all processes for each PMC. +NOTE: VIRTUAL MODE IS NOT YET IMPLEMENTED. + +A separate user space library is available from: +http://www.atnf.csiro.au/~rgooch/linux/ +which provides a simple and convenient interface to the device +driver. In addition, header files are provided which have symbolic +definitions for the event codes for some processor models. diff -urN linux+devfs/arch/i386/config.in linux/arch/i386/config.in --- linux+devfs/arch/i386/config.in Thu Jan 21 05:18:53 1999 +++ linux/arch/i386/config.in Fri Jan 22 15:33:26 1999 @@ -1,3 +1,4 @@ + # # For a description of the syntax of this configuration file, # see the Configure script. @@ -36,6 +37,8 @@ bool 'Math emulation' CONFIG_MATH_EMULATION bool 'MTRR (Memory Type Range Register) support' CONFIG_MTRR bool 'Symmetric multi-processing support' CONFIG_SMP +tristate 'MSR access from user space' CONFIG_MSR +tristate 'Performance monitoring interface' CONFIG_PERFMON endmenu mainmenu_option next_comment diff -urN linux+devfs/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile --- linux+devfs/arch/i386/kernel/Makefile Thu Jan 21 05:18:53 1999 +++ linux/arch/i386/kernel/Makefile Fri Jan 22 15:31:49 1999 @@ -28,9 +28,21 @@ ifeq ($(CONFIG_MTRR),y) OX_OBJS += mtrr.o +endif + +ifeq ($(CONFIG_MSR),y) +OX_OBJS += msr.o else - ifeq ($(CONFIG_MTRR),m) - MX_OBJS += mtrr.o + ifeq ($(CONFIG_MSR),m) + MX_OBJS += msr.o + endif +endif + +ifeq ($(CONFIG_PERFMON),y) +OX_OBJS += perfmon.o +else + ifeq ($(CONFIG_PERFMON),m) + MX_OBJS += perfmon.o endif endif diff -urN linux+devfs/arch/i386/kernel/msr.c linux/arch/i386/kernel/msr.c --- linux+devfs/arch/i386/kernel/msr.c Thu Jan 1 10:00:00 1970 +++ linux/arch/i386/kernel/msr.c Thu Jan 21 22:25:00 1999 @@ -0,0 +1,174 @@ +/* x86 MSR (Model-Specific Register) driver. + + Copyright (C) 1999 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + ChangeLog (kept in Australia/NSW date) + + 19990118 Richard Gooch + Original version for /dev/cpu/msr interface. + v0.1 + 19990119 Philipp Rumpf + Return -EINVAL for invalid MSR accesses instead of oopsing. + v0.2 + 19990120 Richard Gooch + Added file permission checks in . + v0.3 +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define MSR_VERSION "0.3 (19990121)" + +/* Safe access to MSRs + * These routines use pointer indirection to be able to have a useful return + * value + */ + +static inline int wrmsr (struct msr *msr) +{ + int ret; + + asm volatile ( + " 1: + wrmsr + movl $0,%0 + 2: + .section .fixup,\"ax\" + 3: + movl $1,%0 + jmp 2b + .section __ex_table,\"a\" + .align 4 + .long 1b,3b + .text " /* NOTE: This one is dirty, we should use .previous instead */ + : "=r" (ret) + : "a" (msr->low), "d" (msr->high), "c" (msr->regnum)); + return ret; +} /* End Function wrmsr */ + +static inline int rdmsr (struct msr *msr) +{ + int ret; + + asm ( + " 1: + rdmsr + movl $0,%0 + 2: + .section .fixup,\"ax\" + 3: + movl $1,%0 + jmp 2b + .section __ex_table,\"a\" + .align 4 + .long 1b,3b + .text " /* NOTE: This one is dirty, we should use .previous instead */ + : "=r" (ret), "=a" (msr->low), "=d" (msr->high) + : "c" (msr->regnum) + : "memory"); + return ret; +} /* End Function rdmsr */ + +static int msr_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct msr msr; + + switch (cmd) + { + default: + return -ENOIOCTLCMD; + case MSRIOC_READ: + if ( !(file->f_mode & FMODE_READ) ) return -EPERM; + if ( copy_from_user (&msr, (void *) arg, sizeof msr) ) return -EFAULT; + if ( rdmsr (&msr) ) return -EINVAL; + if ( copy_to_user ( (void *) arg, &msr, sizeof msr) ) return -EFAULT; + break; + case MSRIOC_WRITE: + if ( !(file->f_mode & FMODE_WRITE) ) return -EPERM; + if ( copy_from_user (&msr, (void *) arg, sizeof msr) ) return -EFAULT; + if ( wrmsr (&msr) ) return -EINVAL; + break; + } + return 0; +} /* End Function msr_ioctl */ + +static int msr_open (struct inode *ino, struct file *file) +{ + MOD_INC_USE_COUNT; + return 0; +} /* End Function msr_open */ + +static int msr_close (struct inode *ino, struct file *file) +{ + MOD_DEC_USE_COUNT; + return 0; +} /* End Function msr_close */ + +static struct file_operations msr_fops = +{ + ioctl: msr_ioctl, /* IOctl */ + open: msr_open, /* Open */ + release: msr_close, /* Release */ +}; + +static devfs_handle_t devfs_handle = NULL; + +int init_module (void) +{ + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MSR) ) return -ENODEV; +#ifdef __SMP__ + printk ("msr: SMP not supported yet\n"); + return -ENOTSUP; +#endif + devfs_handle = devfs_register ("cpu/msr", 0, DEVFS_FL_AUTO_DEVNUM, 0, 0, + S_IFCHR | S_IRUSR | S_IWUSR, 0, 0, + &msr_fops, NULL); + if (devfs_handle == NULL) return -ENOMEM; + printk ("msr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n", MSR_VERSION); + return 0; +} + +void cleanup_module (void) +{ + devfs_unregister (devfs_handle); +} /* End Function cleanup_module */ diff -urN linux+devfs/arch/i386/kernel/perfmon.c linux/arch/i386/kernel/perfmon.c --- linux+devfs/arch/i386/kernel/perfmon.c Thu Jan 1 10:00:00 1970 +++ linux/arch/i386/kernel/perfmon.c Thu Jan 21 22:23:35 1999 @@ -0,0 +1,402 @@ +/* x86 performance monitor driver. + + Copyright (C) 1999 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + ChangeLog (kept in Australia/NSW date) + + 19990120 Richard Gooch + Original version for /dev/cpu/perfmon%u interface. + v0.1 +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PERFMON_VERSION "0.1 (19990121)" + +#define TRUE 1 +#define FALSE 0 + +/* List of all supported MSRs */ +/* Pentium specific */ +#define MSR_P5_CESR 0x11 +#define MSR_P5_CTR0 0x12 +#define MSR_P5_CTR1 0x13 +/* P6 specific */ +#define MSR_P6_PERFCTR0 0xc1 +#define MSR_P6_EVNTSEL0 0x186 +#define MSR_P6_EVNTSEL1 0x187 + +/* Access to machine-specific registers (available on 586 and better only) + Note: the rd* operations modify the parameters directly (without using + pointer indirection), this allows gcc to optimize better +*/ +#define rdmsr(msr,low,high) \ + __asm__ __volatile__("rdmsr" \ + : "=a" (low), "=d" (high) \ + : "c" (msr)) + +#define wrmsr(msr,low,high) \ + __asm__ __volatile__("wrmsr" \ + : /* no outputs */ \ + : "c" (msr), "a" (low), "d" (high)) + +#define rdpmc(counter,low,high) \ + if (has_rdpmc) _rdpmc (counter, low, high); \ + else {unsigned int msr = MSR_P5_CTR0 + counter; \ + rdmsr (msr, low, high);} + +#define _rdpmc(counter,low,high) \ + __asm__ ("rdpmc" : "=a" (low), "=d" (high) \ + : "c" (counter)) + +#define rdcr4(val) \ + asm volatile ("movl %%cr4, %0" \ + : "=r" (val) : : "memory"); + +#define wrcr4(val) \ + asm volatile ("movl %0, %%cr4" \ + : : "r" (val) : "memory"); + +struct counter_type +{ + spinlock_t lock; + volatile struct file *allocator; + devfs_handle_t de; +}; + +static char has_rdpmc = FALSE; /* Whether the rdpmc instruction is supported */ +static unsigned int max_counters = 0; +static struct counter_type *counters = NULL; +static struct cpuinfo_short cpuinfo_short; + +static int (*allocate) (struct perfmon perfmon, unsigned int counter) = NULL; +static void (*wrpmc) (unsigned int counter, unsigned long low, + unsigned long high) = NULL; + + +static int intel_p5_allocate (struct perfmon perfmon, unsigned int counter) +{ + int shift; + unsigned long val, dummy, tmp; + + shift = (counter == 0) ? 0 : 16; + rdmsr (MSR_P5_CESR, val, dummy); + val &= ~(0xff << shift); + tmp = perfmon.event; + if (perfmon.count_user) tmp |= 2 << 6; + if (perfmon.count_kernel) tmp |= 1 << 6; + val |= tmp << shift; + wrmsr (MSR_P5_CESR, val, dummy); + return 0; +} /* End Function intel_p5_allocate */ + +static void intel_p5_wrpmc (unsigned int counter, unsigned long low, + unsigned long high) +{ + unsigned int msr = MSR_P5_CTR0 + counter; + + wrmsr (msr, low, high); +} /* End Function intel_p5_wrpmc */ + +static int intel_p6_allocate (struct perfmon perfmon, unsigned int counter) +{ + unsigned long val, dummy; + + val = perfmon.event | (perfmon.extra & 0xff) << 8; + if (perfmon.count_user) val |= 1 << 16; + if (perfmon.count_kernel) val |= 1 << 17; + val |= (1 << 18) | (1 << 22); + if (counter == 0) wrmsr (MSR_P6_EVNTSEL0, val, 0); + else + { + wrmsr (MSR_P6_EVNTSEL0 + counter, val, 0); + rdmsr (MSR_P6_EVNTSEL0, val, dummy); + val |= 1 << 22; + wrmsr (MSR_P6_EVNTSEL0, val, dummy); + } + return 0; +} /* End Function intel_p6_allocate */ + +static void intel_p6_wrpmc (unsigned int counter, unsigned long low, + unsigned long high) +{ + unsigned int msr = MSR_P6_PERFCTR0 + counter; + + wrmsr (msr, low, high); +} /* End Function intel_p6_wrpmc */ + +__initfunc(static int intel_setup (void)) +{ + switch (boot_cpu_data.x86) + { + case 0: + case 1: + case 2: + case 3: + case 4: + printk ("perfmon: requires family 5 or above\n"); + return -ENODEV; + /*break;*/ + case 5: + if (boot_cpu_data.x86 == 5) + { + has_rdpmc = (boot_cpu_data.x86_capability & X86_FEATURE_MMX) ? + TRUE : FALSE; + } + max_counters = 2; + allocate = intel_p5_allocate; + wrpmc = intel_p5_wrpmc; + break; + default: + has_rdpmc = TRUE; + max_counters = 2; + allocate = intel_p6_allocate; + wrpmc = intel_p6_wrpmc; + break; + } + cpuinfo_short.family = boot_cpu_data.x86; + cpuinfo_short.vendor = boot_cpu_data.x86_vendor; + cpuinfo_short.model = boot_cpu_data.x86_model; + cpuinfo_short.mask = boot_cpu_data.x86_mask; + return 0; +} /* End Function intel_setup */ + +static ssize_t perfmon_read (struct file *file, char *buf, size_t len, + loff_t *ppos) +{ + int mylen, counternum; + unsigned long low, high; + struct counter_type *counter = file->private_data; + char buffer[max_counters * 40]; + + if ( !(file->f_mode & FMODE_READ) ) return -EPERM; + counternum = counter - counters; + rdpmc (counternum, low, high); + sprintf (buffer, "0x%lx%0lx\n", high & 0xff, low); + mylen = strlen (buffer); + if (*ppos >= mylen) return 0; + if (*ppos + len > mylen) len = mylen - *ppos; + if ( copy_to_user (buf, buffer + *ppos, len) ) return -EFAULT; + *ppos += len; + return len; +} /* End Function perfmon_read */ + +static ssize_t perfmon_write (struct file *file, const char *buf, size_t len, + loff_t *ppos) +{ + int counternum; + unsigned long val; + struct counter_type *counter = file->private_data; + char line[64]; + + if ( !(file->f_mode & FMODE_WRITE) ) return -EPERM; + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) return -ESPIPE; + counternum = counter - counters; + memset (line, 0, 64); + if (len > 64) len = 64; + if ( copy_from_user (line, buf, len - 1) ) return -EFAULT; + val = simple_strtoul (line, NULL, 0); + (*wrpmc) (counternum, val, 0); + return 0; +} /* End Function perfmon_write */ + +static int perfmon_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int enable, counternum; + unsigned long cr4; + struct perfmon perfmon; + struct counter uc; + struct counter_type *counter = file->private_data; + + counternum = counter - counters; + switch (cmd) + { + default: + return -ENOIOCTLCMD; + case PERFMONIOC_ALLOCATE: + if ( !(file->f_mode & FMODE_WRITE) ) return -EPERM; + if (counter->allocator) return -EBUSY; + spin_lock (&counter->lock); + if (counter->allocator) + { + spin_unlock (&counter->lock); + return -EBUSY; + } + counter->allocator = file; + spin_unlock (&counter->lock); + if ( copy_from_user (&perfmon, (void *) arg, sizeof perfmon) ) + return -EFAULT; + return (*allocate) (perfmon, counternum); + break; + case PERFMONIOC_DEALLOCATE: + if ( !(file->f_mode & FMODE_WRITE) ) return -EPERM; + if (counter->allocator != file) return -EPERM; + counter->allocator = NULL; + break; + case PERFMONIOC_ENABLE_DIRECT: + if ( !(file->f_mode & FMODE_WRITE) ) return -EPERM; + /* Even if the rdpmc instruction does not exist, enable/disable + userspace access anyway, because that will control access to the + TSC */ + if ( copy_from_user (&enable, (void *) arg, sizeof enable) ) + return -EFAULT; + /* Bit 8 of Control Register 4 (CR4) controls whether userspace can + read the performance monitoring counters */ + rdcr4 (cr4); + if (enable) cr4 |= 1 << 8; + else cr4 &= ~(1 << 8); + wrcr4 (cr4); + if (enable && !has_rdpmc) + { + /* OK we've enabled access, but since PMC access from userspace + is not available, pass this information back */ + enable = 0; + if ( copy_to_user ( (void *) arg, &enable, sizeof enable) ) + return -EFAULT; + } + break; + case PERFMONIOC_GET_CPUINFO: + if ( !(file->f_mode & FMODE_READ) ) return -EPERM; + if ( copy_to_user ( (void *) arg, &cpuinfo_short, + sizeof cpuinfo_short) ) return -EFAULT; + break; + case PERFMONIOC_READ_PMC: + if ( !(file->f_mode & FMODE_READ) ) return -EPERM; + if ( copy_from_user (&uc, (void *) arg, sizeof counter) ) + return -EFAULT; + if (uc.counternum >= max_counters) return -EINVAL; + rdpmc (uc.counternum, uc.low, uc.high); + if ( copy_to_user ( (void *) arg, &uc, sizeof uc) ) return -EFAULT; + break; + case PERFMONIOC_WRITE_PMC: + if ( !(file->f_mode & FMODE_WRITE) ) return -EPERM; + if ( copy_from_user (&uc, (void *) arg, sizeof counter) ) + return -EFAULT; + (*wrpmc) (uc.counternum, uc.low, uc.high); + break; + } + return 0; +} /* End Function perfmon_ioctl */ + +static int perfmon_open (struct inode *ino, struct file *file) +{ + MOD_INC_USE_COUNT; + return 0; +} /* End Function perfmon_open */ + +static int perfmon_close (struct inode *ino, struct file *file) +{ + struct counter_type *counter = file->private_data; + + if (counter->allocator == file) counter->allocator = NULL; + MOD_DEC_USE_COUNT; + return 0; +} /* End Function perfmon_close */ + +static struct file_operations perfmon_fops = +{ + read: perfmon_read, /* Read */ + write: perfmon_write, /* Write */ + ioctl: perfmon_ioctl, /* IOctl */ + open: perfmon_open, /* Open */ + release: perfmon_close, /* Release */ +}; + +int init_module (void) +{ + int err, count; + devfs_handle_t de; + char buffer[32]; + + if ( !(boot_cpu_data.x86_capability & X86_FEATURE_MSR) ) return -ENODEV; +#ifdef __SMP__ + printk ("perfmon: SMP not supported yet\n"); + return -EINVAL; +#endif + switch (boot_cpu_data.x86_vendor) + { + case X86_VENDOR_INTEL: + err = intel_setup (); + break; + default: + printk ("perfmon: non-Intel vendors not supported yet\n"); + err = -ENODEV; + break; + } + if (err != 0) return err; + if ( ( counters = kmalloc (sizeof *counters * max_counters, GFP_KERNEL) ) + == NULL ) + { + printk ("perfmon: could not allocate\n"); + return -ENOMEM; + } + for (count = 0; count < max_counters; ++count) + { + sprintf (buffer, "cpu/perfmon%d", count); + de = devfs_register (buffer, 0, DEVFS_FL_AUTO_DEVNUM, 0,0, + S_IFREG | S_IRUSR | S_IWUSR, 0, 0, + &perfmon_fops, counters + count); + counters[count].de = de; + if (de == NULL) + { + while (--count >= 0) devfs_unregister (counters[count].de); + kfree (counters); + return -ENOMEM; + } + counters[count].lock = SPIN_LOCK_UNLOCKED; + counters[count].allocator = NULL; + } + printk ("perfmon: v%s Richard Gooch (rgooch@atnf.csiro.au), %u counters\n", + PERFMON_VERSION, max_counters); + return 0; +} + +void cleanup_module (void) +{ + int count; + + for (count = 0; count < max_counters; ++count) + devfs_unregister (counters[count].de); + kfree (counters); +} /* End Function cleanup_module */ diff -urN linux+devfs/include/linux/msr.h linux/include/linux/msr.h --- linux+devfs/include/linux/msr.h Thu Jan 1 10:00:00 1970 +++ linux/include/linux/msr.h Wed Jan 20 11:30:05 1999 @@ -0,0 +1,45 @@ +/* Generic MSR (Model-Specific Register) ioctls. + Use this for the /dev/cpu/msr interface. + + Copyright (C) 1999 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. +*/ +#ifndef _LINUX_MSR_H +#define _LINUX_MSR_H + +#include + +#define MSR_DEVICE "/dev/cpu/msr" + +#define MSR_IOCTL_BASE 'M' + +struct msr +{ + unsigned long low; /* Low 32 bits of register */ + unsigned long high; /* High 32 bits of register */ + unsigned int regnum; /* Register number */ +}; + +/* These are the various ioctls */ +#define MSRIOC_READ _IOWR(MSR_IOCTL_BASE, 0, struct msr) +#define MSRIOC_WRITE _IOWR(MSR_IOCTL_BASE, 1, struct msr) + + +#endif /* _LINUX_MSR_H */ diff -urN linux+devfs/include/linux/perfmon.h linux/include/linux/perfmon.h --- linux+devfs/include/linux/perfmon.h Thu Jan 1 10:00:00 1970 +++ linux/include/linux/perfmon.h Thu Jan 21 17:56:56 1999 @@ -0,0 +1,70 @@ +/* Generic performance monitor driver ioctls. + Use this for the /dev/cpu/perfmon%u interface. + + Copyright (C) 1999 Richard Gooch + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. +*/ +#ifndef _LINUX_PERFMON_H +#define _LINUX_PERFMON_H + +#include + +#define PERFMON_DEVICE "/dev/cpu/perfmon%u" +#define PERFMON_DEVICE0 "/dev/cpu/perfmon0" + +#define PERFMON_IOCTL_BASE 'P' + +struct perfmon +{ + unsigned int event; /* The event to monitor */ + unsigned int extra; /* Extra data (i.e. mask) */ + char count_user:1; /* Whether to count userspace events */ + char count_kernel:1; /* Whether to count kernel space events */ +}; +/* For P6, mask is often used to specify any of the 4 MESI cache states: + M: modified, E: exclusive, S: shared, I: invalid +*/ + +struct cpuinfo_short +{ + unsigned char family; /* CPU family (5==P5, 6==P6, etc.) */ + unsigned char vendor; /* CPU vendor */ + unsigned char model; + unsigned char mask; +}; + +struct counter +{ + unsigned long low; /* Low 32 bits of counter */ + unsigned long high; /* High 32 bits of counter */ + unsigned int counternum; /* Counter number */ +}; + +/* These are the various ioctls */ +#define PERFMONIOC_ALLOCATE _IOW(PERFMON_IOCTL_BASE, 0, struct perfmon) +#define PERFMONIOC_DEALLOCATE _IOW(PERFMON_IOCTL_BASE, 1, void) +#define PERFMONIOC_ENABLE_DIRECT _IOWR(PERFMON_IOCTL_BASE, 2, int) +#define PERFMONIOC_GET_CPUINFO _IOR(PERFMON_IOCTL_BASE, 3, \ + struct cpuinfo_short) +#define PERFMONIOC_READ_PMC _IOR(PERFMON_IOCTL_BASE, 4, struct counter) +#define PERFMONIOC_WRITE_PMC _IOW(PERFMON_IOCTL_BASE, 5, struct counter) + + +#endif /* _LINUX_PERFMON_H */