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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Niagara2 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/archsystm.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/niagara2regs.h> 42 #include <sys/hsvc.h> 43 44 static int ni2_pcbe_init(void); 45 static uint_t ni2_pcbe_ncounters(void); 46 static const char *ni2_pcbe_impl_name(void); 47 static const char *ni2_pcbe_cpuref(void); 48 static char *ni2_pcbe_list_events(uint_t picnum); 49 static char *ni2_pcbe_list_attrs(void); 50 static uint64_t ni2_pcbe_event_coverage(char *event); 51 static uint64_t ni2_pcbe_overflow_bitmap(void); 52 static int ni2_pcbe_configure(uint_t picnum, char *event, uint64_t preset, 53 uint32_t flags, uint_t nattrs, kcpc_attr_t *attrs, void **data, 54 void *token); 55 static void ni2_pcbe_program(void *token); 56 static void ni2_pcbe_allstop(void); 57 static void ni2_pcbe_sample(void *token); 58 static void ni2_pcbe_free(void *config); 59 60 extern void ultra_setpcr(uint64_t); 61 extern uint64_t ultra_getpcr(void); 62 extern void ultra_setpic(uint64_t); 63 extern uint64_t ultra_getpic(void); 64 extern uint64_t ultra_gettick(void); 65 extern char cpu_module_name[]; 66 67 pcbe_ops_t ni2_pcbe_ops = { 68 PCBE_VER_1, 69 CPC_CAP_OVERFLOW_INTERRUPT | CPC_CAP_OVERFLOW_PRECISE, 70 ni2_pcbe_ncounters, 71 ni2_pcbe_impl_name, 72 ni2_pcbe_cpuref, 73 ni2_pcbe_list_events, 74 ni2_pcbe_list_attrs, 75 ni2_pcbe_event_coverage, 76 ni2_pcbe_overflow_bitmap, 77 ni2_pcbe_configure, 78 ni2_pcbe_program, 79 ni2_pcbe_allstop, 80 ni2_pcbe_sample, 81 ni2_pcbe_free 82 }; 83 84 typedef struct _ni2_pcbe_config { 85 uint_t pcbe_picno; /* 0 for pic0 or 1 for pic1 */ 86 uint32_t pcbe_evsel; /* %pcr event code unshifted */ 87 uint32_t pcbe_flags; /* hpriv/user/system/priv */ 88 uint32_t pcbe_pic; /* unshifted raw %pic value */ 89 } ni2_pcbe_config_t; 90 91 typedef struct _ni2_event { 92 const char *name; 93 const uint32_t emask; 94 const uint32_t emask_valid; /* Mask of unreserved MASK bits */ 95 } ni2_event_t; 96 97 #define ULTRA_PCR_PRIVPIC (UINT64_C(1) << CPC_NIAGARA2_PCR_PRIVPIC_SHIFT) 98 #define EV_END {NULL, 0, 0} 99 100 static const uint64_t allstopped = ULTRA_PCR_PRIVPIC; 101 102 static ni2_event_t ni2_events[] = { 103 { "Idle_strands", 0x000, 0x00 }, 104 { "Br_completed", 0x201, 0x7f }, 105 { "Br_taken", 0x202, 0x7f }, 106 { "Instr_FGU_arithmetic", 0x204, 0x7f }, 107 { "Instr_ld", 0x208, 0x7f }, 108 { "Instr_st", 0x210, 0x7f }, 109 { "Instr_sw", 0x220, 0x7f }, 110 { "Instr_other", 0x240, 0x7f }, 111 { "Instr_cnt", 0x27d, 0x7f }, 112 { "IC_miss", 0x301, 0x3f }, 113 { "DC_miss", 0x302, 0x3f }, 114 { "ITLB_miss", 0x304, 0x3f }, 115 { "DTLB_miss", 0x308, 0x3f }, 116 { "L2_imiss", 0x310, 0x3f }, 117 { "L2_dmiss_ld", 0x320, 0x3f }, 118 { "ITLB_HWTW_ref_L2", 0x404, 0x3c }, 119 { "DTLB_HWTW_ref_L2", 0x408, 0x3c }, 120 { "ITLB_HWTW_miss_L2", 0x410, 0x3c }, 121 { "DTLB_HWTW_miss_L2", 0x420, 0x3c }, 122 { "Stream_ld_to_PCX", 0x501, 0x3f }, 123 { "Stream_st_to_PCX", 0x502, 0x3f }, 124 { "CPU_ld_to_PCX", 0x504, 0x3f }, 125 { "CPU_ifetch_to_PCX", 0x508, 0x3f }, 126 { "CPU_st_to_PCX", 0x510, 0x3f }, 127 { "MMU_ld_to_PCX", 0x520, 0x3f }, 128 { "DES_3DES_op", 0x601, 0x3f }, 129 { "AES_op", 0x602, 0x3f }, 130 { "RC4_op", 0x604, 0x3f }, 131 { "MD5_SHA-1_SHA-256_op", 0x608, 0x3f }, 132 { "MA_op", 0x610, 0x3f }, 133 { "CRC_TCPIP_cksum", 0x620, 0x3f }, 134 { "DES_3DES_busy_cycle", 0x701, 0x3f }, 135 { "AES_busy_cycle", 0x702, 0x3f }, 136 { "RC4_busy_cycle", 0x704, 0x3f }, 137 { "MD5_SHA-1_SHA-256_busy_cycle", 0x708, 0x3f }, 138 { "MA_busy_cycle", 0x710, 0x3f }, 139 { "CRC_MPA_cksum", 0x720, 0x3f }, 140 EV_END 141 }; 142 143 static const char *ni2_impl_name = "UltraSPARC T2"; 144 static char *evlist; 145 static size_t evlist_sz; 146 static uint16_t pcr_pic0_mask; 147 static uint16_t pcr_pic1_mask; 148 149 #define CPU_REF_URL " Documentation for Sun processors can be found at: " \ 150 "http://www.sun.com/processors/manuals" 151 152 static const char *niagara2_cpuref = "See the \"UltraSPARC T2 User's Manual\" " 153 "for descriptions of these events." CPU_REF_URL; 154 155 static boolean_t niagara2_hsvc_available = B_TRUE; 156 157 static int 158 ni2_pcbe_init(void) 159 { 160 ni2_event_t *evp; 161 int status; 162 uint64_t niagara2_hsvc_major; 163 uint64_t niagara2_hsvc_minor; 164 165 pcr_pic0_mask = CPC_NIAGARA2_PCR_PIC0_MASK; 166 pcr_pic1_mask = CPC_NIAGARA2_PCR_PIC1_MASK; 167 168 /* 169 * Validate API version for Niagara2 specific hypervisor services 170 */ 171 status = hsvc_version(HSVC_GROUP_NIAGARA2_CPU, &niagara2_hsvc_major, 172 &niagara2_hsvc_minor); 173 if ((status != 0) || (niagara2_hsvc_major != NIAGARA2_HSVC_MAJOR)) { 174 cmn_err(CE_WARN, "hypervisor services not negotiated " 175 "or unsupported major number: group: 0x%x major: 0x%lx " 176 "minor: 0x%lx errno: %d", HSVC_GROUP_NIAGARA2_CPU, 177 niagara2_hsvc_major, niagara2_hsvc_minor, status); 178 niagara2_hsvc_available = B_FALSE; 179 } 180 /* 181 * Construct event list. 182 * 183 * First pass: Calculate size needed. We'll need an additional byte 184 * for the NULL pointer during the last strcat. 185 * 186 * Second pass: Copy strings. 187 */ 188 for (evp = ni2_events; evp->name != NULL; evp++) 189 evlist_sz += strlen(evp->name) + 1; 190 191 evlist = kmem_alloc(evlist_sz + 1, KM_SLEEP); 192 evlist[0] = '\0'; 193 194 for (evp = ni2_events; evp->name != NULL; evp++) { 195 (void) strcat(evlist, evp->name); 196 (void) strcat(evlist, ","); 197 } 198 /* 199 * Remove trailing comma. 200 */ 201 evlist[evlist_sz - 1] = '\0'; 202 203 return (0); 204 } 205 206 static uint_t 207 ni2_pcbe_ncounters(void) 208 { 209 return (2); 210 } 211 212 static const char * 213 ni2_pcbe_impl_name(void) 214 { 215 return (ni2_impl_name); 216 } 217 218 static const char * 219 ni2_pcbe_cpuref(void) 220 { 221 return (niagara2_cpuref); 222 } 223 224 static char * 225 ni2_pcbe_list_events(uint_t picnum) 226 { 227 ASSERT(picnum < cpc_ncounters); 228 229 return (evlist); 230 } 231 232 static char * 233 ni2_pcbe_list_attrs(void) 234 { 235 if (niagara2_hsvc_available == B_TRUE) 236 return ("hpriv,emask"); 237 else 238 return ("emask"); 239 } 240 241 static ni2_event_t * 242 find_event(char *name) 243 { 244 ni2_event_t *evp; 245 246 for (evp = ni2_events; evp->name != NULL; evp++) 247 if (strcmp(name, evp->name) == 0) 248 return (evp); 249 250 return (NULL); 251 } 252 253 /*ARGSUSED*/ 254 static uint64_t 255 ni2_pcbe_event_coverage(char *event) 256 { 257 /* 258 * Fortunately, both pic0 and pic1 can count all events. 259 */ 260 return (0x3); 261 } 262 263 #ifdef N2_ERRATUM_112 264 uint64_t ni2_ov_tstamp[NCPU]; /* last overflow time stamp */ 265 uint64_t ni2_ov_spurious_range = 1000000; /* 1 msec at 1GHz */ 266 #endif 267 268 /* 269 * These processors cannot tell which counter overflowed. The PCBE interface 270 * requires such processors to act as if _all_ counters had overflowed. 271 */ 272 static uint64_t 273 ni2_pcbe_overflow_bitmap(void) 274 { 275 uint64_t pcr, overflow; 276 uint64_t pic; 277 uint32_t pic0, pic1; 278 boolean_t update_pic = B_FALSE; 279 #ifdef N2_ERRATUM_112 280 uint64_t tstamp; 281 processorid_t cpun; 282 #endif 283 284 ASSERT(getpil() >= DISP_LEVEL); 285 pcr = ultra_getpcr(); 286 DTRACE_PROBE1(niagara2__getpcr, uint64_t, pcr); 287 overflow = (pcr & CPC_NIAGARA2_PCR_OV0_MASK) >> 288 CPC_NIAGARA2_PCR_OV0_SHIFT; 289 overflow |= (pcr & CPC_NIAGARA2_PCR_OV1_MASK) >> 290 CPC_NIAGARA2_PCR_OV1_SHIFT; 291 #ifdef N2_ERRATUM_112 292 /* 293 * Niagara2 1.x silicon can generate a duplicate overflow trap per 294 * event. If we take an overflow trap with no counters overflowing, 295 * return a non-zero bitmask with no OV bit set for supported 296 * counter so that the framework can ignore this trap. 297 */ 298 cpun = CPU->cpu_id; 299 tstamp = ultra_gettick(); 300 if (overflow) 301 ni2_ov_tstamp[cpun] = tstamp; 302 else if (tstamp < (ni2_ov_tstamp[cpun] + ni2_ov_spurious_range)) 303 overflow |= 1ULL << 63; 304 #endif 305 pic = ultra_getpic(); 306 pic0 = (uint32_t)(pic & PIC0_MASK); 307 pic1 = (uint32_t)((pic >> PIC1_SHIFT) & PIC0_MASK); 308 309 #ifdef N2_ERRATUM_134 310 /* 311 * In Niagara2 1.x silicon, PMU doesn't set OV bit for precise events. 312 * So, if we take a trap with the counter within the overflow range 313 * and the OV bit is not set, we assume OV bit should have been set. 314 */ 315 316 if (PIC_IN_OV_RANGE(pic0)) 317 overflow |= 0x1; 318 if (PIC_IN_OV_RANGE(pic1)) 319 overflow |= 0x2; 320 #endif 321 /* 322 * Reset the pic, if it is within the overflow range. 323 */ 324 if ((overflow & 0x1) && (PIC_IN_OV_RANGE(pic0))) { 325 pic0 = 0; 326 update_pic = B_TRUE; 327 } 328 if ((overflow & 0x2) && (PIC_IN_OV_RANGE(pic1))) { 329 pic1 = 0; 330 update_pic = B_TRUE; 331 } 332 333 if (update_pic) 334 ultra_setpic(((uint64_t)pic1 << PIC1_SHIFT) | pic0); 335 336 return (overflow); 337 } 338 339 /*ARGSUSED*/ 340 static int 341 ni2_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 ni2_pcbe_config_t *cfg; 345 ni2_pcbe_config_t *other_config; 346 ni2_event_t *evp; 347 int i; 348 uint32_t evsel; 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 cfg = *data; 356 cfg->pcbe_pic = (uint32_t)preset; 357 return (0); 358 } 359 360 if (picnum > 1) 361 return (CPC_INVALID_PICNUM); 362 363 if ((evp = find_event(event)) == NULL) 364 return (CPC_INVALID_EVENT); 365 366 evsel = evp->emask; 367 368 for (i = 0; i < nattrs; i++) { 369 if (strcmp(attrs[i].ka_name, "hpriv") == 0) { 370 if (attrs[i].ka_val != 0) 371 flags |= CPC_COUNT_HPRIV; 372 } else if (strcmp(attrs[i].ka_name, "emask") == 0) { 373 if ((attrs[i].ka_val | evp->emask_valid) != 374 evp->emask_valid) 375 return (CPC_ATTRIBUTE_OUT_OF_RANGE); 376 evsel |= attrs[i].ka_val; 377 } else 378 return (CPC_INVALID_ATTRIBUTE); 379 } 380 381 /* 382 * Find other requests that will be programmed with this one, and ensure 383 * the flags don't conflict. 384 */ 385 if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) && 386 (other_config->pcbe_flags != flags)) 387 return (CPC_CONFLICTING_REQS); 388 389 cfg = kmem_alloc(sizeof (*cfg), KM_SLEEP); 390 391 cfg->pcbe_picno = picnum; 392 cfg->pcbe_evsel = evsel; 393 cfg->pcbe_flags = flags; 394 cfg->pcbe_pic = (uint32_t)preset; 395 396 *data = cfg; 397 return (0); 398 } 399 400 static void 401 ni2_pcbe_program(void *token) 402 { 403 ni2_pcbe_config_t *pic0; 404 ni2_pcbe_config_t *pic1; 405 ni2_pcbe_config_t *tmp; 406 ni2_pcbe_config_t nullcfg = { 1, 0, 0, 0 }; 407 uint64_t pcr; 408 uint64_t curpic; 409 uint64_t toe; 410 411 /* enable trap-on-event for pic0 and pic1 */ 412 toe = (CPC_COUNT_TOE0 | CPC_COUNT_TOE1); 413 414 if ((pic0 = (ni2_pcbe_config_t *)kcpc_next_config(token, NULL, NULL)) == 415 NULL) 416 panic("ni2_pcbe: token %p has no configs", token); 417 418 if ((pic1 = kcpc_next_config(token, pic0, NULL)) == NULL) { 419 pic1 = &nullcfg; 420 nullcfg.pcbe_flags = pic0->pcbe_flags; 421 toe = CPC_COUNT_TOE0; /* enable trap-on-event for pic0 */ 422 } 423 424 if (pic0->pcbe_picno != 0) { 425 /* 426 * pic0 is counter 1, so if we need the null config it should 427 * be counter 0. 428 */ 429 nullcfg.pcbe_picno = 0; 430 tmp = pic0; 431 pic0 = pic1; 432 pic1 = tmp; 433 toe = CPC_COUNT_TOE1; /* enable trap-on-event for pic1 */ 434 } 435 436 if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1) 437 panic("%s: bad config on token %p\n", ni2_impl_name, token); 438 439 /* 440 * UltraSPARC does not allow pic0 to be configured differently 441 * from pic1. If the flags on these two configurations are 442 * different, they are incompatible. This condition should be 443 * caught at configure time. 444 */ 445 ASSERT(pic0->pcbe_flags == pic1->pcbe_flags); 446 447 ultra_setpcr(allstopped); 448 ultra_setpic(((uint64_t)pic1->pcbe_pic << PIC1_SHIFT) | 449 (uint64_t)pic0->pcbe_pic); 450 451 pcr = (pic0->pcbe_evsel & pcr_pic0_mask) << CPC_NIAGARA2_PCR_PIC0_SHIFT; 452 pcr |= (pic1->pcbe_evsel & pcr_pic1_mask) << 453 CPC_NIAGARA2_PCR_PIC1_SHIFT; 454 455 if (pic0->pcbe_flags & CPC_COUNT_USER) 456 pcr |= (1ull << CPC_NIAGARA2_PCR_USR_SHIFT); 457 if (pic0->pcbe_flags & CPC_COUNT_SYSTEM) 458 pcr |= (1ull << CPC_NIAGARA2_PCR_SYS_SHIFT); 459 if (pic0->pcbe_flags & CPC_COUNT_HPRIV) 460 pcr |= (1ull << CPC_NIAGARA2_PCR_HPRIV_SHIFT); 461 pcr |= toe; 462 463 DTRACE_PROBE1(niagara2__setpcr, uint64_t, pcr); 464 465 /* 466 * PCR is set by HV using API call hv_niagara_setperf(). 467 * Silently ignore hvpriv events if access is denied. 468 */ 469 if (pic0->pcbe_flags & CPC_COUNT_HPRIV) { 470 if (hv_niagara_setperf(HV_NIAGARA_SPARC_CTL, pcr) != 0) 471 ultra_setpcr(pcr); 472 } else 473 ultra_setpcr(pcr); 474 475 /* 476 * On UltraSPARC, only read-to-read counts are accurate. We cannot 477 * expect the value we wrote into the PIC, above, to be there after 478 * starting the counter. We must sample the counter value now and use 479 * that as the baseline for future samples. 480 */ 481 curpic = ultra_getpic(); 482 pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK); 483 pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT); 484 485 DTRACE_PROBE1(niagara2__newpic, uint64_t, curpic); 486 } 487 488 static void 489 ni2_pcbe_allstop(void) 490 { 491 ultra_setpcr(allstopped); 492 } 493 494 static void 495 ni2_pcbe_sample(void *token) 496 { 497 uint64_t curpic; 498 int64_t diff; 499 uint64_t *pic0_data; 500 uint64_t *pic1_data; 501 uint64_t *dtmp; 502 uint64_t tmp; 503 ni2_pcbe_config_t *pic0; 504 ni2_pcbe_config_t *pic1; 505 ni2_pcbe_config_t nullcfg = { 1, 0, 0, 0 }; 506 ni2_pcbe_config_t *ctmp; 507 508 curpic = ultra_getpic(); 509 DTRACE_PROBE1(niagara2__getpic, uint64_t, curpic); 510 511 if ((pic0 = kcpc_next_config(token, NULL, &pic0_data)) == NULL) 512 panic("%s: token %p has no configs", ni2_impl_name, token); 513 514 if ((pic1 = kcpc_next_config(token, pic0, &pic1_data)) == NULL) { 515 pic1 = &nullcfg; 516 pic1_data = &tmp; 517 } 518 519 if (pic0->pcbe_picno != 0) { 520 nullcfg.pcbe_picno = 0; 521 ctmp = pic0; 522 pic0 = pic1; 523 pic1 = ctmp; 524 dtmp = pic0_data; 525 pic0_data = pic1_data; 526 pic1_data = dtmp; 527 } 528 529 if (pic0->pcbe_picno != 0 || pic1->pcbe_picno != 1) 530 panic("%s: bad config on token %p\n", ni2_impl_name, token); 531 532 diff = (curpic & PIC0_MASK) - (uint64_t)pic0->pcbe_pic; 533 if (diff < 0) 534 diff += (1ll << 32); 535 *pic0_data += diff; 536 537 diff = (curpic >> 32) - (uint64_t)pic1->pcbe_pic; 538 if (diff < 0) 539 diff += (1ll << 32); 540 *pic1_data += diff; 541 542 pic0->pcbe_pic = (uint32_t)(curpic & PIC0_MASK); 543 pic1->pcbe_pic = (uint32_t)(curpic >> PIC1_SHIFT); 544 } 545 546 static void 547 ni2_pcbe_free(void *config) 548 { 549 kmem_free(config, sizeof (ni2_pcbe_config_t)); 550 } 551 552 553 static struct modlpcbe modlpcbe = { 554 &mod_pcbeops, 555 "UltraSPARC T2 Performance Counters v%I%", 556 &ni2_pcbe_ops 557 }; 558 559 static struct modlinkage modl = { 560 MODREV_1, 561 &modlpcbe, 562 }; 563 564 int 565 _init(void) 566 { 567 if (ni2_pcbe_init() != 0) 568 return (ENOTSUP); 569 return (mod_install(&modl)); 570 } 571 572 int 573 _fini(void) 574 { 575 return (mod_remove(&modl)); 576 } 577 578 int 579 _info(struct modinfo *mi) 580 { 581 return (mod_info(&modl, mi)); 582 } 583