xref: /titanic_44/usr/src/uts/sun4v/pcbe/niagara2_pcbe.c (revision 55434c770c89aa1b84474f2559a106803511aba0)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Niagara2 Performance Counter Backend
28  */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <sys/cpuvar.h>
33 #include <sys/systm.h>
34 #include <sys/archsystm.h>
35 #include <sys/cmn_err.h>
36 #include <sys/cpc_impl.h>
37 #include <sys/cpc_pcbe.h>
38 #include <sys/modctl.h>
39 #include <sys/machsystm.h>
40 #include <sys/sdt.h>
41 #include <sys/niagara2regs.h>
42 #include <sys/hsvc.h>
43 
44 static int ni2_pcbe_init(void);
45 static uint_t ni2_pcbe_ncounters(void);
46 static const char *ni2_pcbe_impl_name(void);
47 static const char *ni2_pcbe_cpuref(void);
48 static char *ni2_pcbe_list_events(uint_t picnum);
49 static char *ni2_pcbe_list_attrs(void);
50 static uint64_t ni2_pcbe_event_coverage(char *event);
51 static uint64_t ni2_pcbe_overflow_bitmap(void);
52 static int ni2_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
53     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
54     void *token);
55 static void ni2_pcbe_program(void *token);
56 static void ni2_pcbe_allstop(void);
57 static void ni2_pcbe_sample(void *token);
58 static void ni2_pcbe_free(void *config);
59 
60 extern void ultra_setpcr(uint64_t);
61 extern uint64_t ultra_getpcr(void);
62 extern void ultra_setpic(uint64_t);
63 extern uint64_t ultra_getpic(void);
64 extern uint64_t ultra_gettick(void);
65 extern char cpu_module_name[];
66 
67 pcbe_ops_t ni2_pcbe_ops = {
68 	PCBE_VER_1,
69 	CPC_CAP_OVERFLOW_INTERRUPT | CPC_CAP_OVERFLOW_PRECISE,
70 	ni2_pcbe_ncounters,
71 	ni2_pcbe_impl_name,
72 	ni2_pcbe_cpuref,
73 	ni2_pcbe_list_events,
74 	ni2_pcbe_list_attrs,
75 	ni2_pcbe_event_coverage,
76 	ni2_pcbe_overflow_bitmap,
77 	ni2_pcbe_configure,
78 	ni2_pcbe_program,
79 	ni2_pcbe_allstop,
80 	ni2_pcbe_sample,
81 	ni2_pcbe_free
82 };
83 
84 typedef struct _ni2_pcbe_config {
85 	uint_t		pcbe_picno;	/* 0 for pic0 or 1 for pic1 */
86 	uint32_t	pcbe_evsel;	/* %pcr event code unshifted */
87 	uint32_t	pcbe_flags;	/* hpriv/user/system/priv */
88 	uint32_t	pcbe_pic;	/* unshifted raw %pic value */
89 } ni2_pcbe_config_t;
90 
91 typedef struct _ni2_event {
92 	const char	*name;
93 	const uint32_t	emask;
94 	const uint32_t	emask_valid;	/* Mask of unreserved MASK bits */
95 } ni2_event_t;
96 
97 #define	ULTRA_PCR_PRIVPIC	(UINT64_C(1) << CPC_NIAGARA2_PCR_PRIVPIC_SHIFT)
98 #define	EV_END {NULL, 0, 0}
99 
100 static const uint64_t   allstopped = ULTRA_PCR_PRIVPIC;
101 
102 static ni2_event_t ni2_events[] = {
103 	{ "Idle_strands",			0x000, 0x00 },
104 	{ "Br_completed",			0x201, 0x7f },
105 	{ "Br_taken",				0x202, 0x7f },
106 	{ "Instr_FGU_arithmetic",		0x204, 0x7f },
107 	{ "Instr_ld",				0x208, 0x7f },
108 	{ "Instr_st",				0x210, 0x7f },
109 	{ "Instr_sw",				0x220, 0x7f },
110 	{ "Instr_other",			0x240, 0x7f },
111 	{ "Instr_cnt",				0x27d, 0x7f },
112 	{ "IC_miss",				0x301, 0x3f },
113 	{ "DC_miss",				0x302, 0x3f },
114 	{ "ITLB_miss",				0x304, 0x3f },
115 	{ "DTLB_miss",				0x308, 0x3f },
116 	{ "L2_imiss",				0x310, 0x3f },
117 	{ "L2_dmiss_ld",			0x320, 0x3f },
118 	{ "ITLB_HWTW_ref_L2",			0x404, 0x3c },
119 	{ "DTLB_HWTW_ref_L2",			0x408, 0x3c },
120 	{ "ITLB_HWTW_miss_L2",			0x410, 0x3c },
121 	{ "DTLB_HWTW_miss_L2",			0x420, 0x3c },
122 	{ "Stream_ld_to_PCX",			0x501, 0x3f },
123 	{ "Stream_st_to_PCX",			0x502, 0x3f },
124 	{ "CPU_ld_to_PCX",			0x504, 0x3f },
125 	{ "CPU_ifetch_to_PCX",			0x508, 0x3f },
126 	{ "CPU_st_to_PCX",			0x510, 0x3f },
127 	{ "MMU_ld_to_PCX",			0x520, 0x3f },
128 	{ "DES_3DES_op",			0x601, 0x3f },
129 	{ "AES_op",				0x602, 0x3f },
130 	{ "RC4_op",				0x604, 0x3f },
131 	{ "MD5_SHA-1_SHA-256_op",		0x608, 0x3f },
132 	{ "MA_op",				0x610, 0x3f },
133 	{ "CRC_TCPIP_cksum",			0x620, 0x3f },
134 	{ "DES_3DES_busy_cycle",		0x701, 0x3f },
135 	{ "AES_busy_cycle",			0x702, 0x3f },
136 	{ "RC4_busy_cycle",			0x704, 0x3f },
137 	{ "MD5_SHA-1_SHA-256_busy_cycle",	0x708, 0x3f },
138 	{ "MA_busy_cycle",			0x710, 0x3f },
139 	{ "CRC_MPA_cksum",			0x720, 0x3f },
140 	EV_END
141 };
142 
143 static const char	*ni2_impl_name = "UltraSPARC T2";
144 static char		*evlist;
145 static size_t		evlist_sz;
146 static uint16_t 	pcr_pic0_mask;
147 static uint16_t 	pcr_pic1_mask;
148 
149 #define	CPU_REF_URL " Documentation for Sun processors can be found at: " \
150 			"http://www.sun.com/processors/manuals"
151 
152 static const char *niagara2_cpuref = "See the \"UltraSPARC T2 User's Manual\" "
153 			"for descriptions of these events." CPU_REF_URL;
154 
155 static boolean_t niagara2_hsvc_available = B_TRUE;
156 
157 static int
158 ni2_pcbe_init(void)
159 {
160 	ni2_event_t	*evp;
161 	int		status;
162 	uint64_t	niagara2_hsvc_major;
163 	uint64_t	niagara2_hsvc_minor;
164 
165 	pcr_pic0_mask = CPC_NIAGARA2_PCR_PIC0_MASK;
166 	pcr_pic1_mask = CPC_NIAGARA2_PCR_PIC1_MASK;
167 
168 	/*
169 	 * Validate API version for Niagara2 specific hypervisor services
170 	 */
171 	status = hsvc_version(HSVC_GROUP_NIAGARA2_CPU, &niagara2_hsvc_major,
172 	    &niagara2_hsvc_minor);
173 	if ((status != 0) || (niagara2_hsvc_major != NIAGARA2_HSVC_MAJOR)) {
174 		cmn_err(CE_WARN, "hypervisor services not negotiated "
175 		    "or unsupported major number: group: 0x%x major: 0x%lx "
176 		    "minor: 0x%lx errno: %d", HSVC_GROUP_NIAGARA2_CPU,
177 		    niagara2_hsvc_major, niagara2_hsvc_minor, status);
178 		niagara2_hsvc_available = B_FALSE;
179 	}
180 	/*
181 	 * Construct event list.
182 	 *
183 	 * First pass:  Calculate size needed. We'll need an additional byte
184 	 *		for the NULL pointer during the last strcat.
185 	 *
186 	 * Second pass: Copy strings.
187 	 */
188 	for (evp = ni2_events; evp->name != NULL; evp++)
189 		evlist_sz += strlen(evp->name) + 1;
190 
191 	evlist = kmem_alloc(evlist_sz + 1, KM_SLEEP);
192 	evlist[0] = '\0';
193 
194 	for (evp = ni2_events; evp->name != NULL; evp++) {
195 		(void) strcat(evlist, evp->name);
196 		(void) strcat(evlist, ",");
197 	}
198 	/*
199 	 * Remove trailing comma.
200 	 */
201 	evlist[evlist_sz - 1] = '\0';
202 
203 	return (0);
204 }
205 
206 static uint_t
207 ni2_pcbe_ncounters(void)
208 {
209 	return (2);
210 }
211 
212 static const char *
213 ni2_pcbe_impl_name(void)
214 {
215 	return (ni2_impl_name);
216 }
217 
218 static const char *
219 ni2_pcbe_cpuref(void)
220 {
221 	return (niagara2_cpuref);
222 }
223 
224 static char *
225 ni2_pcbe_list_events(uint_t picnum)
226 {
227 	ASSERT(picnum < cpc_ncounters);
228 
229 	return (evlist);
230 }
231 
232 static char *
233 ni2_pcbe_list_attrs(void)
234 {
235 	if (niagara2_hsvc_available == B_TRUE)
236 		return ("hpriv,emask");
237 	else
238 		return ("emask");
239 }
240 
241 static ni2_event_t *
242 find_event(char *name)
243 {
244 	ni2_event_t	*evp;
245 
246 	for (evp = ni2_events; evp->name != NULL; evp++)
247 		if (strcmp(name, evp->name) == 0)
248 			return (evp);
249 
250 	return (NULL);
251 }
252 
253 /*ARGSUSED*/
254 static uint64_t
255 ni2_pcbe_event_coverage(char *event)
256 {
257 	/*
258 	 * Fortunately, both pic0 and pic1 can count all events.
259 	 */
260 	return (0x3);
261 }
262 
263 #ifdef N2_ERRATUM_112
264 uint64_t	ni2_ov_tstamp[NCPU];	/* last overflow time stamp */
265 uint64_t	ni2_ov_spurious_range = 1000000; /* 1 msec at 1GHz */
266 #endif
267 
268 /*
269  * These processors cannot tell which counter overflowed. The PCBE interface
270  * requires such processors to act as if _all_ counters had overflowed.
271  */
272 static uint64_t
273 ni2_pcbe_overflow_bitmap(void)
274 {
275 	uint64_t	pcr, overflow;
276 	uint64_t	pic;
277 	uint32_t	pic0, pic1;
278 	boolean_t	update_pic = B_FALSE;
279 #ifdef N2_ERRATUM_112
280 	uint64_t	tstamp;
281 	processorid_t	cpun;
282 #endif
283 
284 	ASSERT(getpil() >= DISP_LEVEL);
285 	pcr = ultra_getpcr();
286 	DTRACE_PROBE1(niagara2__getpcr, uint64_t, pcr);
287 	overflow =  (pcr & CPC_NIAGARA2_PCR_OV0_MASK) >>
288 	    CPC_NIAGARA2_PCR_OV0_SHIFT;
289 	overflow |=  (pcr & CPC_NIAGARA2_PCR_OV1_MASK) >>
290 	    CPC_NIAGARA2_PCR_OV1_SHIFT;
291 #ifdef N2_ERRATUM_112
292 	/*
293 	 * Niagara2 1.x silicon can generate a duplicate overflow trap per
294 	 * event. If we take an overflow trap with no counters overflowing,
295 	 * return a non-zero bitmask with no OV bit set for supported
296 	 * counter so that the framework can ignore this trap.
297 	 */
298 	cpun = CPU->cpu_id;
299 	tstamp = ultra_gettick();
300 	if (overflow)
301 		ni2_ov_tstamp[cpun] = tstamp;
302 	else if (tstamp < (ni2_ov_tstamp[cpun] + ni2_ov_spurious_range))
303 		overflow |= 1ULL << 63;
304 #endif
305 	pic = ultra_getpic();
306 	pic0 = (uint32_t)(pic & PIC0_MASK);
307 	pic1 = (uint32_t)((pic >> PIC1_SHIFT) & PIC0_MASK);
308 
309 #ifdef N2_ERRATUM_134
310 	/*
311 	 * In Niagara2 1.x silicon, PMU doesn't set OV bit for precise events.
312 	 * So, if we take a trap with the counter within the overflow range
313 	 * and the OV bit is not set, we assume OV bit should have been set.
314 	 */
315 
316 	if (PIC_IN_OV_RANGE(pic0))
317 		overflow |= 0x1;
318 	if (PIC_IN_OV_RANGE(pic1))
319 		overflow |= 0x2;
320 #endif
321 	/*
322 	 * Reset the pic, if it is within the overflow range.
323 	 */
324 	if ((overflow & 0x1) && (PIC_IN_OV_RANGE(pic0))) {
325 		pic0 = 0;
326 		update_pic = B_TRUE;
327 	}
328 	if ((overflow & 0x2) && (PIC_IN_OV_RANGE(pic1))) {
329 		pic1 = 0;
330 		update_pic = B_TRUE;
331 	}
332 
333 	if (update_pic)
334 		ultra_setpic(((uint64_t)pic1 << PIC1_SHIFT) | pic0);
335 
336 	return (overflow);
337 }
338 
339 /*ARGSUSED*/
340 static int
341 ni2_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 	ni2_pcbe_config_t	*cfg;
345 	ni2_pcbe_config_t	*other_config;
346 	ni2_event_t		*evp;
347 	int			i;
348 	uint32_t		evsel;
349 
350 	/*
351 	 * If we've been handed an existing configuration, we need only preset
352 	 * the counter value.
353 	 */
354 	if (*data != NULL) {
355 		cfg = *data;
356 		cfg->pcbe_pic = (uint32_t)preset;
357 		return (0);
358 	}
359 
360 	if (picnum > 1)
361 		return (CPC_INVALID_PICNUM);
362 
363 	if ((evp = find_event(event)) == NULL)
364 		return (CPC_INVALID_EVENT);
365 
366 	evsel = evp->emask;
367 
368 	for (i = 0; i < nattrs; i++) {
369 		if (strcmp(attrs[i].ka_name, "hpriv") == 0) {
370 			if (attrs[i].ka_val != 0)
371 				flags |= CPC_COUNT_HPRIV;
372 		} else if (strcmp(attrs[i].ka_name, "emask") == 0) {
373 			if ((attrs[i].ka_val | evp->emask_valid) !=
374 			    evp->emask_valid)
375 				return (CPC_ATTRIBUTE_OUT_OF_RANGE);
376 			evsel |= attrs[i].ka_val;
377 		} else
378 			return (CPC_INVALID_ATTRIBUTE);
379 	}
380 
381 	/*
382 	 * Find other requests that will be programmed with this one, and ensure
383 	 * the flags don't conflict.
384 	 */
385 	if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
386 	    (other_config->pcbe_flags != flags))
387 		return (CPC_CONFLICTING_REQS);
388 
389 	cfg = kmem_alloc(sizeof (*cfg), KM_SLEEP);
390 
391 	cfg->pcbe_picno = picnum;
392 	cfg->pcbe_evsel = evsel;
393 	cfg->pcbe_flags = flags;
394 	cfg->pcbe_pic = (uint32_t)preset;
395 
396 	*data = cfg;
397 	return (0);
398 }
399 
400 static void
401 ni2_pcbe_program(void *token)
402 {
403 	ni2_pcbe_config_t	*pic0;
404 	ni2_pcbe_config_t	*pic1;
405 	ni2_pcbe_config_t	*tmp;
406 	ni2_pcbe_config_t	nullcfg = { 1, 0, 0, 0 };
407 	uint64_t		pcr;
408 	uint64_t		curpic;
409 	uint64_t		toe;
410 
411 	/* enable trap-on-event for pic0 and pic1 */
412 	toe = (CPC_COUNT_TOE0 | CPC_COUNT_TOE1);
413 
414 	if ((pic0 = (ni2_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) ==
415 	    NULL)
416 		panic("ni2_pcbe: token %p has no configs", token);
417 
418 	if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) {
419 		pic1 = &nullcfg;
420 		nullcfg.pcbe_flags = pic0->pcbe_flags;
421 		toe = CPC_COUNT_TOE0; /* enable trap-on-event for pic0 */
422 	}
423 
424 	if (pic0->pcbe_picno != 0) {
425 		/*
426 		 * pic0 is counter 1, so if we need the null config it should
427 		 * be counter 0.
428 		 */
429 		nullcfg.pcbe_picno = 0;
430 		tmp = pic0;
431 		pic0 = pic1;
432 		pic1 = tmp;
433 		toe = CPC_COUNT_TOE1; /* enable trap-on-event for pic1 */
434 	}
435 
436 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
437 		panic("%s: bad config on token %p\n", ni2_impl_name, token);
438 
439 	/*
440 	 * UltraSPARC does not allow pic0 to be configured differently
441 	 * from pic1. If the flags on these two configurations are
442 	 * different, they are incompatible. This condition should be
443 	 * caught at configure time.
444 	 */
445 	ASSERT(pic0->pcbe_flags == pic1->pcbe_flags);
446 
447 	ultra_setpcr(allstopped);
448 	ultra_setpic(((uint64_t)pic1->pcbe_pic << PIC1_SHIFT) |
449 	    (uint64_t)pic0->pcbe_pic);
450 
451 	pcr = (pic0->pcbe_evsel & pcr_pic0_mask) << CPC_NIAGARA2_PCR_PIC0_SHIFT;
452 	pcr |= (pic1->pcbe_evsel & pcr_pic1_mask) <<
453 	    CPC_NIAGARA2_PCR_PIC1_SHIFT;
454 
455 	if (pic0->pcbe_flags & CPC_COUNT_USER)
456 		pcr |= (1ull << CPC_NIAGARA2_PCR_USR_SHIFT);
457 	if (pic0->pcbe_flags & CPC_COUNT_SYSTEM)
458 		pcr |= (1ull << CPC_NIAGARA2_PCR_SYS_SHIFT);
459 	if (pic0->pcbe_flags & CPC_COUNT_HPRIV)
460 		pcr |= (1ull << CPC_NIAGARA2_PCR_HPRIV_SHIFT);
461 	pcr |= toe;
462 
463 	DTRACE_PROBE1(niagara2__setpcr, uint64_t, pcr);
464 
465 	/*
466 	 * PCR is set by HV using API call hv_niagara_setperf().
467 	 * Silently ignore hvpriv events if access is denied.
468 	 */
469 	if (pic0->pcbe_flags & CPC_COUNT_HPRIV) {
470 		if (hv_niagara_setperf(HV_NIAGARA_SPARC_CTL, pcr) != 0)
471 			ultra_setpcr(pcr);
472 	} else
473 		ultra_setpcr(pcr);
474 
475 	/*
476 	 * On UltraSPARC, only read-to-read counts are accurate. We cannot
477 	 * expect the value we wrote into the PIC, above, to be there after
478 	 * starting the counter. We must sample the counter value now and use
479 	 * that as the baseline for future samples.
480 	 */
481 	curpic = ultra_getpic();
482 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
483 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
484 
485 	DTRACE_PROBE1(niagara2__newpic, uint64_t, curpic);
486 }
487 
488 static void
489 ni2_pcbe_allstop(void)
490 {
491 	ultra_setpcr(allstopped);
492 }
493 
494 static void
495 ni2_pcbe_sample(void *token)
496 {
497 	uint64_t		curpic;
498 	int64_t			diff;
499 	uint64_t		*pic0_data;
500 	uint64_t		*pic1_data;
501 	uint64_t		*dtmp;
502 	uint64_t		tmp;
503 	ni2_pcbe_config_t	*pic0;
504 	ni2_pcbe_config_t	*pic1;
505 	ni2_pcbe_config_t	nullcfg = { 1, 0, 0, 0 };
506 	ni2_pcbe_config_t	*ctmp;
507 
508 	curpic = ultra_getpic();
509 	DTRACE_PROBE1(niagara2__getpic, uint64_t, curpic);
510 
511 	if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL)
512 		panic("%s: token %p has no configs", ni2_impl_name, token);
513 
514 	if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) {
515 		pic1 = &nullcfg;
516 		pic1_data = &tmp;
517 	}
518 
519 	if (pic0->pcbe_picno != 0) {
520 		nullcfg.pcbe_picno = 0;
521 		ctmp = pic0;
522 		pic0 = pic1;
523 		pic1 = ctmp;
524 		dtmp = pic0_data;
525 		pic0_data = pic1_data;
526 		pic1_data = dtmp;
527 	}
528 
529 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
530 		panic("%s: bad config on token %p\n", ni2_impl_name, token);
531 
532 	diff = (curpic & PIC0_MASK) - (uint64_t)pic0->pcbe_pic;
533 	if (diff < 0)
534 		diff += (1ll << 32);
535 	*pic0_data += diff;
536 
537 	diff = (curpic >> 32) - (uint64_t)pic1->pcbe_pic;
538 	if (diff < 0)
539 		diff += (1ll << 32);
540 	*pic1_data += diff;
541 
542 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
543 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
544 }
545 
546 static void
547 ni2_pcbe_free(void *config)
548 {
549 	kmem_free(config, sizeof (ni2_pcbe_config_t));
550 }
551 
552 
553 static struct modlpcbe modlpcbe = {
554 	&mod_pcbeops,
555 	"UltraSPARC T2 Performance Counters v%I%",
556 	&ni2_pcbe_ops
557 };
558 
559 static struct modlinkage modl = {
560 	MODREV_1,
561 	&modlpcbe,
562 };
563 
564 int
565 _init(void)
566 {
567 	if (ni2_pcbe_init() != 0)
568 		return (ENOTSUP);
569 	return (mod_install(&modl));
570 }
571 
572 int
573 _fini(void)
574 {
575 	return (mod_remove(&modl));
576 }
577 
578 int
579 _info(struct modinfo *mi)
580 {
581 	return (mod_info(&modl, mi));
582 }
583