xref: /freebsd/sys/i386/i386/perfmon.c (revision 4b9d6057)
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 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/conf.h>
33 #include <sys/fcntl.h>
34 #include <sys/kernel.h>
35 
36 #ifndef SMP
37 #include <machine/cputypes.h>
38 #endif
39 #include <machine/clock.h>
40 #include <machine/perfmon.h>
41 #include <machine/specialreg.h>
42 
43 static int perfmon_inuse;
44 static int perfmon_cpuok;
45 #ifndef SMP
46 static int msr_ctl[NPMC];
47 #endif
48 static int msr_pmc[NPMC];
49 static unsigned int ctl_shadow[NPMC];
50 static quad_t pmc_shadow[NPMC];	/* used when ctr is stopped on P5 */
51 static int (*writectl)(int);
52 #ifndef SMP
53 static int writectl5(int);
54 static int writectl6(int);
55 #endif
56 
57 static d_close_t perfmon_close;
58 static d_open_t	perfmon_open;
59 static d_ioctl_t perfmon_ioctl;
60 
61 /*
62  * XXX perfmon_init_dev(void *) is a split from the perfmon_init() function.
63  * This solves a problem for DEVFS users.  It loads the "perfmon" driver after
64  * the DEVFS subsystem has been kicked into action.  The SI_ORDER_ANY is to
65  * assure that it is the most lowest priority task which, guarantees the
66  * above.
67  */
68 static void perfmon_init_dev(void *);
69 SYSINIT(cpu, SI_SUB_DRIVERS, SI_ORDER_ANY, perfmon_init_dev, NULL);
70 
71 static struct cdevsw perfmon_cdevsw = {
72 	.d_version =	D_VERSION,
73 	.d_flags =	D_NEEDGIANT,
74 	.d_open =	perfmon_open,
75 	.d_close =	perfmon_close,
76 	.d_ioctl =	perfmon_ioctl,
77 	.d_name =	"perfmon",
78 };
79 
80 /*
81  * Must be called after cpu_class is set up.
82  */
83 void
84 perfmon_init(void)
85 {
86 #ifndef SMP
87 	switch(cpu_class) {
88 	case CPUCLASS_586:
89 		perfmon_cpuok = 1;
90 		msr_ctl[0] = MSR_P5_CESR;
91 		msr_ctl[1] = MSR_P5_CESR;
92 		msr_pmc[0] = MSR_P5_CTR0;
93 		msr_pmc[1] = MSR_P5_CTR1;
94 		writectl = writectl5;
95 		break;
96 	case CPUCLASS_686:
97 		perfmon_cpuok = 1;
98 		msr_ctl[0] = MSR_EVNTSEL0;
99 		msr_ctl[1] = MSR_EVNTSEL1;
100 		msr_pmc[0] = MSR_PERFCTR0;
101 		msr_pmc[1] = MSR_PERFCTR1;
102 		writectl = writectl6;
103 		break;
104 
105 	default:
106 		perfmon_cpuok = 0;
107 		break;
108 	}
109 #endif /* SMP */
110 }
111 
112 static void
113 perfmon_init_dev(void *dummy)
114 {
115 	make_dev(&perfmon_cdevsw, 32, UID_ROOT, GID_KMEM, 0640, "perfmon");
116 }
117 
118 int
119 perfmon_avail(void)
120 {
121 	return perfmon_cpuok;
122 }
123 
124 int
125 perfmon_setup(int pmc, unsigned int control)
126 {
127 	register_t	saveintr;
128 
129 	if (pmc < 0 || pmc >= NPMC)
130 		return EINVAL;
131 
132 	perfmon_inuse |= (1 << pmc);
133 	control &= ~(PMCF_SYS_FLAGS << 16);
134 	saveintr = intr_disable();
135 	ctl_shadow[pmc] = control;
136 	writectl(pmc);
137 	wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
138 	intr_restore(saveintr);
139 	return 0;
140 }
141 
142 int
143 perfmon_get(int pmc, unsigned int *control)
144 {
145 	if (pmc < 0 || pmc >= NPMC)
146 		return EINVAL;
147 
148 	if (perfmon_inuse & (1 << pmc)) {
149 		*control = ctl_shadow[pmc];
150 		return 0;
151 	}
152 	return EBUSY;		/* XXX reversed sense */
153 }
154 
155 int
156 perfmon_fini(int pmc)
157 {
158 	if (pmc < 0 || pmc >= NPMC)
159 		return EINVAL;
160 
161 	if (perfmon_inuse & (1 << pmc)) {
162 		perfmon_stop(pmc);
163 		ctl_shadow[pmc] = 0;
164 		perfmon_inuse &= ~(1 << pmc);
165 		return 0;
166 	}
167 	return EBUSY;		/* XXX reversed sense */
168 }
169 
170 int
171 perfmon_start(int pmc)
172 {
173 	register_t	saveintr;
174 
175 	if (pmc < 0 || pmc >= NPMC)
176 		return EINVAL;
177 
178 	if (perfmon_inuse & (1 << pmc)) {
179 		saveintr = intr_disable();
180 		ctl_shadow[pmc] |= (PMCF_EN << 16);
181 		wrmsr(msr_pmc[pmc], pmc_shadow[pmc]);
182 		writectl(pmc);
183 		intr_restore(saveintr);
184 		return 0;
185 	}
186 	return EBUSY;
187 }
188 
189 int
190 perfmon_stop(int pmc)
191 {
192 	register_t	saveintr;
193 
194 	if (pmc < 0 || pmc >= NPMC)
195 		return EINVAL;
196 
197 	if (perfmon_inuse & (1 << pmc)) {
198 		saveintr = intr_disable();
199 		pmc_shadow[pmc] = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
200 		ctl_shadow[pmc] &= ~(PMCF_EN << 16);
201 		writectl(pmc);
202 		intr_restore(saveintr);
203 		return 0;
204 	}
205 	return EBUSY;
206 }
207 
208 int
209 perfmon_read(int pmc, quad_t *val)
210 {
211 	if (pmc < 0 || pmc >= NPMC)
212 		return EINVAL;
213 
214 	if (perfmon_inuse & (1 << pmc)) {
215 		if (ctl_shadow[pmc] & (PMCF_EN << 16))
216 			*val = rdmsr(msr_pmc[pmc]) & 0xffffffffffULL;
217 		else
218 			*val = pmc_shadow[pmc];
219 		return 0;
220 	}
221 
222 	return EBUSY;
223 }
224 
225 int
226 perfmon_reset(int pmc)
227 {
228 	if (pmc < 0 || pmc >= NPMC)
229 		return EINVAL;
230 
231 	if (perfmon_inuse & (1 << pmc)) {
232 		wrmsr(msr_pmc[pmc], pmc_shadow[pmc] = 0);
233 		return 0;
234 	}
235 	return EBUSY;
236 }
237 
238 #ifndef SMP
239 /*
240  * Unfortunately, the performance-monitoring registers are laid out
241  * differently in the P5 and P6.  We keep everything in P6 format
242  * internally (except for the event code), and convert to P5
243  * format as needed on those CPUs.  The writectl function pointer
244  * is set up to point to one of these functions by perfmon_init().
245  */
246 int
247 writectl6(int pmc)
248 {
249 	if (pmc > 0 && !(ctl_shadow[pmc] & (PMCF_EN << 16))) {
250 		wrmsr(msr_ctl[pmc], 0);
251 	} else {
252 		wrmsr(msr_ctl[pmc], ctl_shadow[pmc]);
253 	}
254 	return 0;
255 }
256 
257 #define	P5FLAG_P	0x200
258 #define	P5FLAG_E	0x100
259 #define	P5FLAG_USR	0x80
260 #define	P5FLAG_OS	0x40
261 
262 int
263 writectl5(int pmc)
264 {
265 	quad_t newval = 0;
266 
267 	if (ctl_shadow[1] & (PMCF_EN << 16)) {
268 		if (ctl_shadow[1] & (PMCF_USR << 16))
269 			newval |= P5FLAG_USR << 16;
270 		if (ctl_shadow[1] & (PMCF_OS << 16))
271 			newval |= P5FLAG_OS << 16;
272 		if (!(ctl_shadow[1] & (PMCF_E << 16)))
273 			newval |= P5FLAG_E << 16;
274 		newval |= (ctl_shadow[1] & 0x3f) << 16;
275 	}
276 	if (ctl_shadow[0] & (PMCF_EN << 16)) {
277 		if (ctl_shadow[0] & (PMCF_USR << 16))
278 			newval |= P5FLAG_USR;
279 		if (ctl_shadow[0] & (PMCF_OS << 16))
280 			newval |= P5FLAG_OS;
281 		if (!(ctl_shadow[0] & (PMCF_E << 16)))
282 			newval |= P5FLAG_E;
283 		newval |= ctl_shadow[0] & 0x3f;
284 	}
285 
286 	wrmsr(msr_ctl[0], newval);
287 	return 0;		/* XXX should check for unimplemented bits */
288 }
289 #endif /* !SMP */
290 
291 /*
292  * Now the user-mode interface, called from a subdevice of mem.c.
293  */
294 static int writer;
295 static int writerpmc;
296 
297 static int
298 perfmon_open(struct cdev *dev, int flags, int fmt, struct thread *td)
299 {
300 	if (!perfmon_cpuok)
301 		return ENXIO;
302 
303 	if (flags & FWRITE) {
304 		if (writer) {
305 			return EBUSY;
306 		} else {
307 			writer = 1;
308 			writerpmc = 0;
309 		}
310 	}
311 	return 0;
312 }
313 
314 static int
315 perfmon_close(struct cdev *dev, int flags, int fmt, struct thread *td)
316 {
317 	if (flags & FWRITE) {
318 		int i;
319 
320 		for (i = 0; i < NPMC; i++) {
321 			if (writerpmc & (1 << i))
322 				perfmon_fini(i);
323 		}
324 		writer = 0;
325 	}
326 	return 0;
327 }
328 
329 static int
330 perfmon_ioctl(struct cdev *dev, u_long cmd, caddr_t param, int flags, struct thread *td)
331 {
332 	struct pmc *pmc;
333 	struct pmc_data *pmcd;
334 	struct pmc_tstamp *pmct;
335 	uint64_t freq;
336 	int *ip;
337 	int rv;
338 
339 	switch(cmd) {
340 	case PMIOSETUP:
341 		if (!(flags & FWRITE))
342 			return EPERM;
343 		pmc = (struct pmc *)param;
344 
345 		rv = perfmon_setup(pmc->pmc_num, pmc->pmc_val);
346 		if (!rv) {
347 			writerpmc |= (1 << pmc->pmc_num);
348 		}
349 		break;
350 
351 	case PMIOGET:
352 		pmc = (struct pmc *)param;
353 		rv = perfmon_get(pmc->pmc_num, &pmc->pmc_val);
354 		break;
355 
356 	case PMIOSTART:
357 		if (!(flags & FWRITE))
358 			return EPERM;
359 
360 		ip = (int *)param;
361 		rv = perfmon_start(*ip);
362 		break;
363 
364 	case PMIOSTOP:
365 		if (!(flags & FWRITE))
366 			return EPERM;
367 
368 		ip = (int *)param;
369 		rv = perfmon_stop(*ip);
370 		break;
371 
372 	case PMIORESET:
373 		if (!(flags & FWRITE))
374 			return EPERM;
375 
376 		ip = (int *)param;
377 		rv = perfmon_reset(*ip);
378 		break;
379 
380 	case PMIOREAD:
381 		pmcd = (struct pmc_data *)param;
382 		rv = perfmon_read(pmcd->pmcd_num, &pmcd->pmcd_value);
383 		break;
384 
385 	case PMIOTSTAMP:
386 		freq = atomic_load_acq_64(&tsc_freq);
387 		if (freq == 0) {
388 			rv = ENOTTY;
389 			break;
390 		}
391 		pmct = (struct pmc_tstamp *)param;
392 		/* XXX interface loses precision. */
393 		pmct->pmct_rate = freq / 1000000;
394 		pmct->pmct_value = rdtsc();
395 		rv = 0;
396 		break;
397 	default:
398 		rv = ENOTTY;
399 	}
400 
401 	return rv;
402 }
403