perfmon.c revision 50257
180434Schris/* 280434Schris * Copyright 1996 Massachusetts Institute of Technology 381278Sru * 480434Schris * Permission to use, copy, modify, and distribute this software and 580434Schris * its documentation for any purpose and without fee is hereby 680434Schris * granted, provided that both the above copyright notice and this 780434Schris * permission notice appear in all copies, that both the above 880434Schris * copyright notice and this permission notice appear in all 980434Schris * supporting documentation, and that the name of M.I.T. not be used 1080434Schris * in advertising or publicity pertaining to distribution of the 1180434Schris * software without specific, written prior permission. M.I.T. makes 1281278Sru * no representations about the suitability of this software for any 1380434Schris * purpose. It is provided "as is" without express or implied 1480434Schris * warranty. 1580434Schris * 1680434Schris * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1780434Schris * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1880434Schris * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1980434Schris * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2080434Schris * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2180434Schris * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2280434Schris * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2380434Schris * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2481278Sru * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25141580Sru * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2681278Sru * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2780434Schris * SUCH DAMAGE. 2880434Schris * 2980434Schris * $Id: perfmon.c,v 1.18 1999/05/11 01:54:52 alc Exp $ 3080434Schris */ 3180434Schris 32107788Sru#include <sys/param.h> 3380434Schris#include <sys/systm.h> 3488509Sdavidc#include <sys/conf.h> 3581278Sru#include <sys/fcntl.h> 3680434Schris 3780434Schris#ifndef SMP 3880434Schris#include <machine/cputypes.h> 3980434Schris#endif 4080434Schris#include <machine/clock.h> 4180434Schris#include <machine/perfmon.h> 4280434Schris 43107788Srustatic int perfmon_inuse; 4480434Schrisstatic int perfmon_cpuok; 4580434Schris#ifndef SMP 4680434Schrisstatic int msr_ctl[NPMC]; 4780434Schris#endif 48static int msr_pmc[NPMC]; 49static unsigned int ctl_shadow[NPMC]; 50static quad_t pmc_shadow[NPMC]; /* used when ctr is stopped on P5 */ 51static int (*writectl)(int); 52#ifndef SMP 53static int writectl5(int); 54static int writectl6(int); 55#endif 56 57static d_close_t perfmon_close; 58static d_open_t perfmon_open; 59static d_ioctl_t perfmon_ioctl; 60 61#define CDEV_MAJOR 2 /* We're really a minor of mem.c */ 62static struct cdevsw perfmon_cdevsw = { 63 /* open */ perfmon_open, 64 /* close */ perfmon_close, 65 /* read */ noread, 66 /* write */ nowrite, 67 /* ioctl */ perfmon_ioctl, 68 /* stop */ nostop, 69 /* reset */ noreset, 70 /* devtotty */ nodevtotty, 71 /* poll */ nopoll, 72 /* mmap */ nommap, 73 /* strategy */ nostrategy, 74 /* name */ "perfmon", 75 /* parms */ noparms, 76 /* maj */ CDEV_MAJOR, 77 /* dump */ nodump, 78 /* psize */ nopsize, 79 /* flags */ 0, 80 /* maxio */ 0, 81 /* bmaj */ -1 82}; 83 84/* 85 * Must be called after cpu_class is set up. 86 */ 87void 88perfmon_init(void) 89{ 90#ifndef SMP 91 switch(cpu_class) { 92 case CPUCLASS_586: 93 perfmon_cpuok = 1; 94 msr_ctl[0] = 0x11; 95 msr_ctl[1] = 0x11; 96 msr_pmc[0] = 0x12; 97 msr_pmc[1] = 0x13; 98 writectl = writectl5; 99 break; 100 case CPUCLASS_686: 101 perfmon_cpuok = 1; 102 msr_ctl[0] = 0x186; 103 msr_ctl[1] = 0x187; 104 msr_pmc[0] = 0xc1; 105 msr_pmc[1] = 0xc2; 106 writectl = writectl6; 107 break; 108 109 default: 110 perfmon_cpuok = 0; 111 break; 112 } 113#endif /* SMP */ 114 make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon"); 115} 116 117int 118perfmon_avail(void) 119{ 120 return perfmon_cpuok; 121} 122 123int 124perfmon_setup(int pmc, unsigned int control) 125{ 126 if (pmc < 0 || pmc >= NPMC) 127 return EINVAL; 128 129 perfmon_inuse |= (1 << pmc); 130 control &= ~(PMCF_SYS_FLAGS << 16); 131 disable_intr(); 132 ctl_shadow[pmc] = control; 133 writectl(pmc); 134 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 135 enable_intr(); 136 return 0; 137} 138 139int 140perfmon_get(int pmc, unsigned int *control) 141{ 142 if (pmc < 0 || pmc >= NPMC) 143 return EINVAL; 144 145 if (perfmon_inuse & (1 << pmc)) { 146 *control = ctl_shadow[pmc]; 147 return 0; 148 } 149 return EBUSY; /* XXX reversed sense */ 150} 151 152int 153perfmon_fini(int pmc) 154{ 155 if (pmc < 0 || pmc >= NPMC) 156 return EINVAL; 157 158 if (perfmon_inuse & (1 << pmc)) { 159 perfmon_stop(pmc); 160 ctl_shadow[pmc] = 0; 161 perfmon_inuse &= ~(1 << pmc); 162 return 0; 163 } 164 return EBUSY; /* XXX reversed sense */ 165} 166 167int 168perfmon_start(int pmc) 169{ 170 if (pmc < 0 || pmc >= NPMC) 171 return EINVAL; 172 173 if (perfmon_inuse & (1 << pmc)) { 174 disable_intr(); 175 ctl_shadow[pmc] |= (PMCF_EN << 16); 176 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]); 177 writectl(pmc); 178 enable_intr(); 179 return 0; 180 } 181 return EBUSY; 182} 183 184int 185perfmon_stop(int pmc) 186{ 187 if (pmc < 0 || pmc >= NPMC) 188 return EINVAL; 189 190 if (perfmon_inuse & (1 << pmc)) { 191 disable_intr(); 192 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL; 193 ctl_shadow[pmc] &= ~(PMCF_EN << 16); 194 writectl(pmc); 195 enable_intr(); 196 return 0; 197 } 198 return EBUSY; 199} 200 201int 202perfmon_read(int pmc, quad_t *val) 203{ 204 if (pmc < 0 || pmc >= NPMC) 205 return EINVAL; 206 207 if (perfmon_inuse & (1 << pmc)) { 208 if (ctl_shadow[pmc] & (PMCF_EN << 16)) 209 *val = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL; 210 else 211 *val = pmc_shadow[pmc]; 212 return 0; 213 } 214 215 return EBUSY; 216} 217 218int 219perfmon_reset(int pmc) 220{ 221 if (pmc < 0 || pmc >= NPMC) 222 return EINVAL; 223 224 if (perfmon_inuse & (1 << pmc)) { 225 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 226 return 0; 227 } 228 return EBUSY; 229} 230 231#ifndef SMP 232/* 233 * Unfortunately, the performance-monitoring registers are laid out 234 * differently in the P5 and P6. We keep everything in P6 format 235 * internally (except for the event code), and convert to P5 236 * format as needed on those CPUs. The writectl function pointer 237 * is set up to point to one of these functions by perfmon_init(). 238 */ 239int 240writectl6(int pmc) 241{ 242 if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) { 243 wrmsr(msr_ctl[pmc], 0); 244 } else { 245 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]); 246 } 247 return 0; 248} 249 250#define P5FLAG_P 0x200 251#define P5FLAG_E 0x100 252#define P5FLAG_USR 0x80 253#define P5FLAG_OS 0x40 254 255int 256writectl5(int pmc) 257{ 258 quad_t newval = 0; 259 260 if (ctl_shadow[1] & (PMCF_EN << 16)) { 261 if (ctl_shadow[1] & (PMCF_USR << 16)) 262 newval |= P5FLAG_USR << 16; 263 if (ctl_shadow[1] & (PMCF_OS << 16)) 264 newval |= P5FLAG_OS << 16; 265 if (!(ctl_shadow[1] & (PMCF_E << 16))) 266 newval |= P5FLAG_E << 16; 267 newval |= (ctl_shadow[1] & 0x3f) << 16; 268 } 269 if (ctl_shadow[0] & (PMCF_EN << 16)) { 270 if (ctl_shadow[0] & (PMCF_USR << 16)) 271 newval |= P5FLAG_USR; 272 if (ctl_shadow[0] & (PMCF_OS << 16)) 273 newval |= P5FLAG_OS; 274 if (!(ctl_shadow[0] & (PMCF_E << 16))) 275 newval |= P5FLAG_E; 276 newval |= ctl_shadow[0] & 0x3f; 277 } 278 279 wrmsr(msr_ctl[0], newval); 280 return 0; /* XXX should check for unimplemented bits */ 281} 282#endif /* !SMP */ 283 284/* 285 * Now the user-mode interface, called from a subdevice of mem.c. 286 */ 287static int writer; 288static int writerpmc; 289 290static int 291perfmon_open(dev_t dev, int flags, int fmt, struct proc *p) 292{ 293 if (!perfmon_cpuok) 294 return ENXIO; 295 296 if (flags & FWRITE) { 297 if (writer) { 298 return EBUSY; 299 } else { 300 writer = 1; 301 writerpmc = 0; 302 } 303 } 304 return 0; 305} 306 307static int 308perfmon_close(dev_t dev, int flags, int fmt, struct proc *p) 309{ 310 if (flags & FWRITE) { 311 int i; 312 313 for (i = 0; i < NPMC; i++) { 314 if (writerpmc & (1 << i)) 315 perfmon_fini(i); 316 } 317 writer = 0; 318 } 319 return 0; 320} 321 322static int 323perfmon_ioctl(dev_t dev, u_long cmd, caddr_t param, int flags, struct proc *p) 324{ 325 struct pmc *pmc; 326 struct pmc_data *pmcd; 327 struct pmc_tstamp *pmct; 328 int *ip; 329 int rv; 330 331 switch(cmd) { 332 case PMIOSETUP: 333 if (!(flags & FWRITE)) 334 return EPERM; 335 pmc = (struct pmc *)param; 336 337 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val); 338 if (!rv) { 339 writerpmc |= (1 << pmc->pmc_num); 340 } 341 break; 342 343 case PMIOGET: 344 pmc = (struct pmc *)param; 345 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val); 346 break; 347 348 case PMIOSTART: 349 if (!(flags & FWRITE)) 350 return EPERM; 351 352 ip = (int *)param; 353 rv = perfmon_start(*ip); 354 break; 355 356 case PMIOSTOP: 357 if (!(flags & FWRITE)) 358 return EPERM; 359 360 ip = (int *)param; 361 rv = perfmon_stop(*ip); 362 break; 363 364 case PMIORESET: 365 if (!(flags & FWRITE)) 366 return EPERM; 367 368 ip = (int *)param; 369 rv = perfmon_reset(*ip); 370 break; 371 372 case PMIOREAD: 373 pmcd = (struct pmc_data *)param; 374 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value); 375 break; 376 377 case PMIOTSTAMP: 378 if (!tsc_freq) { 379 rv = ENOTTY; 380 break; 381 } 382 pmct = (struct pmc_tstamp *)param; 383 /* XXX interface loses precision. */ 384 pmct->pmct_rate = tsc_freq / 1000000; 385 pmct->pmct_value = rdtsc(); 386 rv = 0; 387 break; 388 default: 389 rv = ENOTTY; 390 } 391 392 return rv; 393} 394