xref: /illumos-gate/usr/src/uts/sun4v/pcbe/niagara_pcbe.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * This file contains preset event names from the Performance Application
28  * Programming Interface v3.5 which included the following notice:
29  *
30  *                             Copyright (c) 2005,6
31  *                           Innovative Computing Labs
32  *                         Computer Science Department,
33  *                            University of Tennessee,
34  *                                 Knoxville, TN.
35  *                              All Rights Reserved.
36  *
37  *
38  * Redistribution and use in source and binary forms, with or without
39  * modification, are permitted provided that the following conditions are met:
40  *
41  *    * Redistributions of source code must retain the above copyright notice,
42  *      this list of conditions and the following disclaimer.
43  *    * Redistributions in binary form must reproduce the above copyright
44  *      notice, this list of conditions and the following disclaimer in the
45  *      documentation and/or other materials provided with the distribution.
46  *    * Neither the name of the University of Tennessee nor the names of its
47  *      contributors may be used to endorse or promote products derived from
48  *      this software without specific prior written permission.
49  *
50  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
51  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
52  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
53  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
54  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
55  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
56  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
57  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
58  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
59  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
60  * POSSIBILITY OF SUCH DAMAGE.
61  *
62  *
63  * This open source software license conforms to the BSD License template.
64  */
65 
66 /*
67  * Niagara Performance Counter Backend
68  */
69 
70 #include <sys/cpuvar.h>
71 #include <sys/systm.h>
72 #include <sys/cmn_err.h>
73 #include <sys/cpc_impl.h>
74 #include <sys/cpc_pcbe.h>
75 #include <sys/modctl.h>
76 #include <sys/machsystm.h>
77 #include <sys/sdt.h>
78 #include <sys/niagararegs.h>
79 
80 static int ni_pcbe_init(void);
81 static uint_t ni_pcbe_ncounters(void);
82 static const char *ni_pcbe_impl_name(void);
83 static const char *ni_pcbe_cpuref(void);
84 static char *ni_pcbe_list_events(uint_t picnum);
85 static char *ni_pcbe_list_attrs(void);
86 static uint64_t ni_pcbe_event_coverage(char *event);
87 static uint64_t ni_pcbe_overflow_bitmap(void);
88 static int ni_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
89     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
90     void *token);
91 static void ni_pcbe_program(void *token);
92 static void ni_pcbe_allstop(void);
93 static void ni_pcbe_sample(void *token);
94 static void ni_pcbe_free(void *config);
95 
96 extern void ultra_setpcr(uint64_t);
97 extern uint64_t ultra_getpcr(void);
98 extern void ultra_setpic(uint64_t);
99 extern uint64_t ultra_getpic(void);
100 extern uint64_t ultra_gettick(void);
101 
102 pcbe_ops_t ni_pcbe_ops = {
103 	PCBE_VER_1,
104 	CPC_CAP_OVERFLOW_INTERRUPT | CPC_CAP_OVERFLOW_PRECISE,
105 	ni_pcbe_ncounters,
106 	ni_pcbe_impl_name,
107 	ni_pcbe_cpuref,
108 	ni_pcbe_list_events,
109 	ni_pcbe_list_attrs,
110 	ni_pcbe_event_coverage,
111 	ni_pcbe_overflow_bitmap,
112 	ni_pcbe_configure,
113 	ni_pcbe_program,
114 	ni_pcbe_allstop,
115 	ni_pcbe_sample,
116 	ni_pcbe_free
117 };
118 
119 typedef struct _ni_pcbe_config {
120 	uint8_t		pcbe_picno;	/* 0 for pic0 or 1 for pic1 */
121 	uint32_t	pcbe_bits;	/* %pcr event code unshifted */
122 	uint32_t	pcbe_flags;	/* user/system/priv */
123 	uint32_t	pcbe_pic;	/* unshifted raw %pic value */
124 } ni_pcbe_config_t;
125 
126 struct nametable {
127 	const uint8_t	bits;
128 	const char	*name;
129 };
130 
131 typedef struct _ni_generic_events {
132 	char *name;
133 	char *event;
134 } ni_generic_event_t;
135 
136 #define	ULTRA_PCR_PRIVPIC	(UINT64_C(1) << CPC_NIAGARA_PCR_PRIVPIC)
137 #define	NT_END 0xFF
138 #define	GEN_EVT_END { NULL, NULL }
139 
140 static const uint64_t   allstopped = ULTRA_PCR_PRIVPIC;
141 
142 static const struct nametable Niagara_names1[] = {
143 	{0x00, "Instr_cnt"},
144 	{NT_END, ""}
145 };
146 
147 static const struct nametable Niagara_names0[] = {
148 	{0x0,	"SB_full"},
149 	{0x1,	"FP_instr_cnt"},
150 	{0x2,	"IC_miss"},
151 	{0x3,	"DC_miss"},
152 	{0x4,	"ITLB_miss"},
153 	{0x5,	"DTLB_miss"},
154 	{0x6,	"L2_imiss"},
155 	{0x7,	"L2_dmiss_ld"},
156 	{NT_END, ""}
157 };
158 
159 static const struct nametable *Niagara_names[2] = {
160 	Niagara_names0,
161 	Niagara_names1
162 };
163 
164 static const ni_generic_event_t Niagara_generic_names1[] = {
165 	{ "PAPI_tot_ins",	"Instr_cnt" },
166 	{ NULL,			NULL }
167 };
168 
169 static const ni_generic_event_t Niagara_generic_names0[] = {
170 	{ "PAPI_l2_icm",	"L2_imiss" },
171 	{ "PAPI_l2_ldm",	"L2_dmiss_ld" },
172 	{ "PAPI_fp_ops",	"FP_instr_cnt" },
173 	{ "PAPI_l1_icm",	"IC_miss" },
174 	{ "PAPI_l1_dcm",	"DC_miss" },
175 	{ "PAPI_tlb_im",	"ITLB_miss" },
176 	{ "PAPI_tlb_dm",	"DTLB_miss" },
177 	{ NULL,			NULL }
178 };
179 
180 static const ni_generic_event_t *Niagara_generic_names[2] = {
181 	Niagara_generic_names0,
182 	Niagara_generic_names1
183 };
184 
185 static const struct nametable **events;
186 static const ni_generic_event_t **generic_events;
187 static const char *ni_impl_name = "UltraSPARC T1";
188 static char *pic_events[2];
189 static uint16_t pcr_pic0_mask;
190 static uint16_t pcr_pic1_mask;
191 
192 #define	CPU_REF_URL " Documentation for Sun processors can be found at: " \
193 			"http://www.sun.com/processors/manuals"
194 
195 static const char *niagara_cpuref = "See the \"UltraSPARC T1 User's Manual\" "
196 			"for descriptions of these events." CPU_REF_URL;
197 
198 static int
199 ni_pcbe_init(void)
200 {
201 	const struct nametable		*n;
202 	const ni_generic_event_t	*gevp;
203 	int				i;
204 	size_t				size;
205 
206 	events = Niagara_names;
207 	generic_events = Niagara_generic_names;
208 	pcr_pic0_mask = CPC_NIAGARA_PCR_PIC0_MASK;
209 	pcr_pic1_mask = CPC_NIAGARA_PCR_PIC1_MASK;
210 
211 	/*
212 	 * Initialize the list of events for each PIC.
213 	 * Do two passes: one to compute the size necessary and another
214 	 * to copy the strings. Need room for event, comma, and NULL terminator.
215 	 */
216 	for (i = 0; i < 2; i++) {
217 		size = 0;
218 		for (n = events[i]; n->bits != NT_END; n++)
219 			size += strlen(n->name) + 1;
220 		for (gevp = generic_events[i]; gevp->name != NULL; gevp++)
221 			size += strlen(gevp->name) + 1;
222 		pic_events[i] = kmem_alloc(size + 1, KM_SLEEP);
223 		*pic_events[i] = '\0';
224 		for (n = events[i]; n->bits != NT_END; n++) {
225 			(void) strcat(pic_events[i], n->name);
226 			(void) strcat(pic_events[i], ",");
227 		}
228 		for (gevp = generic_events[i]; gevp->name != NULL; gevp++) {
229 			(void) strcat(pic_events[i], gevp->name);
230 			(void) strcat(pic_events[i], ",");
231 		}
232 		/*
233 		 * Remove trailing comma.
234 		 */
235 		pic_events[i][size - 1] = '\0';
236 	}
237 
238 	return (0);
239 }
240 
241 static uint_t
242 ni_pcbe_ncounters(void)
243 {
244 	return (2);
245 }
246 
247 static const char *
248 ni_pcbe_impl_name(void)
249 {
250 	return (ni_impl_name);
251 }
252 
253 static const char *
254 ni_pcbe_cpuref(void)
255 {
256 	return (niagara_cpuref);
257 }
258 
259 static char *
260 ni_pcbe_list_events(uint_t picnum)
261 {
262 	ASSERT(picnum >= 0 && picnum < cpc_ncounters);
263 
264 	return (pic_events[picnum]);
265 }
266 
267 static char *
268 ni_pcbe_list_attrs(void)
269 {
270 	return ("");
271 }
272 
273 static const ni_generic_event_t *
274 find_generic_event(int regno, char *name)
275 {
276 	const ni_generic_event_t *gevp;
277 
278 	for (gevp = generic_events[regno]; gevp->name != NULL; gevp++) {
279 		if (strcmp(gevp->name, name) == 0)
280 			return (gevp);
281 	}
282 
283 	return (NULL);
284 }
285 
286 static const struct nametable *
287 find_event(int regno, char *name)
288 {
289 	const struct nametable		*n;
290 
291 	n = events[regno];
292 
293 	for (; n->bits != NT_END; n++)
294 		if (strcmp(name, n->name) == 0)
295 			return (n);
296 
297 	return (NULL);
298 }
299 
300 static uint64_t
301 ni_pcbe_event_coverage(char *event)
302 {
303 	uint64_t bitmap = 0;
304 
305 	if ((find_event(0, event) != NULL) ||
306 	    (find_generic_event(0, event) != NULL))
307 		bitmap = 0x1;
308 	if ((find_event(1, event) != NULL) ||
309 	    (find_generic_event(1, event) != NULL))
310 		bitmap |= 0x2;
311 
312 	return (bitmap);
313 }
314 
315 /*
316  * These processors cannot tell which counter overflowed. The PCBE interface
317  * requires such processors to act as if _all_ counters had overflowed.
318  */
319 static uint64_t
320 ni_pcbe_overflow_bitmap(void)
321 {
322 	uint64_t pcr, overflow;
323 
324 	pcr = ultra_getpcr();
325 	DTRACE_PROBE1(niagara__getpcr, uint64_t, pcr);
326 	overflow =  (pcr & CPC_NIAGARA_PCR_OVF_MASK) >>
327 	    CPC_NIAGARA_PCR_OVF_SHIFT;
328 #if 0
329 	/*
330 	 * Not needed if the CPC framework is responsible to stop counters
331 	 * and that action ends up clearing overflow flags.
332 	 */
333 	if (overflow)
334 		ultra_setpcr(pcr & ~CPC_NIAGARA_PCR_OVF_MASK);
335 #endif
336 	return (overflow);
337 }
338 
339 /*ARGSUSED*/
340 static int
341 ni_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
342     uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
343 {
344 	ni_pcbe_config_t		*conf;
345 	const struct nametable		*n;
346 	const ni_generic_event_t	*gevp;
347 	ni_pcbe_config_t		*other_config;
348 
349 	/*
350 	 * If we've been handed an existing configuration, we need only preset
351 	 * the counter value.
352 	 */
353 	if (*data != NULL) {
354 		conf = *data;
355 		conf->pcbe_pic = (uint32_t)preset;
356 		return (0);
357 	}
358 	if (picnum < 0 || picnum > 1)
359 		return (CPC_INVALID_PICNUM);
360 
361 	if (nattrs != 0)
362 		return (CPC_INVALID_ATTRIBUTE);
363 
364 	/*
365 	 * Find other requests that will be programmed with this one, and ensure
366 	 * the flags don't conflict.
367 	 */
368 	if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
369 	    (other_config->pcbe_flags != flags))
370 		return (CPC_CONFLICTING_REQS);
371 
372 	if ((n = find_event(picnum, event)) == NULL) {
373 		if ((gevp = find_generic_event(picnum, event)) != NULL) {
374 			n = find_event(picnum, gevp->event);
375 			ASSERT(n != NULL);
376 		} else {
377 			return (CPC_INVALID_EVENT);
378 		}
379 	}
380 
381 	conf = kmem_alloc(sizeof (ni_pcbe_config_t), KM_SLEEP);
382 
383 	conf->pcbe_picno = picnum;
384 	conf->pcbe_bits = (uint32_t)n->bits;
385 	conf->pcbe_flags = flags;
386 	conf->pcbe_pic = (uint32_t)preset;
387 
388 	*data = conf;
389 	return (0);
390 }
391 
392 static void
393 ni_pcbe_program(void *token)
394 {
395 	ni_pcbe_config_t	*pic0;
396 	ni_pcbe_config_t	*pic1;
397 	ni_pcbe_config_t	*tmp;
398 	ni_pcbe_config_t	empty = { 1, 0x1c, 0, 0 }; /* SW_count_1 */
399 	uint64_t		pcr;
400 	uint64_t		curpic;
401 
402 	if ((pic0 = (ni_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) ==
403 	    NULL)
404 		panic("ni_pcbe: token %p has no configs", token);
405 
406 	if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) {
407 		pic1 = &empty;
408 		empty.pcbe_flags = pic0->pcbe_flags;
409 	}
410 
411 	if (pic0->pcbe_picno != 0) {
412 		/*
413 		 * pic0 is counter 1, so if we need the empty config it should
414 		 * be counter 0.
415 		 */
416 		empty.pcbe_picno = 0;
417 #if 0
418 		/* no selection for counter 0 */
419 		empty.pcbe_bits = 0x14; /* SW_count_0 - won't overflow */
420 #endif
421 		tmp = pic0;
422 		pic0 = pic1;
423 		pic1 = tmp;
424 	}
425 
426 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
427 		panic("ni_pcbe: bad config on token %p\n", token);
428 
429 	/*
430 	 * UltraSPARC does not allow pic0 to be configured differently
431 	 * from pic1. If the flags on these two configurations are
432 	 * different, they are incompatible. This condition should be
433 	 * caught at configure time.
434 	 */
435 	ASSERT(pic0->pcbe_flags == pic1->pcbe_flags);
436 
437 	ultra_setpcr(allstopped);
438 	ultra_setpic(((uint64_t)pic1->pcbe_pic << PIC1_SHIFT) |
439 	    (uint64_t)pic0->pcbe_pic);
440 
441 	pcr = (pic0->pcbe_bits & pcr_pic0_mask) << CPC_NIAGARA_PCR_PIC0_SHIFT;
442 	pcr |= (pic1->pcbe_bits & pcr_pic1_mask) << CPC_NIAGARA_PCR_PIC1_SHIFT;
443 
444 	if (pic0->pcbe_flags & CPC_COUNT_USER)
445 		pcr |= (1ull << CPC_NIAGARA_PCR_USR);
446 	if (pic0->pcbe_flags & CPC_COUNT_SYSTEM)
447 		pcr |= (1ull << CPC_NIAGARA_PCR_SYS);
448 
449 	DTRACE_PROBE1(niagara__setpcr, uint64_t, pcr);
450 	ultra_setpcr(pcr);
451 
452 	/*
453 	 * On UltraSPARC, only read-to-read counts are accurate. We cannot
454 	 * expect the value we wrote into the PIC, above, to be there after
455 	 * starting the counter. We must sample the counter value now and use
456 	 * that as the baseline for future samples.
457 	 */
458 	curpic = ultra_getpic();
459 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
460 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
461 	DTRACE_PROBE1(niagara__newpic, uint64_t, curpic);
462 }
463 
464 static void
465 ni_pcbe_allstop(void)
466 {
467 	ultra_setpcr(allstopped);
468 }
469 
470 
471 static void
472 ni_pcbe_sample(void *token)
473 {
474 	uint64_t		curpic;
475 	int64_t			diff;
476 	uint64_t		*pic0_data;
477 	uint64_t		*pic1_data;
478 	uint64_t		*dtmp;
479 	uint64_t		tmp;
480 	ni_pcbe_config_t	*pic0;
481 	ni_pcbe_config_t	*pic1;
482 	ni_pcbe_config_t	empty = { 1, 0, 0, 0 };
483 	ni_pcbe_config_t	*ctmp;
484 
485 	curpic = ultra_getpic();
486 	DTRACE_PROBE1(niagara__getpic, uint64_t, curpic);
487 
488 	if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL)
489 		panic("%s: token %p has no configs", ni_impl_name, token);
490 
491 	if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) {
492 		pic1 = &empty;
493 		pic1_data = &tmp;
494 	}
495 
496 	if (pic0->pcbe_picno != 0) {
497 		empty.pcbe_picno = 0;
498 		ctmp = pic0;
499 		pic0 = pic1;
500 		pic1 = ctmp;
501 		dtmp = pic0_data;
502 		pic0_data = pic1_data;
503 		pic1_data = dtmp;
504 	}
505 
506 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
507 		panic("%s: bad config on token %p\n", ni_impl_name, token);
508 
509 	diff = (curpic & PIC0_MASK) - (uint64_t)pic0->pcbe_pic;
510 	if (diff < 0)
511 		diff += (1ll << 32);
512 	*pic0_data += diff;
513 
514 	diff = (curpic >> 32) - (uint64_t)pic1->pcbe_pic;
515 	if (diff < 0)
516 		diff += (1ll << 32);
517 	*pic1_data += diff;
518 
519 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
520 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
521 }
522 
523 static void
524 ni_pcbe_free(void *config)
525 {
526 	kmem_free(config, sizeof (ni_pcbe_config_t));
527 }
528 
529 
530 static struct modlpcbe modlpcbe = {
531 	&mod_pcbeops,
532 	"UltraSPARC T1 Performance Counters",
533 	&ni_pcbe_ops
534 };
535 
536 static struct modlinkage modl = {
537 	MODREV_1,
538 	&modlpcbe,
539 };
540 
541 int
542 _init(void)
543 {
544 	if (ni_pcbe_init() != 0)
545 		return (ENOTSUP);
546 	return (mod_install(&modl));
547 }
548 
549 int
550 _fini(void)
551 {
552 	return (mod_remove(&modl));
553 }
554 
555 int
556 _info(struct modinfo *mi)
557 {
558 	return (mod_info(&modl, mi));
559 }
560