perfmon.c revision 220433
117209Swollman/*- 217209Swollman * Copyright 1996 Massachusetts Institute of Technology 317209Swollman * 455837Sjasone * Permission to use, copy, modify, and distribute this software and 555837Sjasone * its documentation for any purpose and without fee is hereby 617209Swollman * granted, provided that both the above copyright notice and this 715923Sscrappy * permission notice appear in all copies, that both the above 82708Swollman * copyright notice and this permission notice appear in all 92708Swollman * supporting documentation, and that the name of M.I.T. not be used 1017209Swollman * in advertising or publicity pertaining to distribution of the 112708Swollman * software without specific, written prior permission. M.I.T. makes 122708Swollman * no representations about the suitability of this software for any 132708Swollman * purpose. It is provided "as is" without express or implied 142708Swollman * warranty. 152708Swollman * 162708Swollman * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS 172708Swollman * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, 182708Swollman * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 192708Swollman * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT 202708Swollman * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 212708Swollman * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2271579Sdeischen * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 2318834Swollman * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 2418834Swollman * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2571579Sdeischen * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 2671579Sdeischen * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2771579Sdeischen * SUCH DAMAGE. 2871579Sdeischen */ 2918834Swollman 302708Swollman#include <sys/cdefs.h> 312708Swollman__FBSDID("$FreeBSD: head/sys/i386/i386/perfmon.c 220433 2011-04-07 23:28:28Z jkim $"); 3271579Sdeischen 3371579Sdeischen#include <sys/param.h> 3471579Sdeischen#include <sys/systm.h> 3571579Sdeischen#include <sys/conf.h> 3671579Sdeischen#include <sys/fcntl.h> 379936Swollman#include <sys/kernel.h> 389936Swollman 399936Swollman#ifndef SMP 402708Swollman#include <machine/cputypes.h> 412708Swollman#endif 422708Swollman#include <machine/clock.h> 432708Swollman#include <machine/perfmon.h> 442708Swollman#include <machine/specialreg.h> 452708Swollman 462708Swollmanstatic int perfmon_inuse; 472708Swollmanstatic int perfmon_cpuok; 482708Swollman#ifndef SMP 492708Swollmanstatic int msr_ctl[NPMC]; 502708Swollman#endif 512708Swollmanstatic int msr_pmc[NPMC]; 529936Swollmanstatic unsigned int ctl_shadow[NPMC]; 532708Swollmanstatic quad_t pmc_shadow[NPMC]; /* used when ctr is stopped on P5 */ 549936Swollmanstatic int (*writectl)(int); 552708Swollman#ifndef SMP 562708Swollmanstatic int writectl5(int); 572708Swollmanstatic int writectl6(int); 582708Swollman#endif 592708Swollman 602708Swollmanstatic d_close_t perfmon_close; 612708Swollmanstatic d_open_t perfmon_open; 622708Swollmanstatic d_ioctl_t perfmon_ioctl; 632708Swollman 642708Swollman/* 652708Swollman * XXX perfmon_init_dev(void *) is a split from the perfmon_init() funtion. 662708Swollman * This solves a problem for DEVFS users. It loads the "perfmon" driver after 672708Swollman * the DEVFS subsystem has been kicked into action. The SI_ORDER_ANY is to 682708Swollman * assure that it is the most lowest priority task which, guarantees the 692708Swollman * above. 702708Swollman */ 719936Swollmanstatic void perfmon_init_dev(void *); 722708SwollmanSYSINIT(cpu, SI_SUB_DRIVERS, SI_ORDER_ANY, perfmon_init_dev, NULL); 739936Swollman 749936Swollmanstatic struct cdevsw perfmon_cdevsw = { 752708Swollman .d_version = D_VERSION, 762708Swollman .d_flags = D_NEEDGIANT, 772708Swollman .d_open = perfmon_open, 782708Swollman .d_close = perfmon_close, 792708Swollman .d_ioctl = perfmon_ioctl, 809936Swollman .d_name = "perfmon", 812708Swollman}; 822708Swollman 832708Swollman/* 842708Swollman * Must be called after cpu_class is set up. 852708Swollman */ 862708Swollmanvoid 872708Swollmanperfmon_init(void) 882708Swollman{ 892708Swollman#ifndef SMP 902708Swollman switch(cpu_class) { 912708Swollman case CPUCLASS_586: 922708Swollman perfmon_cpuok = 1; 932708Swollman msr_ctl[0] = MSR_P5_CESR; 942708Swollman msr_ctl[1] = MSR_P5_CESR; 952708Swollman msr_pmc[0] = MSR_P5_CTR0; 962708Swollman msr_pmc[1] = MSR_P5_CTR1; 972708Swollman writectl = writectl5; 982708Swollman break; 992708Swollman case CPUCLASS_686: 1002708Swollman perfmon_cpuok = 1; 1012708Swollman msr_ctl[0] = MSR_EVNTSEL0; 1022708Swollman msr_ctl[1] = MSR_EVNTSEL1; 1032708Swollman msr_pmc[0] = MSR_PERFCTR0; 1042708Swollman msr_pmc[1] = MSR_PERFCTR1; 1059936Swollman writectl = writectl6; 1062708Swollman break; 1072708Swollman 1082708Swollman default: 1092708Swollman perfmon_cpuok = 0; 1102708Swollman break; 1112708Swollman } 1122708Swollman#endif /* SMP */ 1132708Swollman} 1142708Swollman 1152708Swollmanstatic void 1162708Swollmanperfmon_init_dev(dummy) 1172708Swollman void *dummy; 1182708Swollman{ 1192708Swollman make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon"); 1202708Swollman} 1212708Swollman 1222708Swollmanint 1232708Swollmanperfmon_avail(void) 1242708Swollman{ 1252708Swollman return perfmon_cpuok; 1262708Swollman} 1272708Swollman 1282708Swollmanint 1292708Swollmanperfmon_setup(int pmc, unsigned int control) 1302708Swollman{ 1312708Swollman register_t saveintr; 1322708Swollman 1332708Swollman if (pmc < 0 || pmc >= NPMC) 1342708Swollman return EINVAL; 1352708Swollman 1362708Swollman perfmon_inuse |= (1 << pmc); 1372708Swollman control &= ~(PMCF_SYS_FLAGS << 16); 1382708Swollman saveintr = intr_disable(); 1392708Swollman ctl_shadow[pmc] = control; 1402708Swollman writectl(pmc); 1412708Swollman wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 1429936Swollman intr_restore(saveintr); 1439936Swollman return 0; 1449936Swollman} 1452708Swollman 1469936Swollmanint 1479936Swollmanperfmon_get(int pmc, unsigned int *control) 1489936Swollman{ 1492708Swollman if (pmc < 0 || pmc >= NPMC) 1502708Swollman return EINVAL; 1512708Swollman 1522708Swollman if (perfmon_inuse & (1 << pmc)) { 1532708Swollman *control = ctl_shadow[pmc]; 1542708Swollman return 0; 1552708Swollman } 1562708Swollman return EBUSY; /* XXX reversed sense */ 1572708Swollman} 1582708Swollman 1592708Swollmanint 1602708Swollmanperfmon_fini(int pmc) 1612708Swollman{ 1622708Swollman if (pmc < 0 || pmc >= NPMC) 1632708Swollman return EINVAL; 1642708Swollman 1652708Swollman if (perfmon_inuse & (1 << pmc)) { 1662708Swollman perfmon_stop(pmc); 1672708Swollman ctl_shadow[pmc] = 0; 1682708Swollman perfmon_inuse &= ~(1 << pmc); 1692708Swollman return 0; 1702708Swollman } 1712708Swollman return EBUSY; /* XXX reversed sense */ 1729936Swollman} 1739936Swollman 1749936Swollmanint 1759936Swollmanperfmon_start(int pmc) 1769936Swollman{ 1772708Swollman register_t saveintr; 1782708Swollman 17971579Sdeischen if (pmc < 0 || pmc >= NPMC) 18071579Sdeischen return EINVAL; 1812708Swollman 1822708Swollman if (perfmon_inuse & (1 << pmc)) { 1839936Swollman saveintr = intr_disable(); 1849936Swollman ctl_shadow[pmc] |= (PMCF_EN << 16); 1852708Swollman wrmsr(msr_pmc[pmc], pmc_shadow[pmc]); 1862708Swollman writectl(pmc); 1879936Swollman intr_restore(saveintr); 1889936Swollman return 0; 1899936Swollman } 1909936Swollman return EBUSY; 1919936Swollman} 1929936Swollman 1939936Swollmanint 1949936Swollmanperfmon_stop(int pmc) 1959936Swollman{ 1969936Swollman register_t saveintr; 1972708Swollman 1982708Swollman if (pmc < 0 || pmc >= NPMC) 1992708Swollman return EINVAL; 2002708Swollman 2012708Swollman if (perfmon_inuse & (1 << pmc)) { 2022708Swollman saveintr = intr_disable(); 2032708Swollman pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL; 2042708Swollman ctl_shadow[pmc] &= ~(PMCF_EN << 16); 2052708Swollman writectl(pmc); 2062708Swollman intr_restore(saveintr); 2072708Swollman return 0; 2082708Swollman } 2092708Swollman return EBUSY; 21092889Sobrien} 21192889Sobrien 2122708Swollmanint 21317209Swollmanperfmon_read(int pmc, quad_t *val) 2142708Swollman{ 2152708Swollman if (pmc < 0 || pmc >= NPMC) 2162708Swollman return EINVAL; 2172708Swollman 2182708Swollman if (perfmon_inuse & (1 << pmc)) { 2192708Swollman if (ctl_shadow[pmc] & (PMCF_EN << 16)) 2209936Swollman *val = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL; 2212708Swollman else 22292889Sobrien *val = pmc_shadow[pmc]; 22392889Sobrien return 0; 2242708Swollman } 2259936Swollman 2269936Swollman return EBUSY; 2272708Swollman} 2282708Swollman 2292708Swollmanint 2302708Swollmanperfmon_reset(int pmc) 2312708Swollman{ 2322708Swollman if (pmc < 0 || pmc >= NPMC) 2332708Swollman return EINVAL; 2342708Swollman 2352708Swollman if (perfmon_inuse & (1 << pmc)) { 2369936Swollman wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0); 2372708Swollman return 0; 2382708Swollman } 2392708Swollman return EBUSY; 2402708Swollman} 24192889Sobrien 2422708Swollman#ifndef SMP 2432708Swollman/* 2449936Swollman * Unfortunately, the performance-monitoring registers are laid out 2452708Swollman * differently in the P5 and P6. We keep everything in P6 format 2462708Swollman * internally (except for the event code), and convert to P5 2472708Swollman * format as needed on those CPUs. The writectl function pointer 2482708Swollman * is set up to point to one of these functions by perfmon_init(). 2492708Swollman */ 2502708Swollmanint 2512708Swollmanwritectl6(int pmc) 2522708Swollman{ 2532708Swollman if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) { 2542708Swollman wrmsr(msr_ctl[pmc], 0); 2552708Swollman } else { 2562708Swollman wrmsr(msr_ctl[pmc], ctl_shadow[pmc]); 2572708Swollman } 2582708Swollman return 0; 2592708Swollman} 26092889Sobrien 2612708Swollman#define P5FLAG_P 0x200 2622708Swollman#define P5FLAG_E 0x100 2632708Swollman#define P5FLAG_USR 0x80 2642708Swollman#define P5FLAG_OS 0x40 2659936Swollman 2662708Swollmanint 2672708Swollmanwritectl5(int pmc) 2682708Swollman{ 2692708Swollman quad_t newval = 0; 2702708Swollman 27192889Sobrien if (ctl_shadow[1] & (PMCF_EN << 16)) { 27292889Sobrien if (ctl_shadow[1] & (PMCF_USR << 16)) 2732708Swollman newval |= P5FLAG_USR << 16; 27492889Sobrien if (ctl_shadow[1] & (PMCF_OS << 16)) 27592889Sobrien newval |= P5FLAG_OS << 16; 27692889Sobrien if (!(ctl_shadow[1] & (PMCF_E << 16))) 2772708Swollman newval |= P5FLAG_E << 16; 27839327Simp newval |= (ctl_shadow[1] & 0x3f) << 16; 27939327Simp } 28039327Simp if (ctl_shadow[0] & (PMCF_EN << 16)) { 28139327Simp if (ctl_shadow[0] & (PMCF_USR << 16)) 28239327Simp newval |= P5FLAG_USR; 2832708Swollman if (ctl_shadow[0] & (PMCF_OS << 16)) 2842708Swollman newval |= P5FLAG_OS; 2852708Swollman if (!(ctl_shadow[0] & (PMCF_E << 16))) 28692889Sobrien newval |= P5FLAG_E; 28718834Swollman newval |= ctl_shadow[0] & 0x3f; 2889936Swollman } 2899936Swollman 2909936Swollman wrmsr(msr_ctl[0], newval); 29118834Swollman return 0; /* XXX should check for unimplemented bits */ 2929936Swollman} 2939936Swollman#endif /* !SMP */ 2949936Swollman 2952708Swollman/* 2962708Swollman * Now the user-mode interface, called from a subdevice of mem.c. 2972708Swollman */ 2982708Swollmanstatic int writer; 2992708Swollmanstatic int writerpmc; 3002708Swollman 3012708Swollmanstatic int 3022708Swollmanperfmon_open(struct cdev *dev, int flags, int fmt, struct thread *td) 30339327Simp{ 3042708Swollman if (!perfmon_cpuok) 3052708Swollman return ENXIO; 3062708Swollman 3072708Swollman if (flags & FWRITE) { 3082708Swollman if (writer) { 3092708Swollman return EBUSY; 3102708Swollman } else { 3112708Swollman writer = 1; 3122708Swollman writerpmc = 0; 3132708Swollman } 3142708Swollman } 31524253Simp return 0; 31639327Simp} 31756698Sjasone 3182708Swollmanstatic int 31971579Sdeischenperfmon_close(struct cdev *dev, int flags, int fmt, struct thread *td) 32018834Swollman{ 3212708Swollman if (flags & FWRITE) { 3222708Swollman int i; 3239936Swollman 3249936Swollman for (i = 0; i < NPMC; i++) { 3259936Swollman if (writerpmc & (1 << i)) 3269936Swollman perfmon_fini(i); 3272708Swollman } 32856698Sjasone writer = 0; 32956698Sjasone } 3302708Swollman return 0; 3319936Swollman} 33242989Swollman 3339936Swollmanstatic int 3349936Swollmanperfmon_ioctl(struct cdev *dev, u_long cmd, caddr_t param, int flags, struct thread *td) 3359936Swollman{ 3369936Swollman struct pmc *pmc; 3379936Swollman struct pmc_data *pmcd; 3389936Swollman struct pmc_tstamp *pmct; 3399936Swollman uint64_t freq; 3409936Swollman int *ip; 3419936Swollman int rv; 3429936Swollman 3439936Swollman switch(cmd) { 3449936Swollman case PMIOSETUP: 3452708Swollman if (!(flags & FWRITE)) 3462708Swollman return EPERM; 3472708Swollman pmc = (struct pmc *)param; 3482708Swollman 3499936Swollman rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val); 3509936Swollman if (!rv) { 3512708Swollman writerpmc |= (1 << pmc->pmc_num); 3529936Swollman } 3539936Swollman break; 3549936Swollman 3559936Swollman case PMIOGET: 3569936Swollman pmc = (struct pmc *)param; 3579936Swollman rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val); 3589936Swollman break; 3592708Swollman 3602708Swollman case PMIOSTART: 3612708Swollman if (!(flags & FWRITE)) 3622708Swollman return EPERM; 3632708Swollman 3642708Swollman ip = (int *)param; 3652708Swollman rv = perfmon_start(*ip); 3662708Swollman break; 3672708Swollman 3682708Swollman case PMIOSTOP: 3692708Swollman if (!(flags & FWRITE)) 37092889Sobrien return EPERM; 3712708Swollman 3722708Swollman ip = (int *)param; 3732708Swollman rv = perfmon_stop(*ip); 3742708Swollman break; 3752708Swollman 3762708Swollman case PMIORESET: 3772708Swollman if (!(flags & FWRITE)) 3782708Swollman return EPERM; 3792708Swollman 3802708Swollman ip = (int *)param; 3812708Swollman rv = perfmon_reset(*ip); 3822708Swollman break; 3832708Swollman 3842708Swollman case PMIOREAD: 3852708Swollman pmcd = (struct pmc_data *)param; 3862708Swollman rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value); 38792889Sobrien break; 3882708Swollman 3892708Swollman case PMIOTSTAMP: 3902708Swollman freq = atomic_load_acq_64(&tsc_freq); 3912708Swollman if (freq == 0) { 3922708Swollman rv = ENOTTY; 3932708Swollman break; 3942708Swollman } 3952708Swollman pmct = (struct pmc_tstamp *)param; 39692889Sobrien /* XXX interface loses precision. */ 3972708Swollman pmct->pmct_rate = freq / 1000000; 3982708Swollman pmct->pmct_value = rdtsc(); 3992708Swollman rv = 0; 4002708Swollman break; 4012708Swollman default: 4022708Swollman rv = ENOTTY; 4032708Swollman } 4042708Swollman 4052708Swollman return rv; 4062708Swollman} 4072708Swollman