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 2009 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_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_fp_ins", "FP_instr_cnt" },
174 { "PAPI_l1_icm", "IC_miss" },
175 { "PAPI_l1_dcm", "DC_miss" },
176 { "PAPI_tlb_im", "ITLB_miss" },
177 { "PAPI_tlb_dm", "DTLB_miss" },
178 { NULL, NULL }
179 };
180
181 static const ni_generic_event_t *Niagara_generic_names[2] = {
182 Niagara_generic_names0,
183 Niagara_generic_names1
184 };
185
186 static const struct nametable **events;
187 static const ni_generic_event_t **generic_events;
188 static const char *ni_impl_name = "UltraSPARC T1";
189 static char *pic_events[2];
190 static uint16_t pcr_pic0_mask;
191 static uint16_t pcr_pic1_mask;
192
193 #define CPU_REF_URL " Documentation for Sun processors can be found at: " \
194 "http://www.sun.com/processors/manuals"
195
196 static const char *niagara_cpuref = "See the \"UltraSPARC T1 User's Manual\" "
197 "for descriptions of these events." CPU_REF_URL;
198
199 static int
ni_pcbe_init(void)200 ni_pcbe_init(void)
201 {
202 const struct nametable *n;
203 const ni_generic_event_t *gevp;
204 int i;
205 size_t size;
206
207 events = Niagara_names;
208 generic_events = Niagara_generic_names;
209 pcr_pic0_mask = CPC_PCR_PIC0_MASK;
210 pcr_pic1_mask = CPC_PCR_PIC1_MASK;
211
212 /*
213 * Initialize the list of events for each PIC.
214 * Do two passes: one to compute the size necessary and another
215 * to copy the strings. Need room for event, comma, and NULL terminator.
216 */
217 for (i = 0; i < 2; i++) {
218 size = 0;
219 for (n = events[i]; n->bits != NT_END; n++)
220 size += strlen(n->name) + 1;
221 for (gevp = generic_events[i]; gevp->name != NULL; gevp++)
222 size += strlen(gevp->name) + 1;
223 pic_events[i] = kmem_alloc(size + 1, KM_SLEEP);
224 *pic_events[i] = '\0';
225 for (n = events[i]; n->bits != NT_END; n++) {
226 (void) strcat(pic_events[i], n->name);
227 (void) strcat(pic_events[i], ",");
228 }
229 for (gevp = generic_events[i]; gevp->name != NULL; gevp++) {
230 (void) strcat(pic_events[i], gevp->name);
231 (void) strcat(pic_events[i], ",");
232 }
233 /*
234 * Remove trailing comma.
235 */
236 pic_events[i][size - 1] = '\0';
237 }
238
239 return (0);
240 }
241
242 static uint_t
ni_pcbe_ncounters(void)243 ni_pcbe_ncounters(void)
244 {
245 return (2);
246 }
247
248 static const char *
ni_pcbe_impl_name(void)249 ni_pcbe_impl_name(void)
250 {
251 return (ni_impl_name);
252 }
253
254 static const char *
ni_pcbe_cpuref(void)255 ni_pcbe_cpuref(void)
256 {
257 return (niagara_cpuref);
258 }
259
260 static char *
ni_pcbe_list_events(uint_t picnum)261 ni_pcbe_list_events(uint_t picnum)
262 {
263 ASSERT(picnum >= 0 && picnum < cpc_ncounters);
264
265 return (pic_events[picnum]);
266 }
267
268 static char *
ni_pcbe_list_attrs(void)269 ni_pcbe_list_attrs(void)
270 {
271 return ("");
272 }
273
274 static const ni_generic_event_t *
find_generic_event(int regno,char * name)275 find_generic_event(int regno, char *name)
276 {
277 const ni_generic_event_t *gevp;
278
279 for (gevp = generic_events[regno]; gevp->name != NULL; gevp++) {
280 if (strcmp(gevp->name, name) == 0)
281 return (gevp);
282 }
283
284 return (NULL);
285 }
286
287 static const struct nametable *
find_event(int regno,char * name)288 find_event(int regno, char *name)
289 {
290 const struct nametable *n;
291
292 n = events[regno];
293
294 for (; n->bits != NT_END; n++)
295 if (strcmp(name, n->name) == 0)
296 return (n);
297
298 return (NULL);
299 }
300
301 static uint64_t
ni_pcbe_event_coverage(char * event)302 ni_pcbe_event_coverage(char *event)
303 {
304 uint64_t bitmap = 0;
305
306 if ((find_event(0, event) != NULL) ||
307 (find_generic_event(0, event) != NULL))
308 bitmap = 0x1;
309 if ((find_event(1, event) != NULL) ||
310 (find_generic_event(1, event) != NULL))
311 bitmap |= 0x2;
312
313 return (bitmap);
314 }
315
316 /*
317 * These processors cannot tell which counter overflowed. The PCBE interface
318 * requires such processors to act as if _all_ counters had overflowed.
319 */
320 static uint64_t
ni_pcbe_overflow_bitmap(void)321 ni_pcbe_overflow_bitmap(void)
322 {
323 uint64_t pcr, overflow;
324
325 pcr = ultra_getpcr();
326 DTRACE_PROBE1(niagara__getpcr, uint64_t, pcr);
327 overflow = (pcr & CPC_PCR_OVF_MASK) >>
328 CPC_PCR_OVF_SHIFT;
329 #if 0
330 /*
331 * Not needed if the CPC framework is responsible to stop counters
332 * and that action ends up clearing overflow flags.
333 */
334 if (overflow)
335 ultra_setpcr(pcr & ~CPC_PCR_OVF_MASK);
336 #endif
337 return (overflow);
338 }
339
340 /*ARGSUSED*/
341 static int
ni_pcbe_configure(uint_t picnum,char * event,uint64_t preset,uint32_t flags,uint_t nattrs,kcpc_attr_t * attrs,void ** data,void * token)342 ni_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags,
343 uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token)
344 {
345 ni_pcbe_config_t *conf;
346 const struct nametable *n;
347 const ni_generic_event_t *gevp;
348 ni_pcbe_config_t *other_config;
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 conf = *data;
356 conf->pcbe_pic = (uint32_t)preset;
357 return (0);
358 }
359 if (picnum < 0 || picnum > 1)
360 return (CPC_INVALID_PICNUM);
361
362 if (nattrs != 0)
363 return (CPC_INVALID_ATTRIBUTE);
364
365 /*
366 * Find other requests that will be programmed with this one, and ensure
367 * the flags don't conflict.
368 */
369 if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) &&
370 (other_config->pcbe_flags != flags))
371 return (CPC_CONFLICTING_REQS);
372
373 if ((n = find_event(picnum, event)) == NULL) {
374 if ((gevp = find_generic_event(picnum, event)) != NULL) {
375 n = find_event(picnum, gevp->event);
376 ASSERT(n != NULL);
377 } else {
378 return (CPC_INVALID_EVENT);
379 }
380 }
381
382 conf = kmem_alloc(sizeof (ni_pcbe_config_t), KM_SLEEP);
383
384 conf->pcbe_picno = picnum;
385 conf->pcbe_bits = (uint32_t)n->bits;
386 conf->pcbe_flags = flags;
387 conf->pcbe_pic = (uint32_t)preset;
388
389 *data = conf;
390 return (0);
391 }
392
393 static void
ni_pcbe_program(void * token)394 ni_pcbe_program(void *token)
395 {
396 ni_pcbe_config_t *pic0;
397 ni_pcbe_config_t *pic1;
398 ni_pcbe_config_t *tmp;
399 ni_pcbe_config_t empty = { 1, 0x1c, 0, 0 }; /* SW_count_1 */
400 uint64_t pcr;
401 uint64_t curpic;
402
403 if ((pic0 = (ni_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) ==
404 NULL)
405 panic("ni_pcbe: token %p has no configs", token);
406
407 if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) {
408 pic1 = ∅
409 empty.pcbe_flags = pic0->pcbe_flags;
410 }
411
412 if (pic0->pcbe_picno != 0) {
413 /*
414 * pic0 is counter 1, so if we need the empty config it should
415 * be counter 0.
416 */
417 empty.pcbe_picno = 0;
418 #if 0
419 /* no selection for counter 0 */
420 empty.pcbe_bits = 0x14; /* SW_count_0 - won't overflow */
421 #endif
422 tmp = pic0;
423 pic0 = pic1;
424 pic1 = tmp;
425 }
426
427 if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
428 panic("ni_pcbe: bad config on token %p\n", token);
429
430 /*
431 * UltraSPARC does not allow pic0 to be configured differently
432 * from pic1. If the flags on these two configurations are
433 * different, they are incompatible. This condition should be
434 * caught at configure time.
435 */
436 ASSERT(pic0->pcbe_flags == pic1->pcbe_flags);
437
438 ultra_setpcr(allstopped);
439 ultra_setpic(((uint64_t)pic1->pcbe_pic << PIC1_SHIFT) |
440 (uint64_t)pic0->pcbe_pic);
441
442 pcr = (pic0->pcbe_bits & pcr_pic0_mask) << CPC_PCR_PIC0_SHIFT;
443 pcr |= (pic1->pcbe_bits & pcr_pic1_mask) << CPC_PCR_PIC1_SHIFT;
444
445 if (pic0->pcbe_flags & CPC_COUNT_USER)
446 pcr |= (1ull << CPC_PCR_USR);
447 if (pic0->pcbe_flags & CPC_COUNT_SYSTEM)
448 pcr |= (1ull << CPC_PCR_SYS);
449
450 DTRACE_PROBE1(niagara__setpcr, uint64_t, pcr);
451 ultra_setpcr(pcr);
452
453 /*
454 * On UltraSPARC, only read-to-read counts are accurate. We cannot
455 * expect the value we wrote into the PIC, above, to be there after
456 * starting the counter. We must sample the counter value now and use
457 * that as the baseline for future samples.
458 */
459 curpic = ultra_getpic();
460 pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
461 pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
462 DTRACE_PROBE1(niagara__newpic, uint64_t, curpic);
463 }
464
465 static void
ni_pcbe_allstop(void)466 ni_pcbe_allstop(void)
467 {
468 ultra_setpcr(allstopped);
469 }
470
471
472 static void
ni_pcbe_sample(void * token)473 ni_pcbe_sample(void *token)
474 {
475 uint64_t curpic;
476 int64_t diff;
477 uint64_t *pic0_data;
478 uint64_t *pic1_data;
479 uint64_t *dtmp;
480 uint64_t tmp;
481 ni_pcbe_config_t *pic0;
482 ni_pcbe_config_t *pic1;
483 ni_pcbe_config_t empty = { 1, 0, 0, 0 };
484 ni_pcbe_config_t *ctmp;
485
486 curpic = ultra_getpic();
487 DTRACE_PROBE1(niagara__getpic, uint64_t, curpic);
488
489 if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL)
490 panic("%s: token %p has no configs", ni_impl_name, token);
491
492 if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) {
493 pic1 = ∅
494 pic1_data = &tmp;
495 }
496
497 if (pic0->pcbe_picno != 0) {
498 empty.pcbe_picno = 0;
499 ctmp = pic0;
500 pic0 = pic1;
501 pic1 = ctmp;
502 dtmp = pic0_data;
503 pic0_data = pic1_data;
504 pic1_data = dtmp;
505 }
506
507 if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1)
508 panic("%s: bad config on token %p\n", ni_impl_name, token);
509
510 diff = (curpic & PIC0_MASK) - (uint64_t)pic0->pcbe_pic;
511 if (diff < 0)
512 diff += (1ll << 32);
513 *pic0_data += diff;
514
515 diff = (curpic >> 32) - (uint64_t)pic1->pcbe_pic;
516 if (diff < 0)
517 diff += (1ll << 32);
518 *pic1_data += diff;
519
520 pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK);
521 pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT);
522 }
523
524 static void
ni_pcbe_free(void * config)525 ni_pcbe_free(void *config)
526 {
527 kmem_free(config, sizeof (ni_pcbe_config_t));
528 }
529
530
531 static struct modlpcbe modlpcbe = {
532 &mod_pcbeops,
533 "UltraSPARC T1 Performance Counters",
534 &ni_pcbe_ops
535 };
536
537 static struct modlinkage modl = {
538 MODREV_1,
539 &modlpcbe,
540 };
541
542 int
_init(void)543 _init(void)
544 {
545 if (ni_pcbe_init() != 0)
546 return (ENOTSUP);
547 return (mod_install(&modl));
548 }
549
550 int
_fini(void)551 _fini(void)
552 {
553 return (mod_remove(&modl));
554 }
555
556 int
_info(struct modinfo * mi)557 _info(struct modinfo *mi)
558 {
559 return (mod_info(&modl, mi));
560 }
561