perfmon.c revision 42543
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.16 1998/12/07 21:58:18 archie Exp $ 30 */ 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/fcntl.h> 35 36#ifndef SMP 37#include <machine/cputypes.h> 38#endif 39#include <machine/clock.h> 40#include <machine/perfmon.h> 41 42static int perfmon_inuse; 43static int perfmon_cpuok; 44#ifndef SMP 45static int msr_ctl[NPMC]; 46#endif 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); 51#ifndef SMP 52static int writectl5(int); 53static int writectl6(int); 54#endif 55 56/* 57 * Must be called after cpu_class is set up. 58 */ 59void 60perfmon_init(void) 61{ 62#ifndef SMP 63 switch(cpu_class) { 64 case CPUCLASS_586: 65 perfmon_cpuok = 1; 66 msr_ctl[0] = 0x11; 67 msr_ctl[1] = 0x11; 68 msr_pmc[0] = 0x12; 69 msr_pmc[1] = 0x13; 70 writectl = writectl5; 71 break; 72 case CPUCLASS_686: 73 perfmon_cpuok = 1; 74 msr_ctl[0] = 0x186; 75 msr_ctl[1] = 0x187; 76 msr_pmc[0] = 0xc1; 77 msr_pmc[1] = 0xc2; 78 writectl = writectl6; 79 break; 80 81 default: 82 perfmon_cpuok = 0; 83 break; 84 } 85#endif /* SMP */ 86} 87 88int 89perfmon_avail(void) 90{ 91 return perfmon_cpuok; 92} 93 94int 95perfmon_setup(int pmc, unsigned int control) 96{ 97 if (pmc < 0 || pmc >= NPMC) 98 return EINVAL; 99 100 perfmon_inuse |= (1 << pmc); 101 control &= ~(PMCF_SYS_FLAGS << 16); 102 disable_intr(); 103 ctl_shadow[pmc] = control; 104 writectl(pmc); 105 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 106 enable_intr(); 107 return 0; 108} 109 110int 111perfmon_get(int pmc, unsigned int *control) 112{ 113 if (pmc < 0 || pmc >= NPMC) 114 return EINVAL; 115 116 if (perfmon_inuse & (1 << pmc)) { 117 *control = ctl_shadow[pmc]; 118 return 0; 119 } 120 return EBUSY; /* XXX reversed sense */ 121} 122 123int 124perfmon_fini(int pmc) 125{ 126 if (pmc < 0 || pmc >= NPMC) 127 return EINVAL; 128 129 if (perfmon_inuse & (1 << pmc)) { 130 perfmon_stop(pmc); 131 ctl_shadow[pmc] = 0; 132 perfmon_inuse &= ~(1 << pmc); 133 return 0; 134 } 135 return EBUSY; /* XXX reversed sense */ 136} 137 138int 139perfmon_start(int pmc) 140{ 141 if (pmc < 0 || pmc >= NPMC) 142 return EINVAL; 143 144 if (perfmon_inuse & (1 << pmc)) { 145 disable_intr(); 146 ctl_shadow[pmc] |= (PMCF_EN << 16); 147 wrmsr(msr_pmc[pmc], pmc_shadow[pmc]); 148 writectl(pmc); 149 enable_intr(); 150 return 0; 151 } 152 return EBUSY; 153} 154 155int 156perfmon_stop(int pmc) 157{ 158 if (pmc < 0 || pmc >= NPMC) 159 return EINVAL; 160 161 if (perfmon_inuse & (1 << pmc)) { 162 disable_intr(); 163 pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]); 164 ctl_shadow[pmc] &= ~(PMCF_EN << 16); 165 writectl(pmc); 166 enable_intr(); 167 return 0; 168 } 169 return EBUSY; 170} 171 172int 173perfmon_read(int pmc, quad_t *val) 174{ 175 if (pmc < 0 || pmc >= NPMC) 176 return EINVAL; 177 178 if (perfmon_inuse & (1 << pmc)) { 179 if (ctl_shadow[pmc] & (PMCF_EN << 16)) 180 *val = rdmsr(msr_pmc[pmc]); 181 else 182 *val = pmc_shadow[pmc]; 183 return 0; 184 } 185 186 return EBUSY; 187} 188 189int 190perfmon_reset(int pmc) 191{ 192 if (pmc < 0 || pmc >= NPMC) 193 return EINVAL; 194 195 if (perfmon_inuse & (1 << pmc)) { 196 wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 197 return 0; 198 } 199 return EBUSY; 200} 201 202#ifndef SMP 203/* 204 * Unfortunately, the performance-monitoring registers are laid out 205 * differently in the P5 and P6. We keep everything in P6 format 206 * internally (except for the event code), and convert to P5 207 * format as needed on those CPUs. The writectl function pointer 208 * is set up to point to one of these functions by perfmon_init(). 209 */ 210int 211writectl6(int pmc) 212{ 213 if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) { 214 wrmsr(msr_ctl[pmc], 0); 215 } else { 216 wrmsr(msr_ctl[pmc], ctl_shadow[pmc]); 217 } 218 return 0; 219} 220 221#define P5FLAG_P 0x200 222#define P5FLAG_E 0x100 223#define P5FLAG_USR 0x80 224#define P5FLAG_OS 0x40 225 226int 227writectl5(int pmc) 228{ 229 quad_t newval = 0; 230 231 if (ctl_shadow[1] & (PMCF_EN << 16)) { 232 if (ctl_shadow[1] & (PMCF_USR << 16)) 233 newval |= P5FLAG_USR << 16; 234 if (ctl_shadow[1] & (PMCF_OS << 16)) 235 newval |= P5FLAG_OS << 16; 236 if (!(ctl_shadow[1] & (PMCF_E << 16))) 237 newval |= P5FLAG_E << 16; 238 newval |= (ctl_shadow[1] & 0x3f) << 16; 239 } 240 if (ctl_shadow[0] & (PMCF_EN << 16)) { 241 if (ctl_shadow[0] & (PMCF_USR << 16)) 242 newval |= P5FLAG_USR; 243 if (ctl_shadow[0] & (PMCF_OS << 16)) 244 newval |= P5FLAG_OS; 245 if (!(ctl_shadow[0] & (PMCF_E << 16))) 246 newval |= P5FLAG_E; 247 newval |= ctl_shadow[0] & 0x3f; 248 } 249 250 wrmsr(msr_ctl[0], newval); 251 return 0; /* XXX should check for unimplemented bits */ 252} 253#endif /* !SMP */ 254 255/* 256 * Now the user-mode interface, called from a subdevice of mem.c. 257 */ 258static int writer; 259static int writerpmc; 260 261int 262perfmon_open(dev_t dev, int flags, int fmt, struct proc *p) 263{ 264 if (!perfmon_cpuok) 265 return ENXIO; 266 267 if (flags & FWRITE) { 268 if (writer) { 269 return EBUSY; 270 } else { 271 writer = 1; 272 writerpmc = 0; 273 } 274 } 275 return 0; 276} 277 278int 279perfmon_close(dev_t dev, int flags, int fmt, struct proc *p) 280{ 281 if (flags & FWRITE) { 282 int i; 283 284 for (i = 0; i < NPMC; i++) { 285 if (writerpmc & (1 << i)) 286 perfmon_fini(i); 287 } 288 writer = 0; 289 } 290 return 0; 291} 292 293int 294perfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p) 295{ 296 struct pmc *pmc; 297 struct pmc_data *pmcd; 298 struct pmc_tstamp *pmct; 299 int *ip; 300 int rv; 301 302 switch(cmd) { 303 case PMIOSETUP: 304 if (!(flags & FWRITE)) 305 return EPERM; 306 pmc = (struct pmc *)param; 307 308 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val); 309 if (!rv) { 310 writerpmc |= (1 << pmc->pmc_num); 311 } 312 break; 313 314 case PMIOGET: 315 pmc = (struct pmc *)param; 316 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val); 317 break; 318 319 case PMIOSTART: 320 if (!(flags & FWRITE)) 321 return EPERM; 322 323 ip = (int *)param; 324 rv = perfmon_start(*ip); 325 break; 326 327 case PMIOSTOP: 328 if (!(flags & FWRITE)) 329 return EPERM; 330 331 ip = (int *)param; 332 rv = perfmon_stop(*ip); 333 break; 334 335 case PMIORESET: 336 if (!(flags & FWRITE)) 337 return EPERM; 338 339 ip = (int *)param; 340 rv = perfmon_reset(*ip); 341 break; 342 343 case PMIOREAD: 344 pmcd = (struct pmc_data *)param; 345 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value); 346 break; 347 348 case PMIOTSTAMP: 349 if (!tsc_freq) { 350 rv = ENOTTY; 351 break; 352 } 353 pmct = (struct pmc_tstamp *)param; 354 /* XXX interface loses precision. */ 355 pmct->pmct_rate = tsc_freq / 1000000; 356 pmct->pmct_value = rdtsc(); 357 rv = 0; 358 break; 359 default: 360 rv = ENOTTY; 361 } 362 363 return rv; 364} 365