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 = ∅ 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 = ∅ 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