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