xref: /illumos-gate/usr/src/uts/sun4v/pcbe/niagara_pcbe.c (revision 4764d912222e53f8386bae7bf491f5780fd102ec)
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  * Niagara 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/cmn_err.h>
35 #include <sys/cpc_impl.h>
36 #include <sys/cpc_pcbe.h>
37 #include <sys/modctl.h>
38 #include <sys/machsystm.h>
39 #include <sys/sdt.h>
40 #include <sys/niagararegs.h>
41 
42 static int ni_pcbe_init(void);
43 static uint_t ni_pcbe_ncounters(void);
44 static const char *ni_pcbe_impl_name(void);
45 static const char *ni_pcbe_cpuref(void);
46 static char *ni_pcbe_list_events(uint_t picnum);
47 static char *ni_pcbe_list_attrs(void);
48 static uint64_t ni_pcbe_event_coverage(char *event);
49 static uint64_t ni_pcbe_overflow_bitmap(void);
50 static int ni_pcbe_configure(uint_t picnum, char *event, uint64_t preset,
51     uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data,
52     void *token);
53 static void ni_pcbe_program(void *token);
54 static void ni_pcbe_allstop(void);
55 static void ni_pcbe_sample(void *token);
56 static void ni_pcbe_free(void *config);
57 
58 extern void ultra_setpcr(uint64_t);
59 extern uint64_t ultra_getpcr(void);
60 extern void ultra_setpic(uint64_t);
61 extern uint64_t ultra_getpic(void);
62 extern uint64_t ultra_gettick(void);
63 
64 pcbe_ops_t ni_pcbe_ops = {
65 	PCBE_VER_1,
66 	CPC_CAP_OVERFLOW_INTERRUPT | CPC_CAP_OVERFLOW_PRECISE,
67 	ni_pcbe_ncounters,
68 	ni_pcbe_impl_name,
69 	ni_pcbe_cpuref,
70 	ni_pcbe_list_events,
71 	ni_pcbe_list_attrs,
72 	ni_pcbe_event_coverage,
73 	ni_pcbe_overflow_bitmap,
74 	ni_pcbe_configure,
75 	ni_pcbe_program,
76 	ni_pcbe_allstop,
77 	ni_pcbe_sample,
78 	ni_pcbe_free
79 };
80 
81 typedef struct _ni_pcbe_config {
82 	uint8_t		pcbe_picno;	/* 0 for pic0 or 1 for pic1 */
83 	uint32_t	pcbe_bits;	/* %pcr event code unshifted */
84 	uint32_t	pcbe_flags;	/* user/system/priv */
85 	uint32_t	pcbe_pic;	/* unshifted raw %pic value */
86 } ni_pcbe_config_t;
87 
88 struct nametable {
89 	const uint8_t	bits;
90 	const char	*name;
91 };
92 
93 #define	ULTRA_PCR_PRIVPIC	(UINT64_C(1) << CPC_NIAGARA_PCR_PRIVPIC)
94 #define	NT_END 0xFF
95 
96 static const uint64_t   allstopped = ULTRA_PCR_PRIVPIC;
97 
98 static const struct nametable Niagara_names1[] = {
99 	{0x00, "Instr_cnt"},
100 	{NT_END, ""}
101 };
102 
103 static const struct nametable Niagara_names0[] = {
104 	{0x0,	"SB_full"},
105 	{0x1,	"FP_instr_cnt"},
106 	{0x2,	"IC_miss"},
107 	{0x3,	"DC_miss"},
108 	{0x4,	"ITLB_miss"},
109 	{0x5,	"DTLB_miss"},
110 	{0x6,	"L2_imiss"},
111 	{0x7,	"L2_dmiss_ld"},
112 	{NT_END, ""}
113 };
114 
115 static const struct nametable *Niagara_names[2] = {
116 	Niagara_names0,
117 	Niagara_names1
118 };
119 
120 static const struct nametable **events;
121 static const char *ni_impl_name = "UltraSPARC T1";
122 static char *pic_events[2];
123 static uint16_t pcr_pic0_mask;
124 static uint16_t pcr_pic1_mask;
125 
126 #define	CPU_REF_URL " Documentation for Sun processors can be found at: " \
127 			"http://www.sun.com/processors/manuals"
128 
129 static const char *niagara_cpuref = "See the \"UltraSPARC T1 User's Manual\" "
130 			"for descriptions of these events." CPU_REF_URL;
131 
132 static int
133 ni_pcbe_init(void)
134 {
135 	const struct nametable	*n;
136 	int			i;
137 	size_t			size;
138 
139 	events = Niagara_names;
140 	pcr_pic0_mask = CPC_NIAGARA_PCR_PIC0_MASK;
141 	pcr_pic1_mask = CPC_NIAGARA_PCR_PIC1_MASK;
142 
143 	/*
144 	 * Initialize the list of events for each PIC.
145 	 * Do two passes: one to compute the size necessary and another
146 	 * to copy the strings. Need room for event, comma, and NULL terminator.
147 	 */
148 	for (i = 0; i < 2; i++) {
149 		size = 0;
150 		for (n = events[i]; n->bits != NT_END; n++)
151 			size += strlen(n->name) + 1;
152 		pic_events[i] = kmem_alloc(size + 1, KM_SLEEP);
153 		*pic_events[i] = '\0';
154 		for (n = events[i]; n->bits != NT_END; n++) {
155 			(void) strcat(pic_events[i], n->name);
156 			(void) strcat(pic_events[i], ",");
157 		}
158 		/*
159 		 * Remove trailing comma.
160 		 */
161 		pic_events[i][size - 1] = '\0';
162 	}
163 
164 	return (0);
165 }
166 
167 static uint_t
168 ni_pcbe_ncounters(void)
169 {
170 	return (2);
171 }
172 
173 static const char *
174 ni_pcbe_impl_name(void)
175 {
176 	return (ni_impl_name);
177 }
178 
179 static const char *
180 ni_pcbe_cpuref(void)
181 {
182 	return (niagara_cpuref);
183 }
184 
185 static char *
186 ni_pcbe_list_events(uint_t picnum)
187 {
188 	ASSERT(picnum >= 0 && picnum < cpc_ncounters);
189 
190 	return (pic_events[picnum]);
191 }
192 
193 static char *
194 ni_pcbe_list_attrs(void)
195 {
196 	return ("");
197 }
198 
199 static const struct nametable *
200 find_event(int regno, char *name)
201 {
202 	const struct nametable *n;
203 
204 	n = events[regno];
205 
206 	for (; n->bits != NT_END; n++)
207 		if (strcmp(name, n->name) == 0)
208 			return (n);
209 
210 	return (NULL);
211 }
212 
213 static uint64_t
214 ni_pcbe_event_coverage(char *event)
215 {
216 	uint64_t bitmap = 0;
217 
218 	if (find_event(0, event) != NULL)
219 		bitmap = 0x1;
220 	if (find_event(1, event) != NULL)
221 		bitmap |= 0x2;
222 
223 	return (bitmap);
224 }
225 
226 /*
227  * These processors cannot tell which counter overflowed. The PCBE interface
228  * requires such processors to act as if _all_ counters had overflowed.
229  */
230 static uint64_t
231 ni_pcbe_overflow_bitmap(void)
232 {
233 	uint64_t pcr, overflow;
234 
235 	pcr = ultra_getpcr();
236 	DTRACE_PROBE1(niagara__getpcr, uint64_t, pcr);
237 	overflow =  (pcr & CPC_NIAGARA_PCR_OVF_MASK) >>
238 	    CPC_NIAGARA_PCR_OVF_SHIFT;
239 #if 0
240 	/*
241 	 * Not needed if the CPC framework is responsible to stop counters
242 	 * and that action ends up clearing overflow flags.
243 	 */
244 	if (overflow)
245 		ultra_setpcr(pcr & ~CPC_NIAGARA_PCR_OVF_MASK);
246 #endif
247 	return (overflow);
248 }
249 
250 /*ARGSUSED*/
251 static int
252 ni_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
253     uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
254 {
255 	ni_pcbe_config_t *conf;
256 	const struct nametable *n;
257 	ni_pcbe_config_t *other_config;
258 
259 	/*
260 	 * If we've been handed an existing configuration, we need only preset
261 	 * the counter value.
262 	 */
263 	if (*data != NULL) {
264 		conf = *data;
265 		conf->pcbe_pic = (uint32_t)preset;
266 		return (0);
267 	}
268 	if (picnum < 0 || picnum > 1)
269 		return (CPC_INVALID_PICNUM);
270 
271 	if (nattrs != 0)
272 		return (CPC_INVALID_ATTRIBUTE);
273 
274 	/*
275 	 * Find other requests that will be programmed with this one, and ensure
276 	 * the flags don't conflict.
277 	 */
278 	if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
279 	    (other_config->pcbe_flags != flags))
280 		return (CPC_CONFLICTING_REQS);
281 
282 	if ((n = find_event(picnum, event)) == NULL)
283 		return (CPC_INVALID_EVENT);
284 
285 	conf = kmem_alloc(sizeof (ni_pcbe_config_t), KM_SLEEP);
286 
287 	conf->pcbe_picno = picnum;
288 	conf->pcbe_bits = (uint32_t)n->bits;
289 	conf->pcbe_flags = flags;
290 	conf->pcbe_pic = (uint32_t)preset;
291 
292 	*data = conf;
293 	return (0);
294 }
295 
296 static void
297 ni_pcbe_program(void *token)
298 {
299 	ni_pcbe_config_t	*pic0;
300 	ni_pcbe_config_t	*pic1;
301 	ni_pcbe_config_t	*tmp;
302 	ni_pcbe_config_t	empty = { 1, 0x1c, 0, 0 }; /* SW_count_1 */
303 	uint64_t		pcr;
304 	uint64_t		curpic;
305 
306 	if ((pic0 = (ni_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) ==
307 	    NULL)
308 		panic("ni_pcbe: token %p has no configs", token);
309 
310 	if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) {
311 		pic1 = &empty;
312 		empty.pcbe_flags = pic0->pcbe_flags;
313 	}
314 
315 	if (pic0->pcbe_picno != 0) {
316 		/*
317 		 * pic0 is counter 1, so if we need the empty config it should
318 		 * be counter 0.
319 		 */
320 		empty.pcbe_picno = 0;
321 #if 0
322 		/* no selection for counter 0 */
323 		empty.pcbe_bits = 0x14; /* SW_count_0 - won't overflow */
324 #endif
325 		tmp = pic0;
326 		pic0 = pic1;
327 		pic1 = tmp;
328 	}
329 
330 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
331 		panic("ni_pcbe: bad config on token %p\n", token);
332 
333 	/*
334 	 * UltraSPARC does not allow pic0 to be configured differently
335 	 * from pic1. If the flags on these two configurations are
336 	 * different, they are incompatible. This condition should be
337 	 * caught at configure time.
338 	 */
339 	ASSERT(pic0->pcbe_flags == pic1->pcbe_flags);
340 
341 	ultra_setpcr(allstopped);
342 	ultra_setpic(((uint64_t)pic1->pcbe_pic << PIC1_SHIFT) |
343 	    (uint64_t)pic0->pcbe_pic);
344 
345 	pcr = (pic0->pcbe_bits & pcr_pic0_mask) << CPC_NIAGARA_PCR_PIC0_SHIFT;
346 	pcr |= (pic1->pcbe_bits & pcr_pic1_mask) << CPC_NIAGARA_PCR_PIC1_SHIFT;
347 
348 	if (pic0->pcbe_flags & CPC_COUNT_USER)
349 		pcr |= (1ull << CPC_NIAGARA_PCR_USR);
350 	if (pic0->pcbe_flags & CPC_COUNT_SYSTEM)
351 		pcr |= (1ull << CPC_NIAGARA_PCR_SYS);
352 
353 	DTRACE_PROBE1(niagara__setpcr, uint64_t, pcr);
354 	ultra_setpcr(pcr);
355 
356 	/*
357 	 * On UltraSPARC, only read-to-read counts are accurate. We cannot
358 	 * expect the value we wrote into the PIC, above, to be there after
359 	 * starting the counter. We must sample the counter value now and use
360 	 * that as the baseline for future samples.
361 	 */
362 	curpic = ultra_getpic();
363 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
364 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
365 	DTRACE_PROBE1(niagara__newpic, uint64_t, curpic);
366 }
367 
368 static void
369 ni_pcbe_allstop(void)
370 {
371 	ultra_setpcr(allstopped);
372 }
373 
374 
375 static void
376 ni_pcbe_sample(void *token)
377 {
378 	uint64_t		curpic;
379 	int64_t			diff;
380 	uint64_t		*pic0_data;
381 	uint64_t		*pic1_data;
382 	uint64_t		*dtmp;
383 	uint64_t		tmp;
384 	ni_pcbe_config_t	*pic0;
385 	ni_pcbe_config_t	*pic1;
386 	ni_pcbe_config_t	empty = { 1, 0, 0, 0 };
387 	ni_pcbe_config_t	*ctmp;
388 
389 	curpic = ultra_getpic();
390 	DTRACE_PROBE1(niagara__getpic, uint64_t, curpic);
391 
392 	if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL)
393 		panic("%s: token %p has no configs", ni_impl_name, token);
394 
395 	if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) {
396 		pic1 = &empty;
397 		pic1_data = &tmp;
398 	}
399 
400 	if (pic0->pcbe_picno != 0) {
401 		empty.pcbe_picno = 0;
402 		ctmp = pic0;
403 		pic0 = pic1;
404 		pic1 = ctmp;
405 		dtmp = pic0_data;
406 		pic0_data = pic1_data;
407 		pic1_data = dtmp;
408 	}
409 
410 	if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
411 		panic("%s: bad config on token %p\n", ni_impl_name, token);
412 
413 	diff = (curpic & PIC0_MASK) - (uint64_t)pic0->pcbe_pic;
414 	if (diff < 0)
415 		diff += (1ll << 32);
416 	*pic0_data += diff;
417 
418 	diff = (curpic >> 32) - (uint64_t)pic1->pcbe_pic;
419 	if (diff < 0)
420 		diff += (1ll << 32);
421 	*pic1_data += diff;
422 
423 	pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
424 	pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
425 }
426 
427 static void
428 ni_pcbe_free(void *config)
429 {
430 	kmem_free(config, sizeof (ni_pcbe_config_t));
431 }
432 
433 
434 static struct modlpcbe modlpcbe = {
435 	&mod_pcbeops,
436 	"UltraSPARC T1 Performance Counters",
437 	&ni_pcbe_ops
438 };
439 
440 static struct modlinkage modl = {
441 	MODREV_1,
442 	&modlpcbe,
443 };
444 
445 int
446 _init(void)
447 {
448 	if (ni_pcbe_init() != 0)
449 		return (ENOTSUP);
450 	return (mod_install(&modl));
451 }
452 
453 int
454 _fini(void)
455 {
456 	return (mod_remove(&modl));
457 }
458 
459 int
460 _info(struct modinfo *mi)
461 {
462 	return (mod_info(&modl, mi));
463 }
464