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