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