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