perfmon.c revision 74903
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 * $FreeBSD: head/sys/i386/i386/perfmon.c 74903 2001-03-28 03:06:10Z jhb $ 30 */ 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/conf.h> 35#include <sys/fcntl.h> 36 37#ifndef SMP 38#include <machine/cputypes.h> 39#endif 40#include <machine/clock.h> 41#include <machine/perfmon.h> 42 43static int perfmon_inuse; 44static int perfmon_cpuok; 45#ifndef SMP 46static int msr_ctl[NPMC]; 47#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 /* poll */ nopoll, 69 /* mmap */ nommap, 70 /* strategy */ nostrategy, 71 /* name */ "perfmon", 72 /* maj */ CDEV_MAJOR, 73 /* dump */ nodump, 74 /* psize */ nopsize, 75 /* flags */ 0, 76}; 77 78/* 79 * Must be called after cpu_class is set up. 80 */ 81void 82perfmon_init(void) 83{ 84#ifndef SMP 85 switch(cpu_class) { 86 case CPUCLASS_586: 87 perfmon_cpuok = 1; 88 msr_ctl[0] = 0x11; 89 msr_ctl[1] = 0x11; 90 msr_pmc[0] = 0x12; 91 msr_pmc[1] = 0x13; 92 writectl = writectl5; 93 break; 94 case CPUCLASS_686: 95 perfmon_cpuok = 1; 96 msr_ctl[0] = 0x186; 97 msr_ctl[1] = 0x187; 98 msr_pmc[0] = 0xc1; 99 msr_pmc[1] = 0xc2; 100 writectl = writectl6; 101 break; 102 103 default: 104 perfmon_cpuok = 0; 105 break; 106 } 107#endif /* SMP */ 108 make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon"); 109} 110 111int 112perfmon_avail(void) 113{ 114 return perfmon_cpuok; 115} 116 117int 118perfmon_setup(int pmc, unsigned int control) 119{ 120 critical_t savecrit; 121 122 if (pmc < 0 || pmc >= NPMC) 123 return EINVAL; 124 125 perfmon_inuse |= (1 << pmc); 126 control &= ~(PMCF_SYS_FLAGS << 16); 127 savecrit = critical_enter(); 128 ctl_shadow[pmc] = control; 129 writectl(pmc); 130 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 131 critical_exit(savecrit); 132 return 0; 133} 134 135int 136perfmon_get(int pmc, unsigned int *control) 137{ 138 if (pmc < 0 || pmc >= NPMC) 139 return EINVAL; 140 141 if (perfmon_inuse & (1 << pmc)) { 142 *control = ctl_shadow[pmc]; 143 return 0; 144 } 145 return EBUSY; /* XXX reversed sense */ 146} 147 148int 149perfmon_fini(int pmc) 150{ 151 if (pmc < 0 || pmc >= NPMC) 152 return EINVAL; 153 154 if (perfmon_inuse & (1 << pmc)) { 155 perfmon_stop(pmc); 156 ctl_shadow[pmc] = 0; 157 perfmon_inuse &= ~(1 << pmc); 158 return 0; 159 } 160 return EBUSY; /* XXX reversed sense */ 161} 162 163int 164perfmon_start(int pmc) 165{ 166 critical_t savecrit; 167 168 if (pmc < 0 || pmc >= NPMC) 169 return EINVAL; 170 171 if (perfmon_inuse & (1 << pmc)) { 172 savecrit = critical_enter(); 173 ctl_shadow[pmc] |= (PMCF_EN << 16); 174 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]); 175 writectl(pmc); 176 critical_exit(savecrit); 177 return 0; 178 } 179 return EBUSY; 180} 181 182int 183perfmon_stop(int pmc) 184{ 185 critical_t savecrit; 186 187 if (pmc < 0 || pmc >= NPMC) 188 return EINVAL; 189 190 if (perfmon_inuse & (1 << pmc)) { 191 savecrit = critical_enter(); 192 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL; 193 ctl_shadow[pmc] &= ~(PMCF_EN << 16); 194 writectl(pmc); 195 critical_exit(savecrit); 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