perfmon.c revision 18854
1/* 2 * Copyright 1996 Massachusetts Institute of Technology 3 * 4 * Permission to use, copy, modify, and distribute this software and 5 * its documentation for any purpose and without fee is hereby 6 * granted, provided that both the above copyright notice and this 7 * permission notice appear in all copies, that both the above 8 * copyright notice and this permission notice appear in all 9 * supporting documentation, and that the name of M.I.T. not be used 10 * in advertising or publicity pertaining to distribution of the 11 * software without specific, written prior permission. M.I.T. makes 12 * no representations about the suitability of this software for any 13 * purpose. It is provided "as is" without express or implied 14 * warranty. 15 * 16 * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 17 * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 20 * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 * $Id: perfmon.c,v 1.7 1996/08/02 21:15:48 bde Exp $ 30 */ 31 32#include "opt_cpu.h" 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/fcntl.h> 37#include <sys/ioccom.h> 38 39#include <machine/cpu.h> 40#include <machine/cputypes.h> 41#include <machine/clock.h> 42#include <machine/perfmon.h> 43 44static int perfmon_inuse; 45static int perfmon_cpuok; 46static int msr_ctl[NPMC]; 47static int msr_pmc[NPMC]; 48static unsigned int ctl_shadow[NPMC]; 49static quad_t pmc_shadow[NPMC]; /* used when ctr is stopped on P5 */ 50static int (*writectl)(int); 51static int writectl5(int); 52static int writectl6(int); 53 54/* 55 * Must be called after cpu_class is set up. 56 */ 57void 58perfmon_init(void) 59{ 60 switch(cpu_class) { 61 case CPUCLASS_586: 62 perfmon_cpuok = 1; 63 msr_ctl[0] = 0x11; 64 msr_ctl[1] = 0x11; 65 msr_pmc[0] = 0x12; 66 msr_pmc[1] = 0x13; 67 writectl = writectl5; 68 break; 69 case CPUCLASS_686: 70 perfmon_cpuok = 1; 71 msr_ctl[0] = 0x186; 72 msr_ctl[1] = 0x187; 73 msr_pmc[0] = 0xc1; 74 msr_pmc[1] = 0xc2; 75 writectl = writectl6; 76 break; 77 78 default: 79 perfmon_cpuok = 0; 80 break; 81 } 82} 83 84int 85perfmon_avail(void) 86{ 87 return perfmon_cpuok; 88} 89 90int 91perfmon_setup(int pmc, unsigned int control) 92{ 93 if (pmc < 0 || pmc >= NPMC) 94 return EINVAL; 95 96 perfmon_inuse |= (1 << pmc); 97 control &= ~(PMCF_SYS_FLAGS << 16); 98 disable_intr(); 99 ctl_shadow[pmc] = control; 100 writectl(pmc); 101 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 102 enable_intr(); 103 return 0; 104} 105 106int 107perfmon_get(int pmc, unsigned int *control) 108{ 109 if (pmc < 0 || pmc >= NPMC) 110 return EINVAL; 111 112 if (perfmon_inuse & (1 << pmc)) { 113 *control = ctl_shadow[pmc]; 114 return 0; 115 } 116 return EBUSY; /* XXX reversed sense */ 117} 118 119int 120perfmon_fini(int pmc) 121{ 122 if (pmc < 0 || pmc >= NPMC) 123 return EINVAL; 124 125 if (perfmon_inuse & (1 << pmc)) { 126 perfmon_stop(pmc); 127 ctl_shadow[pmc] = 0; 128 perfmon_inuse &= ~(1 << pmc); 129 return 0; 130 } 131 return EBUSY; /* XXX reversed sense */ 132} 133 134int 135perfmon_start(int pmc) 136{ 137 if (pmc < 0 || pmc >= NPMC) 138 return EINVAL; 139 140 if (perfmon_inuse & (1 << pmc)) { 141 disable_intr(); 142 ctl_shadow[pmc] |= (PMCF_EN << 16); 143 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]); 144 writectl(pmc); 145 enable_intr(); 146 return 0; 147 } 148 return EBUSY; 149} 150 151int 152perfmon_stop(int pmc) 153{ 154 if (pmc < 0 || pmc >= NPMC) 155 return EINVAL; 156 157 if (perfmon_inuse & (1 << pmc)) { 158 disable_intr(); 159 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]); 160 ctl_shadow[pmc] &= ~(PMCF_EN << 16); 161 writectl(pmc); 162 enable_intr(); 163 return 0; 164 } 165 return EBUSY; 166} 167 168int 169perfmon_read(int pmc, quad_t *val) 170{ 171 if (pmc < 0 || pmc >= NPMC) 172 return EINVAL; 173 174 if (perfmon_inuse & (1 << pmc)) { 175 if (ctl_shadow[pmc] & (PMCF_EN << 16)) 176 *val = rdmsr(msr_pmc[pmc]); 177 else 178 *val = pmc_shadow[pmc]; 179 return 0; 180 } 181 182 return EBUSY; 183} 184 185int 186perfmon_reset(int pmc) 187{ 188 if (pmc < 0 || pmc >= NPMC) 189 return EINVAL; 190 191 if (perfmon_inuse & (1 << pmc)) { 192 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 193 return 0; 194 } 195 return EBUSY; 196} 197 198/* 199 * Unfortunately, the performance-monitoring registers are laid out 200 * differently in the P5 and P6. We keep everything in P6 format 201 * internally (except for the event code), and convert to P5 202 * format as needed on those CPUs. The writectl function pointer 203 * is set up to point to one of these functions by perfmon_init(). 204 */ 205int 206writectl6(int pmc) 207{ 208 if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) { 209 wrmsr(msr_ctl[pmc], 0); 210 } else { 211 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]); 212 } 213 return 0; 214} 215 216#define P5FLAG_P 0x200 217#define P5FLAG_E 0x100 218#define P5FLAG_USR 0x80 219#define P5FLAG_OS 0x40 220 221int 222writectl5(int pmc) 223{ 224 quad_t newval = 0; 225 226 if (ctl_shadow[1] & (PMCF_EN << 16)) { 227 if (ctl_shadow[1] & (PMCF_USR << 16)) 228 newval |= P5FLAG_USR << 16; 229 if (ctl_shadow[1] & (PMCF_OS << 16)) 230 newval |= P5FLAG_OS << 16; 231 if (!(ctl_shadow[1] & (PMCF_E << 16))) 232 newval |= P5FLAG_E << 16; 233 newval |= (ctl_shadow[1] & 0x3f) << 16; 234 } 235 if (ctl_shadow[0] & (PMCF_EN << 16)) { 236 if (ctl_shadow[0] & (PMCF_USR << 16)) 237 newval |= P5FLAG_USR; 238 if (ctl_shadow[0] & (PMCF_OS << 16)) 239 newval |= P5FLAG_OS; 240 if (!(ctl_shadow[0] & (PMCF_E << 16))) 241 newval |= P5FLAG_E; 242 newval |= ctl_shadow[0] & 0x3f; 243 } 244 245 wrmsr(msr_ctl[0], newval); 246 return 0; /* XXX should check for unimplemented bits */ 247} 248 249/* 250 * Now the user-mode interface, called from a subdevice of mem.c. 251 */ 252static int writer; 253static int writerpmc; 254 255int 256perfmon_open(dev_t dev, int flags, int fmt, struct proc *p) 257{ 258 if (!perfmon_cpuok) 259 return ENXIO; 260 261 if (flags & FWRITE) { 262 if (writer) { 263 return EBUSY; 264 } else { 265 writer = 1; 266 writerpmc = 0; 267 } 268 } 269 return 0; 270} 271 272int 273perfmon_close(dev_t dev, int flags, int fmt, struct proc *p) 274{ 275 if (flags & FWRITE) { 276 int i; 277 278 for (i = 0; i < NPMC; i++) { 279 if (writerpmc & (1 << i)) 280 perfmon_fini(i); 281 } 282 writer = 0; 283 } 284 return 0; 285} 286 287int 288perfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p) 289{ 290 struct pmc *pmc; 291 struct pmc_data *pmcd; 292 struct pmc_tstamp *pmct; 293 int *ip; 294 int rv; 295 296 switch(cmd) { 297 case PMIOSETUP: 298 if (!(flags & FWRITE)) 299 return EPERM; 300 pmc = (struct pmc *)param; 301 302 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val); 303 if (!rv) { 304 writerpmc |= (1 << pmc->pmc_num); 305 } 306 break; 307 308 case PMIOGET: 309 pmc = (struct pmc *)param; 310 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val); 311 break; 312 313 case PMIOSTART: 314 if (!(flags & FWRITE)) 315 return EPERM; 316 317 ip = (int *)param; 318 rv = perfmon_start(*ip); 319 break; 320 321 case PMIOSTOP: 322 if (!(flags & FWRITE)) 323 return EPERM; 324 325 ip = (int *)param; 326 rv = perfmon_stop(*ip); 327 break; 328 329 case PMIORESET: 330 if (!(flags & FWRITE)) 331 return EPERM; 332 333 ip = (int *)param; 334 rv = perfmon_reset(*ip); 335 break; 336 337 case PMIOREAD: 338 pmcd = (struct pmc_data *)param; 339 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value); 340 break; 341 342 case PMIOTSTAMP: 343 pmct = (struct pmc_tstamp *)param; 344 /* XXX interface loses precision. */ 345 pmct->pmct_rate = i586_ctr_freq / 1000000; 346 pmct->pmct_value = rdtsc(); 347 rv = 0; 348 break; 349 350 default: 351 rv = ENOTTY; 352 } 353 354 return rv; 355} 356