niagara_pcbe.c revision 7640:c468bb5b1d2d
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
80static int ni_pcbe_init(void);
81static uint_t ni_pcbe_ncounters(void);
82static const char *ni_pcbe_impl_name(void);
83static const char *ni_pcbe_cpuref(void);
84static char *ni_pcbe_list_events(uint_t picnum);
85static char *ni_pcbe_list_attrs(void);
86static uint64_t ni_pcbe_event_coverage(char *event);
87static uint64_t ni_pcbe_overflow_bitmap(void);
88static 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);
91static void ni_pcbe_program(void *token);
92static void ni_pcbe_allstop(void);
93static void ni_pcbe_sample(void *token);
94static void ni_pcbe_free(void *config);
95
96extern void ultra_setpcr(uint64_t);
97extern uint64_t ultra_getpcr(void);
98extern void ultra_setpic(uint64_t);
99extern uint64_t ultra_getpic(void);
100extern uint64_t ultra_gettick(void);
101
102pcbe_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
119typedef 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
126struct nametable {
127	const uint8_t	bits;
128	const char	*name;
129};
130
131typedef 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
140static const uint64_t   allstopped = ULTRA_PCR_PRIVPIC;
141
142static const struct nametable Niagara_names1[] = {
143	{0x00, "Instr_cnt"},
144	{NT_END, ""}
145};
146
147static 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
159static const struct nametable *Niagara_names[2] = {
160	Niagara_names0,
161	Niagara_names1
162};
163
164static const ni_generic_event_t Niagara_generic_names1[] = {
165	{ "PAPI_tot_ins",	"Instr_cnt" },
166	{ NULL,			NULL }
167};
168
169static 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
180static const ni_generic_event_t *Niagara_generic_names[2] = {
181	Niagara_generic_names0,
182	Niagara_generic_names1
183};
184
185static const struct nametable **events;
186static const ni_generic_event_t **generic_events;
187static const char *ni_impl_name = "UltraSPARC T1";
188static char *pic_events[2];
189static uint16_t pcr_pic0_mask;
190static 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
195static const char *niagara_cpuref = "See the \"UltraSPARC T1 User's Manual\" "
196			"for descriptions of these events." CPU_REF_URL;
197
198static int
199ni_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
241static uint_t
242ni_pcbe_ncounters(void)
243{
244	return (2);
245}
246
247static const char *
248ni_pcbe_impl_name(void)
249{
250	return (ni_impl_name);
251}
252
253static const char *
254ni_pcbe_cpuref(void)
255{
256	return (niagara_cpuref);
257}
258
259static char *
260ni_pcbe_list_events(uint_t picnum)
261{
262	ASSERT(picnum >= 0 && picnum < cpc_ncounters);
263
264	return (pic_events[picnum]);
265}
266
267static char *
268ni_pcbe_list_attrs(void)
269{
270	return ("");
271}
272
273static const ni_generic_event_t *
274find_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
286static const struct nametable *
287find_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
300static uint64_t
301ni_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 */
319static uint64_t
320ni_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*/
340static int
341ni_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
392static void
393ni_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
464static void
465ni_pcbe_allstop(void)
466{
467	ultra_setpcr(allstopped);
468}
469
470
471static void
472ni_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
523static void
524ni_pcbe_free(void *config)
525{
526	kmem_free(config, sizeof (ni_pcbe_config_t));
527}
528
529
530static struct modlpcbe modlpcbe = {
531	&mod_pcbeops,
532	"UltraSPARC T1 Performance Counters",
533	&ni_pcbe_ops
534};
535
536static struct modlinkage modl = {
537	MODREV_1,
538	&modlpcbe,
539};
540
541int
542_init(void)
543{
544	if (ni_pcbe_init() != 0)
545		return (ENOTSUP);
546	return (mod_install(&modl));
547}
548
549int
550_fini(void)
551{
552	return (mod_remove(&modl));
553}
554
555int
556_info(struct modinfo *mi)
557{
558	return (mod_info(&modl, mi));
559}
560