perfmon.c revision 92649
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 92649 2002-03-19 06:14:34Z alfred $ 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/* 62 * XXX perfmon_init_dev(void *) is a split from the perfmon_init() funtion. 63 * This solves a problem for DEVFS users. It loads the "perfmon" driver after 64 * the DEVFS subsystem has been kicked into action. The SI_ORDER_ANY is to 65 * assure that it is the most lowest priority task which, guarantees the 66 * above. 67 */ 68static void perfmon_init_dev __P((void *)); 69SYSINIT(cpu, SI_SUB_DRIVERS, SI_ORDER_ANY, perfmon_init_dev, NULL); 70 71#define CDEV_MAJOR 2 /* We're really a minor of mem.c */ 72static struct cdevsw perfmon_cdevsw = { 73 /* open */ perfmon_open, 74 /* close */ perfmon_close, 75 /* read */ noread, 76 /* write */ nowrite, 77 /* ioctl */ perfmon_ioctl, 78 /* poll */ nopoll, 79 /* mmap */ nommap, 80 /* strategy */ nostrategy, 81 /* name */ "perfmon", 82 /* maj */ CDEV_MAJOR, 83 /* dump */ nodump, 84 /* psize */ nopsize, 85 /* flags */ 0, 86}; 87 88/* 89 * Must be called after cpu_class is set up. 90 */ 91void 92perfmon_init(void) 93{ 94#ifndef SMP 95 switch(cpu_class) { 96 case CPUCLASS_586: 97 perfmon_cpuok = 1; 98 msr_ctl[0] = 0x11; 99 msr_ctl[1] = 0x11; 100 msr_pmc[0] = 0x12; 101 msr_pmc[1] = 0x13; 102 writectl = writectl5; 103 break; 104 case CPUCLASS_686: 105 perfmon_cpuok = 1; 106 msr_ctl[0] = 0x186; 107 msr_ctl[1] = 0x187; 108 msr_pmc[0] = 0xc1; 109 msr_pmc[1] = 0xc2; 110 writectl = writectl6; 111 break; 112 113 default: 114 perfmon_cpuok = 0; 115 break; 116 } 117#endif /* SMP */ 118} 119 120static void 121perfmon_init_dev(dummy) 122 void *dummy; 123{ 124 make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon"); 125} 126 127int 128perfmon_avail(void) 129{ 130 return perfmon_cpuok; 131} 132 133int 134perfmon_setup(int pmc, unsigned int control) 135{ 136 critical_t savecrit; 137 138 if (pmc < 0 || pmc >= NPMC) 139 return EINVAL; 140 141 perfmon_inuse |= (1 << pmc); 142 control &= ~(PMCF_SYS_FLAGS << 16); 143 savecrit = cpu_critical_enter(); 144 ctl_shadow[pmc] = control; 145 writectl(pmc); 146 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 147 cpu_critical_exit(savecrit); 148 return 0; 149} 150 151int 152perfmon_get(int pmc, unsigned int *control) 153{ 154 if (pmc < 0 || pmc >= NPMC) 155 return EINVAL; 156 157 if (perfmon_inuse & (1 << pmc)) { 158 *control = ctl_shadow[pmc]; 159 return 0; 160 } 161 return EBUSY; /* XXX reversed sense */ 162} 163 164int 165perfmon_fini(int pmc) 166{ 167 if (pmc < 0 || pmc >= NPMC) 168 return EINVAL; 169 170 if (perfmon_inuse & (1 << pmc)) { 171 perfmon_stop(pmc); 172 ctl_shadow[pmc] = 0; 173 perfmon_inuse &= ~(1 << pmc); 174 return 0; 175 } 176 return EBUSY; /* XXX reversed sense */ 177} 178 179int 180perfmon_start(int pmc) 181{ 182 critical_t savecrit; 183 184 if (pmc < 0 || pmc >= NPMC) 185 return EINVAL; 186 187 if (perfmon_inuse & (1 << pmc)) { 188 savecrit = cpu_critical_enter(); 189 ctl_shadow[pmc] |= (PMCF_EN << 16); 190 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]); 191 writectl(pmc); 192 cpu_critical_exit(savecrit); 193 return 0; 194 } 195 return EBUSY; 196} 197 198int 199perfmon_stop(int pmc) 200{ 201 critical_t savecrit; 202 203 if (pmc < 0 || pmc >= NPMC) 204 return EINVAL; 205 206 if (perfmon_inuse & (1 << pmc)) { 207 savecrit = cpu_critical_enter(); 208 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL; 209 ctl_shadow[pmc] &= ~(PMCF_EN << 16); 210 writectl(pmc); 211 cpu_critical_exit(savecrit); 212 return 0; 213 } 214 return EBUSY; 215} 216 217int 218perfmon_read(int pmc, quad_t *val) 219{ 220 if (pmc < 0 || pmc >= NPMC) 221 return EINVAL; 222 223 if (perfmon_inuse & (1 << pmc)) { 224 if (ctl_shadow[pmc] & (PMCF_EN << 16)) 225 *val = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL; 226 else 227 *val = pmc_shadow[pmc]; 228 return 0; 229 } 230 231 return EBUSY; 232} 233 234int 235perfmon_reset(int pmc) 236{ 237 if (pmc < 0 || pmc >= NPMC) 238 return EINVAL; 239 240 if (perfmon_inuse & (1 << pmc)) { 241 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 242 return 0; 243 } 244 return EBUSY; 245} 246 247#ifndef SMP 248/* 249 * Unfortunately, the performance-monitoring registers are laid out 250 * differently in the P5 and P6. We keep everything in P6 format 251 * internally (except for the event code), and convert to P5 252 * format as needed on those CPUs. The writectl function pointer 253 * is set up to point to one of these functions by perfmon_init(). 254 */ 255int 256writectl6(int pmc) 257{ 258 if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) { 259 wrmsr(msr_ctl[pmc], 0); 260 } else { 261 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]); 262 } 263 return 0; 264} 265 266#define P5FLAG_P 0x200 267#define P5FLAG_E 0x100 268#define P5FLAG_USR 0x80 269#define P5FLAG_OS 0x40 270 271int 272writectl5(int pmc) 273{ 274 quad_t newval = 0; 275 276 if (ctl_shadow[1] & (PMCF_EN << 16)) { 277 if (ctl_shadow[1] & (PMCF_USR << 16)) 278 newval |= P5FLAG_USR << 16; 279 if (ctl_shadow[1] & (PMCF_OS << 16)) 280 newval |= P5FLAG_OS << 16; 281 if (!(ctl_shadow[1] & (PMCF_E << 16))) 282 newval |= P5FLAG_E << 16; 283 newval |= (ctl_shadow[1] & 0x3f) << 16; 284 } 285 if (ctl_shadow[0] & (PMCF_EN << 16)) { 286 if (ctl_shadow[0] & (PMCF_USR << 16)) 287 newval |= P5FLAG_USR; 288 if (ctl_shadow[0] & (PMCF_OS << 16)) 289 newval |= P5FLAG_OS; 290 if (!(ctl_shadow[0] & (PMCF_E << 16))) 291 newval |= P5FLAG_E; 292 newval |= ctl_shadow[0] & 0x3f; 293 } 294 295 wrmsr(msr_ctl[0], newval); 296 return 0; /* XXX should check for unimplemented bits */ 297} 298#endif /* !SMP */ 299 300/* 301 * Now the user-mode interface, called from a subdevice of mem.c. 302 */ 303static int writer; 304static int writerpmc; 305 306static int 307perfmon_open(dev_t dev, int flags, int fmt, struct thread *td) 308{ 309 if (!perfmon_cpuok) 310 return ENXIO; 311 312 if (flags & FWRITE) { 313 if (writer) { 314 return EBUSY; 315 } else { 316 writer = 1; 317 writerpmc = 0; 318 } 319 } 320 return 0; 321} 322 323static int 324perfmon_close(dev_t dev, int flags, int fmt, struct thread *td) 325{ 326 if (flags & FWRITE) { 327 int i; 328 329 for (i = 0; i < NPMC; i++) { 330 if (writerpmc & (1 << i)) 331 perfmon_fini(i); 332 } 333 writer = 0; 334 } 335 return 0; 336} 337 338static int 339perfmon_ioctl(dev_t dev, u_long cmd, caddr_t param, int flags, struct thread *td) 340{ 341 struct pmc *pmc; 342 struct pmc_data *pmcd; 343 struct pmc_tstamp *pmct; 344 int *ip; 345 int rv; 346 347 switch(cmd) { 348 case PMIOSETUP: 349 if (!(flags & FWRITE)) 350 return EPERM; 351 pmc = (struct pmc *)param; 352 353 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val); 354 if (!rv) { 355 writerpmc |= (1 << pmc->pmc_num); 356 } 357 break; 358 359 case PMIOGET: 360 pmc = (struct pmc *)param; 361 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val); 362 break; 363 364 case PMIOSTART: 365 if (!(flags & FWRITE)) 366 return EPERM; 367 368 ip = (int *)param; 369 rv = perfmon_start(*ip); 370 break; 371 372 case PMIOSTOP: 373 if (!(flags & FWRITE)) 374 return EPERM; 375 376 ip = (int *)param; 377 rv = perfmon_stop(*ip); 378 break; 379 380 case PMIORESET: 381 if (!(flags & FWRITE)) 382 return EPERM; 383 384 ip = (int *)param; 385 rv = perfmon_reset(*ip); 386 break; 387 388 case PMIOREAD: 389 pmcd = (struct pmc_data *)param; 390 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value); 391 break; 392 393 case PMIOTSTAMP: 394 if (!tsc_freq) { 395 rv = ENOTTY; 396 break; 397 } 398 pmct = (struct pmc_tstamp *)param; 399 /* XXX interface loses precision. */ 400 pmct->pmct_rate = tsc_freq / 1000000; 401 pmct->pmct_value = rdtsc(); 402 rv = 0; 403 break; 404 default: 405 rv = ENOTTY; 406 } 407 408 return rv; 409} 410