perfmon.c revision 14888
1139825Simp/* 299663Sbenno * Copyright 1996 Massachusetts Institute of Technology 399663Sbenno * 499663Sbenno * Permission to use, copy, modify, and distribute this software and 599663Sbenno * its documentation for any purpose and without fee is hereby 699663Sbenno * granted, provided that both the above copyright notice and this 799663Sbenno * permission notice appear in all copies, that both the above 899663Sbenno * copyright notice and this permission notice appear in all 999663Sbenno * supporting documentation, and that the name of M.I.T. not be used 1099663Sbenno * in advertising or publicity pertaining to distribution of the 1199663Sbenno * software without specific, written prior permission. M.I.T. makes 1299663Sbenno * no representations about the suitability of this software for any 1399663Sbenno * purpose. It is provided "as is" without express or implied 1499663Sbenno * warranty. 1599663Sbenno * 1699663Sbenno * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 1799663Sbenno * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 1899663Sbenno * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 1999663Sbenno * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 2099663Sbenno * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2199663Sbenno * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2299663Sbenno * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2399663Sbenno * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2499663Sbenno * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2599663Sbenno * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2699663Sbenno * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2799663Sbenno * SUCH DAMAGE. 2899663Sbenno * 2999663Sbenno * $Id: perfmon.c,v 1.2 1996/03/27 22:02:18 wollman Exp $ 30131102Sgrehan */ 3199663Sbenno 3299663Sbenno#include <sys/param.h> 3399663Sbenno#include <sys/systm.h> 3499663Sbenno#include <sys/fcntl.h> 3599663Sbenno#include <sys/ioccom.h> 3699663Sbenno 37183882Snwhitehorn#include <machine/cpu.h> 38186128Snwhitehorn#include <machine/cputypes.h> 3999663Sbenno#include <machine/clock.h> 4099663Sbenno#include <machine/perfmon.h> 4199663Sbenno 4299663Sbennostatic int perfmon_inuse; 4399663Sbennostatic int perfmon_cpuok; 44208149Snwhitehornstatic int msr_ctl[NPMC]; 4599663Sbennostatic int msr_pmc[NPMC]; 46174782Smarcelstatic unsigned int ctl_shadow[NPMC]; 4799663Sbennostatic quad_t pmc_shadow[NPMC]; /* used when ctr is stopped on P5 */ 4899663Sbennostatic int (*writectl)(int); 4999663Sbennostatic int writectl5(int); 5099663Sbennostatic int writectl6(int); 5199663Sbenno 5299663Sbenno/* 5399663Sbenno * Must be called after cpu_class is set up. 5499663Sbenno */ 5599663Sbennovoid 56208149Snwhitehornperfmon_init(void) 57208149Snwhitehorn{ 58208149Snwhitehorn switch(cpu_class) { 5999663Sbenno case CPUCLASS_586: 60208149Snwhitehorn perfmon_cpuok = 1; 6199663Sbenno msr_ctl[0] = 0x11; 6299663Sbenno msr_ctl[1] = 0x11; 6399663Sbenno msr_pmc[0] = 0x12; 6499663Sbenno msr_pmc[1] = 0x13; 6599663Sbenno writectl = writectl5; 66208149Snwhitehorn break; 67208149Snwhitehorn case CPUCLASS_686: 68208149Snwhitehorn perfmon_cpuok = 1; 6999663Sbenno msr_ctl[0] = 0x186; 7099663Sbenno msr_ctl[1] = 0x187; 7199663Sbenno msr_pmc[0] = 0xc1; 72208149Snwhitehorn msr_pmc[1] = 0xc2; 73208149Snwhitehorn writectl = writectl6; 74208149Snwhitehorn break; 75294883Sjhibbits 76294883Sjhibbits default: 77208149Snwhitehorn perfmon_cpuok = 0; 78208149Snwhitehorn break; 79208149Snwhitehorn } 80208149Snwhitehorn} 81208149Snwhitehorn 82208149Snwhitehornint 83208149Snwhitehornperfmon_avail(void) 8499663Sbenno{ 8599663Sbenno return perfmon_cpuok; 86183882Snwhitehorn} 87183882Snwhitehorn 88183882Snwhitehornint 89208149Snwhitehornperfmon_setup(int pmc, unsigned int control) 90183882Snwhitehorn{ 91183882Snwhitehorn if (pmc < 0 || pmc >= NPMC) 92208149Snwhitehorn return EINVAL; 9399663Sbenno 9499663Sbenno perfmon_inuse |= (1 << pmc); 95208149Snwhitehorn control &= ~(PMCF_SYS_FLAGS << 16); 96208149Snwhitehorn disable_intr(); 97208149Snwhitehorn ctl_shadow[pmc] = control; 9899663Sbenno writectl(pmc); 9999663Sbenno wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 10099663Sbenno enable_intr(); 101208149Snwhitehorn return 0; 102208149Snwhitehorn} 10399663Sbenno 104208149Snwhitehornint 105208149Snwhitehornperfmon_get(int pmc, unsigned int *control) 10699663Sbenno{ 10799663Sbenno if (pmc < 0 || pmc >= NPMC) 108208149Snwhitehorn return EINVAL; 109208149Snwhitehorn 110208149Snwhitehorn if (perfmon_inuse & (1 << pmc)) { 111208149Snwhitehorn *control = ctl_shadow[pmc]; 11299663Sbenno return 0; 113208149Snwhitehorn } 114208149Snwhitehorn return EBUSY; /* XXX reversed sense */ 115208149Snwhitehorn} 116208149Snwhitehorn 117208149Snwhitehornint 11899663Sbennoperfmon_fini(int pmc) 119208871Snwhitehorn{ 120208871Snwhitehorn if (pmc < 0 || pmc >= NPMC) 121208149Snwhitehorn return EINVAL; 122208149Snwhitehorn 123208149Snwhitehorn if (perfmon_inuse & (1 << pmc)) { 124208149Snwhitehorn perfmon_stop(pmc); 125208149Snwhitehorn ctl_shadow[pmc] = 0; 126208149Snwhitehorn perfmon_inuse &= ~(1 << pmc); 127208149Snwhitehorn return 0; 128183882Snwhitehorn } 12999663Sbenno return EBUSY; /* XXX reversed sense */ 13099663Sbenno} 13199663Sbenno 132208149Snwhitehornint 133208149Snwhitehornperfmon_start(int pmc) 134208149Snwhitehorn{ 135208149Snwhitehorn if (pmc < 0 || pmc >= NPMC) 13699663Sbenno return EINVAL; 13799663Sbenno 138208149Snwhitehorn if (perfmon_inuse & (1 << pmc)) { 13999663Sbenno disable_intr(); 140259284Sjhibbits ctl_shadow[pmc] |= (PMCF_EN << 16); 141259284Sjhibbits wrmsr(msr_pmc[pmc], pmc_shadow[pmc]); 142259284Sjhibbits writectl(pmc); 143259284Sjhibbits enable_intr(); 144259284Sjhibbits return 0; 145259284Sjhibbits } 146259284Sjhibbits return EBUSY; 147261513Snwhitehorn} 14899663Sbenno 149208149Snwhitehornint 150208149Snwhitehornperfmon_stop(int pmc) 151208149Snwhitehorn{ 152208149Snwhitehorn if (pmc < 0 || pmc >= NPMC) 153208149Snwhitehorn return EINVAL; 15499663Sbenno 155209298Snwhitehorn if (perfmon_inuse & (1 << pmc)) { 156208149Snwhitehorn disable_intr(); 157208149Snwhitehorn pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]); 158208149Snwhitehorn ctl_shadow[pmc] &= ~(PMCF_EN << 16); 15999663Sbenno writectl(pmc); 160208149Snwhitehorn enable_intr(); 161208149Snwhitehorn return 0; 162208149Snwhitehorn } 163208149Snwhitehorn return EBUSY; 16499663Sbenno} 165208149Snwhitehorn 166208149Snwhitehornint 167208149Snwhitehornperfmon_read(int pmc, quad_t *val) 168208149Snwhitehorn{ 169208149Snwhitehorn if (pmc < 0 || pmc >= NPMC) 170208149Snwhitehorn return EINVAL; 171208149Snwhitehorn 172208149Snwhitehorn if (perfmon_inuse & (1 << pmc)) { 17399663Sbenno if (ctl_shadow[pmc] & (PMCF_EN << 16)) 174208149Snwhitehorn *val = rdmsr(msr_pmc[pmc]); 175208149Snwhitehorn else 17699663Sbenno *val = pmc_shadow[pmc]; 177208149Snwhitehorn return 0; 178208149Snwhitehorn } 179208149Snwhitehorn 180208149Snwhitehorn return EBUSY; 181208149Snwhitehorn} 182208149Snwhitehorn 183208149Snwhitehornint 184208149Snwhitehornperfmon_reset(int pmc) 185208149Snwhitehorn{ 186218184Smarcel if (pmc < 0 || pmc >= NPMC) 187218184Smarcel return EINVAL; 188208149Snwhitehorn 189218184Smarcel if (perfmon_inuse & (1 << pmc)) { 190208149Snwhitehorn wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 191209298Snwhitehorn return 0; 192218184Smarcel } 193209298Snwhitehorn return EBUSY; 194209299Snwhitehorn} 195209298Snwhitehorn 196209298Snwhitehorn/* 197218184Smarcel * Unfortunately, the performance-monitoring registers are laid out 198208149Snwhitehorn * differently in the P5 and P6. We keep everything in P6 format 199183882Snwhitehorn * internally (except for the event code), and convert to P5 20099663Sbenno * format as needed on those CPUs. The writectl function pointer 20199663Sbenno * is set up to point to one of these functions by perfmon_init(). 202208149Snwhitehorn */ 203208149Snwhitehornint 20499663Sbennowritectl6(int pmc) 205208149Snwhitehorn{ 206208149Snwhitehorn if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) { 20799663Sbenno wrmsr(msr_ctl[pmc], 0); 208208149Snwhitehorn } else { 209208149Snwhitehorn wrmsr(msr_ctl[pmc], ctl_shadow[pmc]); 210208149Snwhitehorn } 21199663Sbenno return 0; 212208149Snwhitehorn} 213208149Snwhitehorn 214208149Snwhitehorn#define P5FLAG_P 0x200 215208149Snwhitehorn#define P5FLAG_E 0x100 216208149Snwhitehorn#define P5FLAG_USR 0x80 217208149Snwhitehorn#define P5FLAG_OS 0x40 218208149Snwhitehorn 21999663Sbennoint 220208149Snwhitehornwritectl5(int pmc) 221259284Sjhibbits{ 222208149Snwhitehorn quad_t newval = 0; 223259284Sjhibbits quad_t oldtsc; 224208149Snwhitehorn 225208149Snwhitehorn if (ctl_shadow[1] & (PMCF_EN << 16)) { 22699663Sbenno if (ctl_shadow[1] & (PMCF_USR << 16)) 227208149Snwhitehorn newval |= P5FLAG_USR << 16; 228259284Sjhibbits if (ctl_shadow[1] & (PMCF_OS << 16)) 229259284Sjhibbits newval |= P5FLAG_OS << 16; 230259284Sjhibbits if (ctl_shadow[1] & (PMCF_E << 16)) 231259284Sjhibbits newval |= P5FLAG_E << 16; 232259284Sjhibbits newval |= (ctl_shadow[1] & 0x3f) << 16; 233208149Snwhitehorn } 234183882Snwhitehorn if (ctl_shadow[0] & (PMCF_EN << 16)) { 235208149Snwhitehorn if (ctl_shadow[0] & (PMCF_USR << 16)) 236259284Sjhibbits newval |= P5FLAG_USR; 237259284Sjhibbits if (ctl_shadow[0] & (PMCF_OS << 16)) 238259284Sjhibbits newval |= P5FLAG_OS; 239259284Sjhibbits if (ctl_shadow[0] & (PMCF_E << 16)) 240259284Sjhibbits newval |= P5FLAG_E; 241259284Sjhibbits newval |= ctl_shadow[0] & 0x3f; 242208149Snwhitehorn } 243208149Snwhitehorn /* 244259284Sjhibbits * ``...But this is the blackest of sins!'' 245208149Snwhitehorn * 24699663Sbenno * According to the Harvard code, it is necessary to zero the 247208149Snwhitehorn * cycle counter before writing to the control MSR. This must 248208149Snwhitehorn * be an Intel processor... Hope we don't lose too many ticks. 249208149Snwhitehorn */ 250208149Snwhitehorn disable_intr(); 251183882Snwhitehorn oldtsc = rdtsc(); 252208149Snwhitehorn wrmsr(0x10 /* TSC */, 0); 253183882Snwhitehorn wrmsr(msr_ctl[0], newval); 254208149Snwhitehorn wrmsr(0x10, oldtsc); 25599663Sbenno enable_intr(); 25699663Sbenno return 0; /* XXX should check for errors */ 257208149Snwhitehorn} 258208149Snwhitehorn 259208149Snwhitehorn/* 26099663Sbenno * Now the user-mode interface, called from a subdevice of mem.c. 261208149Snwhitehorn */ 262208149Snwhitehornstatic int writer; 263208149Snwhitehornstatic int writerpmc; 26499663Sbenno 265208149Snwhitehornint 266208149Snwhitehornperfmon_open(dev_t dev, int flags, int fmt, struct proc *p) 267208149Snwhitehorn{ 268208149Snwhitehorn if (!perfmon_cpuok) 269208149Snwhitehorn return ENXIO; 270208149Snwhitehorn 271208149Snwhitehorn if (flags & FWRITE) { 272233188Sandreast if (writer) { 273208149Snwhitehorn return EBUSY; 274258272Snwhitehorn } else { 275208149Snwhitehorn writer = 1; 276233188Sandreast writerpmc = 0; 277233188Sandreast } 278208149Snwhitehorn } 279208149Snwhitehorn return 0; 280208149Snwhitehorn} 281208149Snwhitehorn 282208149Snwhitehornint 283208149Snwhitehornperfmon_close(dev_t dev, int flags, int fmt, struct proc *p) 28499663Sbenno{ 28599663Sbenno if (flags & FWRITE) { 286258272Snwhitehorn int i; 287258272Snwhitehorn 288258272Snwhitehorn for (i = 0; i < NPMC; i++) { 289208149Snwhitehorn if (writerpmc & (1 << i)) 290258272Snwhitehorn perfmon_fini(i); 291258272Snwhitehorn } 292258272Snwhitehorn writer = 0; 293258272Snwhitehorn } 294258272Snwhitehorn return 0; 295258272Snwhitehorn} 296258272Snwhitehorn 297258272Snwhitehornint 298258272Snwhitehornperfmon_ioctl(dev_t dev, int cmd, caddr_t param, int flags, struct proc *p) 299258272Snwhitehorn{ 300258272Snwhitehorn struct pmc *pmc; 301208149Snwhitehorn struct pmc_data *pmcd; 30299663Sbenno struct pmc_tstamp *pmct; 303208149Snwhitehorn int *ip; 304208149Snwhitehorn int rv; 305179746Skevlo 306208149Snwhitehorn switch(cmd) { 307179746Skevlo case PMIOSETUP: 308179746Skevlo if (!(flags & FWRITE)) 309179746Skevlo return EPERM; 31099663Sbenno pmc = (struct pmc *)param; 311208149Snwhitehorn 312208149Snwhitehorn rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val); 313208149Snwhitehorn if (!rv) { 314208149Snwhitehorn writerpmc |= (1 << pmc->pmc_num); 315208149Snwhitehorn } 316208149Snwhitehorn break; 317208149Snwhitehorn 318208149Snwhitehorn case PMIOGET: 319208149Snwhitehorn pmc = (struct pmc *)param; 320208149Snwhitehorn rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val); 321259284Sjhibbits break; 322259284Sjhibbits 323259284Sjhibbits case PMIOSTART: 324208149Snwhitehorn if (!(flags & FWRITE)) 325208149Snwhitehorn return EPERM; 326208149Snwhitehorn 327208149Snwhitehorn ip = (int *)param; 328208149Snwhitehorn rv = perfmon_start(*ip); 329208149Snwhitehorn break; 330208149Snwhitehorn 331208149Snwhitehorn case PMIOSTOP: 332208149Snwhitehorn if (!(flags & FWRITE)) 333208149Snwhitehorn return EPERM; 33499663Sbenno 335208149Snwhitehorn ip = (int *)param; 336208149Snwhitehorn rv = perfmon_stop(*ip); 337208149Snwhitehorn break; 338208149Snwhitehorn 339208149Snwhitehorn case PMIORESET: 340233188Sandreast if (!(flags & FWRITE)) 341233188Sandreast return EPERM; 342233188Sandreast 343233188Sandreast ip = (int *)param; 344233188Sandreast rv = perfmon_reset(*ip); 345233188Sandreast break; 346233188Sandreast 347233188Sandreast case PMIOREAD: 348233188Sandreast pmcd = (struct pmc_data *)param; 349233188Sandreast rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value); 350233188Sandreast break; 351233188Sandreast 352233188Sandreast case PMIOTSTAMP: 353233188Sandreast pmct = (struct pmc_tstamp *)param; 354233188Sandreast pmct->pmct_rate = i586_ctr_rate >> I586_CTR_RATE_SHIFT; 355233188Sandreast pmct->pmct_value = rdtsc(); 356233188Sandreast rv = 0; 357233188Sandreast break; 358233188Sandreast 359233188Sandreast default: 360233188Sandreast rv = ENOTTY; 361233188Sandreast } 362233188Sandreast 363233188Sandreast return rv; 364233188Sandreast} 365233188Sandreast