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 * SPARC64 VI 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/cpu_impl.h> 41 42 static int opl_pcbe_init(void); 43 static uint_t opl_pcbe_ncounters(void); 44 static const char *opl_pcbe_impl_name(void); 45 static const char *opl_pcbe_cpuref(void); 46 static char *opl_pcbe_list_events(uint_t picnum); 47 static char *opl_pcbe_list_attrs(void); 48 static uint64_t opl_pcbe_event_coverage(char *event); 49 static uint64_t opl_pcbe_overflow_bitmap(void); 50 static int opl_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 opl_pcbe_program(void *token); 54 static void opl_pcbe_allstop(void); 55 static void opl_pcbe_sample(void *token); 56 static void opl_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 opl_pcbe_ops = { 65 PCBE_VER_1, 66 CPC_CAP_OVERFLOW_INTERRUPT, 67 opl_pcbe_ncounters, 68 opl_pcbe_impl_name, 69 opl_pcbe_cpuref, 70 opl_pcbe_list_events, 71 opl_pcbe_list_attrs, 72 opl_pcbe_event_coverage, 73 opl_pcbe_overflow_bitmap, 74 opl_pcbe_configure, 75 opl_pcbe_program, 76 opl_pcbe_allstop, 77 opl_pcbe_sample, 78 opl_pcbe_free 79 }; 80 81 typedef struct _opl_pcbe_config { 82 uint8_t opl_picno; /* From 0 to 7 */ 83 uint32_t opl_bits; /* %pcr event code unshifted */ 84 uint32_t opl_flags; /* user/system/priv */ 85 uint32_t opl_pic; /* unshifted raw %pic value */ 86 } opl_pcbe_config_t; 87 88 struct nametable { 89 const uint8_t bits; 90 const char *name; 91 }; 92 93 /* 94 * Performance Control Register (PCR) 95 * 96 * +----------+-----+-----+------+----+ 97 * | 0 | OVF | 0 | OVR0 | 0 | 98 * +----------+-----+-----+------+----+ 99 * 63 48 47:32 31:27 26 25 100 * 101 * +----+----+--- -+----+-----+---+-----+-----+----+----+----+ 102 * | NC | 0 | SC | 0 | SU | 0 | SL |ULRO | UT | ST |PRIV| 103 * +----+----+-----+----+-----+---+-----+-----+----+----+----+ 104 * 24:22 21 20:18 17 16:11 10 9:4 3 2 1 0 105 * 106 * 107 * Performance Instrumentation Counter (PIC) 108 * Four PICs are implemented in SPARC64 VI, 109 * each PIC is accessed using PCR.SC as a select field. 110 * 111 * +------------------------+--------------------------+ 112 * | PICU | PICL | 113 * +------------------------+--------------------------+ 114 * 63 32 31 0 115 */ 116 117 #define PIC_MASK (((uint64_t)1 << 32) - 1) 118 119 #define SPARC64_VI_PCR_PRIVPIC UINT64_C(0) 120 121 #define CPC_SPARC64_VI_PCR_SYS_SHIFT 1 122 #define CPC_SPARC64_VI_PCR_USR_SHIFT 2 123 124 #define CPC_SPARC64_VI_PCR_PICL_SHIFT 4 125 #define CPC_SPARC64_VI_PCR_PICU_SHIFT 11 126 #define CPC_SPARC64_VI_PCR_PIC_MASK UINT64_C(0x3F) 127 128 #define CPC_SPARC64_VI_NPIC 8 129 130 #define CPC_SPARC64_VI_PCR_ULRO_SHIFT 3 131 #define CPC_SPARC64_VI_PCR_SC_SHIFT 18 132 #define CPC_SPARC64_VI_PCR_SC_MASK UINT64_C(0x7) 133 #define CPC_SPARC64_VI_PCR_NC_SHIFT 22 134 #define CPC_SPARC64_VI_PCR_NC_MASK UINT64_C(0x7) 135 #define CPC_SPARC64_VI_PCR_OVRO_SHIFT 26 136 #define CPC_SPARC64_VI_PCR_OVF_SHIFT 32 137 #define CPC_SPARC64_VI_PCR_OVF_MASK UINT64_C(0xffff) 138 139 #define SPARC64_VI_PCR_SYS (UINT64_C(1) << CPC_SPARC64_VI_PCR_SYS_SHIFT) 140 #define SPARC64_VI_PCR_USR (UINT64_C(1) << CPC_SPARC64_VI_PCR_USR_SHIFT) 141 #define SPARC64_VI_PCR_ULRO (UINT64_C(1) << CPC_SPARC64_VI_PCR_ULRO_SHIFT) 142 #define SPARC64_VI_PCR_OVRO (UINT64_C(1) << CPC_SPARC64_VI_PCR_OVRO_SHIFT) 143 #define SPARC64_VI_PCR_OVF (CPC_SPARC64_VI_PCR_OVF_MASK << \ 144 CPC_SPARC64_VI_PCR_OVF_SHIFT) 145 146 #define SPARC64_VI_NUM_PIC_PAIRS 4 147 148 #define SPARC64_VI_PCR_SEL_PIC(pcr, picno) { \ 149 pcr &= ~((CPC_SPARC64_VI_PCR_SC_MASK \ 150 << CPC_SPARC64_VI_PCR_SC_SHIFT)); \ 151 \ 152 pcr |= (((picno) & CPC_SPARC64_VI_PCR_SC_MASK) \ 153 << CPC_SPARC64_VI_PCR_SC_SHIFT); \ 154 } 155 156 #define SPARC64_VI_PCR_SEL_EVENT(pcr, sl, su) { \ 157 pcr &= ~((CPC_SPARC64_VI_PCR_PIC_MASK \ 158 << CPC_SPARC64_VI_PCR_PICL_SHIFT) \ 159 | (CPC_SPARC64_VI_PCR_PIC_MASK \ 160 << CPC_SPARC64_VI_PCR_PICU_SHIFT)); \ 161 \ 162 pcr |= (((sl) & CPC_SPARC64_VI_PCR_PIC_MASK) \ 163 << CPC_SPARC64_VI_PCR_PICL_SHIFT); \ 164 pcr |= (((su) & CPC_SPARC64_VI_PCR_PIC_MASK) \ 165 << CPC_SPARC64_VI_PCR_PICU_SHIFT); \ 166 } 167 168 #define NT_END 0xFF 169 170 static const uint64_t allstopped = SPARC64_VI_PCR_PRIVPIC | 171 SPARC64_VI_PCR_ULRO | SPARC64_VI_PCR_OVRO; 172 173 #define SPARC64_VI_EVENTS_comm \ 174 {0x0, "cycle_counts"}, \ 175 {0x1, "instruction_counts"}, \ 176 {0x8, "load_store_instructions"}, \ 177 {0x9, "branch_instructions"}, \ 178 {0xa, "floating_instructions"}, \ 179 {0xb, "impdep2_instructions"}, \ 180 {0xc, "prefetch_instructions"} 181 182 static const struct nametable SPARC64_VI_names_l0[] = { 183 SPARC64_VI_EVENTS_comm, 184 {0x16, "trap_int_vector"}, 185 {0x20, "write_op_uTLB"}, 186 {0x30, "sx_miss_wait_pf"}, 187 {0x31, "jbus_cpi_count"}, 188 {NT_END, ""} 189 }; 190 191 static const struct nametable SPARC64_VI_names_u0[] = { 192 SPARC64_VI_EVENTS_comm, 193 {0x16, "trap_all"}, 194 {0x20, "write_if_uTLB"}, 195 {0x30, "sx_miss_wait_dm"}, 196 {0x31, "jbus_bi_count"}, 197 {NT_END, ""} 198 }; 199 200 static const struct nametable SPARC64_VI_names_l1[] = { 201 SPARC64_VI_EVENTS_comm, 202 {0x16, "trap_spill"}, 203 {0x20, "write_op_uTLB"}, 204 {0x30, "sx_miss_count_pf"}, 205 {0x31, "jbus_cpd_count"}, 206 {NT_END, ""} 207 }; 208 209 static const struct nametable SPARC64_VI_names_u1[] = { 210 SPARC64_VI_EVENTS_comm, 211 {0x16, "trap_int_level"}, 212 {0x20, "write_if_uTLB"}, 213 {0x30, "sx_miss_count_dm"}, 214 {0x31, "jbus_cpb_count"}, 215 {NT_END, ""} 216 }; 217 218 static const struct nametable SPARC64_VI_names_l2[] = { 219 SPARC64_VI_EVENTS_comm, 220 {0x16, "trap_trap_inst"}, 221 {0x20, "op_r_iu_req_mi_go"}, 222 {0x30, "sx_read_count_pf"}, 223 {NT_END, ""} 224 }; 225 226 static const struct nametable SPARC64_VI_names_u2[] = { 227 SPARC64_VI_EVENTS_comm, 228 {0x16, "trap_fill"}, 229 {0x20, "if_r_iu_req_mi_go"}, 230 {0x30, "sx_read_count_dm"}, 231 {NT_END, ""} 232 }; 233 234 static const struct nametable SPARC64_VI_names_l3[] = { 235 SPARC64_VI_EVENTS_comm, 236 {0x16, "trap_DMMU_miss"}, 237 {0x20, "op_wait_all"}, 238 {0x30, "dvp_count_pf"}, 239 {NT_END, ""} 240 }; 241 242 static const struct nametable SPARC64_VI_names_u3[] = { 243 SPARC64_VI_EVENTS_comm, 244 {0x16, "trap_IMMU_miss"}, 245 {0x20, "if_wait_all"}, 246 {0x30, "dvp_count_dm"}, 247 {NT_END, ""} 248 }; 249 250 #undef SPARC64_VI_EVENTS_comm 251 252 static const struct nametable *SPARC64_VI_names[CPC_SPARC64_VI_NPIC] = { 253 SPARC64_VI_names_l0, 254 SPARC64_VI_names_u0, 255 SPARC64_VI_names_l1, 256 SPARC64_VI_names_u1, 257 SPARC64_VI_names_l2, 258 SPARC64_VI_names_u2, 259 SPARC64_VI_names_l3, 260 SPARC64_VI_names_u3 261 }; 262 263 opl_pcbe_config_t nullpic[CPC_SPARC64_VI_NPIC] = { 264 {0, 0x3f, 0, 0}, 265 {1, 0x3f, 0, 0}, 266 {2, 0x3f, 0, 0}, 267 {3, 0x3f, 0, 0}, 268 {4, 0x3f, 0, 0}, 269 {5, 0x3f, 0, 0}, 270 {6, 0x3f, 0, 0}, 271 {7, 0x3f, 0, 0} 272 }; 273 274 static const struct nametable **events; 275 static const char *opl_impl_name; 276 static const char *opl_cpuref; 277 static char *pic_events[CPC_SPARC64_VI_NPIC]; 278 279 static const char *sp_6_ref = "See the \"SPARC64 VI extensions\" " 280 "for descriptions of these events."; 281 282 static int 283 opl_pcbe_init(void) 284 { 285 const struct nametable *n; 286 int i; 287 size_t size; 288 289 /* 290 * Discover type of CPU 291 * 292 * Point nametable to that CPU's table 293 */ 294 switch (ULTRA_VER_IMPL(ultra_getver())) { 295 case OLYMPUS_C_IMPL: 296 events = SPARC64_VI_names; 297 opl_impl_name = "SPARC64 VI"; 298 opl_cpuref = sp_6_ref; 299 break; 300 default: 301 return (-1); 302 } 303 304 /* 305 * Initialize the list of events for each PIC. 306 * Do two passes: one to compute the size necessary and another 307 * to copy the strings. Need room for event, comma, and NULL terminator. 308 */ 309 for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) { 310 size = 0; 311 for (n = events[i]; n->bits != NT_END; n++) 312 size += strlen(n->name) + 1; 313 pic_events[i] = kmem_alloc(size + 1, KM_SLEEP); 314 *pic_events[i] = '\0'; 315 for (n = events[i]; n->bits != NT_END; n++) { 316 (void) strcat(pic_events[i], n->name); 317 (void) strcat(pic_events[i], ","); 318 } 319 /* 320 * Remove trailing comma. 321 */ 322 pic_events[i][size - 1] = '\0'; 323 } 324 325 return (0); 326 } 327 328 static uint_t 329 opl_pcbe_ncounters(void) 330 { 331 return (CPC_SPARC64_VI_NPIC); 332 } 333 334 static const char * 335 opl_pcbe_impl_name(void) 336 { 337 return (opl_impl_name); 338 } 339 340 static const char * 341 opl_pcbe_cpuref(void) 342 { 343 return (opl_cpuref); 344 } 345 346 static char * 347 opl_pcbe_list_events(uint_t picnum) 348 { 349 ASSERT(picnum >= 0 && picnum < cpc_ncounters); 350 351 return (pic_events[picnum]); 352 } 353 354 static char * 355 opl_pcbe_list_attrs(void) 356 { 357 return (""); 358 } 359 360 static const struct nametable * 361 find_event(int regno, char *name) 362 { 363 const struct nametable *n; 364 365 n = events[regno]; 366 367 for (; n->bits != NT_END; n++) 368 if (strcmp(name, n->name) == 0) 369 return (n); 370 371 return (NULL); 372 } 373 374 static uint64_t 375 opl_pcbe_event_coverage(char *event) 376 { 377 uint64_t bitmap = 0; 378 379 int i; 380 for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) { 381 if (find_event(i, event) != NULL) 382 bitmap |= (1 << i); 383 } 384 385 return (bitmap); 386 } 387 388 /* 389 * Check if counter overflow and clear it. 390 */ 391 static uint64_t 392 opl_pcbe_overflow_bitmap(void) 393 { 394 uint64_t pcr, overflow; 395 396 pcr = ultra_getpcr(); 397 DTRACE_PROBE1(sparc64__getpcr, uint64_t, pcr); 398 399 overflow = (pcr & SPARC64_VI_PCR_OVF) >> 400 CPC_SPARC64_VI_PCR_OVF_SHIFT; 401 402 if (overflow) 403 ultra_setpcr(pcr & ~SPARC64_VI_PCR_OVF); 404 405 return (overflow); 406 } 407 408 /*ARGSUSED*/ 409 static int 410 opl_pcbe_configure(uint_t picnum, char *event, uint64_t preset, uint32_t flags, 411 uint_t nattrs, kcpc_attr_t *attrs, void **data, void *token) 412 { 413 opl_pcbe_config_t *conf; 414 const struct nametable *n; 415 opl_pcbe_config_t *other_config; 416 417 /* 418 * If we've been handed an existing configuration, we need only preset 419 * the counter value. 420 */ 421 if (*data != NULL) { 422 conf = *data; 423 conf->opl_pic = (uint32_t)preset; 424 return (0); 425 } 426 427 if (picnum < 0 || picnum >= CPC_SPARC64_VI_NPIC) 428 return (CPC_INVALID_PICNUM); 429 430 if (nattrs != 0) 431 return (CPC_INVALID_ATTRIBUTE); 432 433 /* 434 * Find other requests that will be programmed with this one, and ensure 435 * the flags don't conflict. 436 */ 437 if (((other_config = kcpc_next_config(token, NULL, NULL)) != NULL) && 438 (other_config->opl_flags != flags)) 439 return (CPC_CONFLICTING_REQS); 440 441 if ((n = find_event(picnum, event)) == NULL) 442 return (CPC_INVALID_EVENT); 443 444 conf = kmem_alloc(sizeof (opl_pcbe_config_t), KM_SLEEP); 445 446 conf->opl_picno = picnum; 447 conf->opl_bits = (uint32_t)n->bits; 448 conf->opl_flags = flags; 449 conf->opl_pic = (uint32_t)preset; 450 451 *data = conf; 452 return (0); 453 } 454 455 static void 456 opl_pcbe_program(void *token) 457 { 458 opl_pcbe_config_t *pic[CPC_SPARC64_VI_NPIC]; 459 opl_pcbe_config_t *firstconfig; 460 opl_pcbe_config_t *tmp; 461 uint64_t pcr; 462 uint64_t curpic; 463 uint8_t bitmap = 0; /* for used pic config */ 464 int i; 465 opl_pcbe_config_t dummypic[CPC_SPARC64_VI_NPIC]; 466 467 /* Get next pic config */ 468 firstconfig = tmp = kcpc_next_config(token, NULL, NULL); 469 470 while (tmp != NULL) { 471 ASSERT(tmp->opl_picno < CPC_SPARC64_VI_NPIC); 472 ASSERT(firstconfig->opl_flags == tmp->opl_flags); 473 pic[tmp->opl_picno] = tmp; 474 bitmap |= (uint8_t)(1 << tmp->opl_picno); 475 tmp = kcpc_next_config(token, tmp, NULL); 476 } 477 if (bitmap == 0) 478 panic("opl_pcbe: token %p has no configs", token); 479 480 /* Fill in unused pic config */ 481 for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) { 482 if (bitmap & (1 << i)) 483 continue; 484 485 dummypic[i] = nullpic[i]; 486 dummypic[i].opl_flags = firstconfig->opl_flags; 487 pic[i] = &dummypic[i]; 488 } 489 490 /* 491 * For each counter pair, initialize event settings and 492 * counter values. 493 */ 494 ultra_setpcr(allstopped); 495 pcr = allstopped; 496 pcr &= ~SPARC64_VI_PCR_ULRO; 497 for (i = 0; i < SPARC64_VI_NUM_PIC_PAIRS; i++) { 498 SPARC64_VI_PCR_SEL_PIC(pcr, i); 499 SPARC64_VI_PCR_SEL_EVENT(pcr, pic[i*2]->opl_bits, 500 pic[i*2 + 1]->opl_bits); 501 502 ultra_setpcr(pcr); 503 curpic = (uint64_t)(pic[i*2]->opl_pic | 504 ((uint64_t)pic[i*2 + 1]->opl_pic << 32)); 505 ultra_setpic(curpic); 506 } 507 508 /* 509 * For each counter pair, enable the trace flags to start 510 * counting. Re-read the counters to sample the counter value now 511 * and use that as the baseline for future samples. 512 */ 513 514 /* Get PCR */ 515 pcr = ultra_getpcr(); 516 pcr |= (SPARC64_VI_PCR_ULRO | SPARC64_VI_PCR_OVRO); 517 518 if (pic[0]->opl_flags & CPC_COUNT_USER) 519 pcr |= SPARC64_VI_PCR_USR; 520 if (pic[0]->opl_flags & CPC_COUNT_SYSTEM) 521 pcr |= SPARC64_VI_PCR_SYS; 522 523 /* Set counter values */ 524 525 for (i = 0; i < SPARC64_VI_NUM_PIC_PAIRS; i++) { 526 SPARC64_VI_PCR_SEL_PIC(pcr, i); 527 SPARC64_VI_PCR_SEL_EVENT(pcr, pic[i*2]->opl_bits, 528 pic[i*2 + 1]->opl_bits); 529 530 ultra_setpcr(pcr); 531 DTRACE_PROBE1(sparc64__setpcr, uint64_t, pcr); 532 533 curpic = ultra_getpic(); 534 DTRACE_PROBE1(sparc64__newpic, uint64_t, curpic); 535 pic[i*2]->opl_pic = (uint32_t)(curpic & PIC_MASK); 536 pic[i*2 + 1]->opl_pic = (uint32_t)(curpic >> 32); 537 } 538 539 } 540 541 static void 542 opl_pcbe_allstop(void) 543 { 544 ultra_setpcr(allstopped); 545 } 546 547 548 static void 549 opl_pcbe_sample(void *token) 550 { 551 uint64_t curpic; 552 uint64_t pcr; 553 int64_t diff; 554 uint64_t *pic_data[CPC_SPARC64_VI_NPIC]; 555 uint64_t *dtmp; 556 opl_pcbe_config_t *pic[CPC_SPARC64_VI_NPIC]; 557 opl_pcbe_config_t *ctmp; 558 opl_pcbe_config_t *firstconfig; 559 uint8_t bitmap = 0; /* for used pic config */ 560 int i; 561 opl_pcbe_config_t dummypic[CPC_SPARC64_VI_NPIC]; 562 uint64_t dummypic_data[CPC_SPARC64_VI_NPIC]; 563 564 /* Get next pic config */ 565 firstconfig = ctmp = kcpc_next_config(token, NULL, &dtmp); 566 567 while (ctmp != NULL) { 568 ASSERT(ctmp->opl_picno < CPC_SPARC64_VI_NPIC); 569 ASSERT(firstconfig->opl_flags == ctmp->opl_flags); 570 pic[ctmp->opl_picno] = ctmp; 571 pic_data[ctmp->opl_picno] = dtmp; 572 bitmap |= (uint8_t)(1 << ctmp->opl_picno); 573 ctmp = kcpc_next_config(token, ctmp, &dtmp); 574 } 575 if (bitmap == 0) 576 panic("opl_pcbe: token %p has no configs", token); 577 578 /* Fill in unuse pic config */ 579 for (i = 0; i < CPC_SPARC64_VI_NPIC; i++) { 580 if (bitmap & (1 << i)) 581 continue; 582 583 dummypic[i] = nullpic[i]; 584 dummypic[i].opl_flags = firstconfig->opl_flags; 585 pic[i] = &dummypic[i]; 586 587 dummypic_data[i] = 0; 588 pic_data[i] = &dummypic_data[i]; 589 } 590 591 pcr = ultra_getpcr(); 592 pcr |= (SPARC64_VI_PCR_ULRO | SPARC64_VI_PCR_OVRO); 593 594 for (i = 0; i < SPARC64_VI_NUM_PIC_PAIRS; i++) { 595 SPARC64_VI_PCR_SEL_PIC(pcr, i); 596 SPARC64_VI_PCR_SEL_EVENT(pcr, pic[i*2]->opl_bits, 597 pic[i*2 + 1]->opl_bits); 598 599 ultra_setpcr(pcr); 600 601 curpic = ultra_getpic(); 602 DTRACE_PROBE1(sparc64__getpic, unit64_t, curpic); 603 604 diff = (int64_t)((uint32_t)(curpic & PIC_MASK) - 605 pic[i*2]->opl_pic); 606 if (diff < 0) 607 diff += (1ll << 32); 608 *pic_data[i*2] += diff; 609 610 diff = (int64_t)((uint32_t)(curpic >> 32) - 611 pic[i*2 + 1]->opl_pic); 612 if (diff < 0) 613 diff += (1ll << 32); 614 *pic_data[i*2 + 1] += diff; 615 616 pic[i*2]->opl_pic = (uint32_t)(curpic & PIC_MASK); 617 pic[i*2 + 1]->opl_pic = (uint32_t)(curpic >> 32); 618 } 619 620 } 621 622 static void 623 opl_pcbe_free(void *config) 624 { 625 kmem_free(config, sizeof (opl_pcbe_config_t)); 626 } 627 628 629 static struct modlpcbe modlpcbe = { 630 &mod_pcbeops, 631 "SPARC64 VI Performance Counters v1.2", 632 &opl_pcbe_ops 633 }; 634 635 static struct modlinkage modl = { 636 MODREV_1, 637 &modlpcbe, 638 }; 639 640 int 641 _init(void) 642 { 643 if (opl_pcbe_init() != 0) 644 return (ENOTSUP); 645 return (mod_install(&modl)); 646 } 647 648 int 649 _fini(void) 650 { 651 return (mod_remove(&modl)); 652 } 653 654 int 655 _info(struct modinfo *mi) 656 { 657 return (mod_info(&modl, mi)); 658 } 659