perfmon.c revision 14825
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$ 30 */ 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/fcntl.h> 35#include <sys/ioccom.h> 36 37#include <machine/cpu.h> 38#include <machine/cputypes.h> 39#include <machine/clock.h> 40#include <machine/perfmon.h> 41 42static int perfmon_inuse; 43static int perfmon_cpuok; 44static int msr_ctl[NPMC]; 45static int msr_pmc[NPMC]; 46 47/* 48 * Must be called after cpu_class is set up. 49 */ 50void 51perfmon_init(void) 52{ 53 switch(cpu_class) { 54 case CPUCLASS_586: /* assume it's the same for now */ 55 case CPUCLASS_686: 56 perfmon_cpuok = 1; 57 msr_ctl[0] = 0x186; 58 msr_ctl[1] = 0x187; 59 msr_pmc[0] = 0xc1; 60 msr_pmc[1] = 0xc2; 61 break; 62 63 default: 64 perfmon_cpuok = 0; 65 break; 66 } 67} 68 69int 70perfmon_avail(void) 71{ 72 return perfmon_cpuok; 73} 74 75int 76perfmon_setup(int pmc, unsigned int control) 77{ 78 if (pmc < 0 || pmc >= NPMC) 79 return EINVAL; 80 81 perfmon_inuse |= (1 << pmc); 82 control &= ~(PMCF_SYS_FLAGS << 16); 83 wrmsr(msr_ctl[pmc], control); 84 wrmsr(msr_pmc[pmc], 0); 85 return 0; 86} 87 88int 89perfmon_get(int pmc, unsigned int *control) 90{ 91 if (pmc < 0 || pmc >= NPMC) 92 return EINVAL; 93 94 if (perfmon_inuse & (1 << pmc)) { 95 *control = rdmsr(msr_ctl[pmc]); 96 return 0; 97 } 98 return EBUSY; /* XXX reversed sense */ 99} 100 101int 102perfmon_fini(int pmc) 103{ 104 if (pmc < 0 || pmc >= NPMC) 105 return EINVAL; 106 107 if (perfmon_inuse & (1 << pmc)) { 108 perfmon_stop(pmc); 109 perfmon_inuse &= ~(1 << pmc); 110 return 0; 111 } 112 return EBUSY; /* XXX reversed sense */ 113} 114 115int 116perfmon_start(int pmc) 117{ 118 /* 119 * XXX - Current Intel design does not allow counters to be enabled 120 * independently. 121 */ 122 if (pmc != PMC_ALL) 123 return EINVAL; 124 125#if 0 126 if (perfmon_inuse & (1 << pmc)) { 127 wrmsr(msr_ctl[pmc], rdmsr(msr_ctl[pmc]) | (PMCF_EN << 16)); 128 return 0; 129 } 130#else 131 if (perfmon_inuse) { 132 wrmsr(msr_ctl[0], rdmsr(msr_ctl[0]) | (PMCF_EN << 16)); 133 return 0; 134 } 135#endif 136 return EBUSY; 137} 138 139int 140perfmon_stop(int pmc) 141{ 142 /* 143 * XXX - Current Intel design does not allow counters to be enabled 144 * independently. 145 */ 146 if (pmc != PMC_ALL) 147 return EINVAL; 148 149#if 0 150 if (perfmon_inuse & (1 << pmc)) { 151 wrmsr(msr_ctl[pmc], rdmsr(msr_ctl[pmc]) & ~(PMCF_EN << 16)); 152 return 0; 153 } 154#else 155 if (perfmon_inuse) { 156 wrmsr(msr_ctl[0], rdmsr(msr_ctl[0]) & ~(PMCF_EN << 16)); 157 return 0; 158 } 159#endif 160 return EBUSY; 161} 162 163int 164perfmon_read(int pmc, quad_t *val) 165{ 166 if (pmc < 0 || pmc >= NPMC) 167 return EINVAL; 168 169 if (perfmon_inuse & (1 << pmc)) { 170 *val = rdmsr(msr_pmc[pmc]); 171 return 0; 172 } 173 174 return EBUSY; 175} 176 177int 178perfmon_reset(int pmc) 179{ 180 if (pmc < 0 || pmc >= NPMC) 181 return EINVAL; 182 183 if (perfmon_inuse & (1 << pmc)) { 184 wrmsr(msr_pmc[pmc], 0); 185 return 0; 186 } 187 return EBUSY; 188} 189 190/* 191 * Now the user-mode interface, called from a subdevice of mem.c. 192 */ 193static int writer; 194static int writerpmc; 195 196int 197perfmon_open(dev_t dev, int flags, int fmt, struct proc *p) 198{ 199 if (!perfmon_cpuok) 200 return ENXIO; 201 202 if (flags & FWRITE) { 203 if (writer) { 204 return EBUSY; 205 } else { 206 writer = 1; 207 writerpmc = 0; 208 } 209 } 210 return 0; 211} 212 213int 214perfmon_close(dev_t dev, int flags, int fmt, struct proc *p) 215{ 216 if (flags & FWRITE) { 217 int i; 218 219 for (i = 0; i < NPMC; i++) { 220 if (writerpmc & (1 << i)) 221 perfmon_fini(i); 222 } 223 writer = 0; 224 } 225 return 0; 226} 227 228int 229perfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p) 230{ 231 struct pmc *pmc; 232 struct pmc_data *pmcd; 233 struct pmc_tstamp *pmct; 234 int *ip; 235 int rv; 236 237 switch(cmd) { 238 case PMIOSETUP: 239 if (!(flags & FWRITE)) 240 return EPERM; 241 pmc = (struct pmc *)param; 242 243 rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val); 244 if (!rv) { 245 writerpmc |= (1 << pmc->pmc_num); 246 } 247 break; 248 249 case PMIOGET: 250 pmc = (struct pmc *)param; 251 rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val); 252 break; 253 254 case PMIOSTART: 255 if (!(flags & FWRITE)) 256 return EPERM; 257 258 ip = (int *)param; 259 rv = perfmon_start(*ip); 260 break; 261 262 case PMIOSTOP: 263 if (!(flags & FWRITE)) 264 return EPERM; 265 266 ip = (int *)param; 267 rv = perfmon_stop(*ip); 268 break; 269 270 case PMIORESET: 271 if (!(flags & FWRITE)) 272 return EPERM; 273 274 ip = (int *)param; 275 rv = perfmon_reset(*ip); 276 break; 277 278 case PMIOREAD: 279 pmcd = (struct pmc_data *)param; 280 rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value); 281 break; 282 283 case PMIOTSTAMP: 284 pmct = (struct pmc_tstamp *)param; 285 pmct->pmct_rate = i586_ctr_rate >> I586_CTR_RATE_SHIFT; 286 pmct->pmct_value = rdtsc(); 287 rv = 0; 288 break; 289 290 default: 291 rv = ENOTTY; 292 } 293 294 return rv; 295} 296