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