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 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * hermon_stats.c 29 * Hermon IB Performance Statistics routines 30 * 31 * Implements all the routines necessary for setting up, querying, and 32 * (later) tearing down all the kstats necessary for implementing to 33 * the interfaces necessary to provide busstat(1M) access. 34 */ 35 36 #include <sys/types.h> 37 #include <sys/conf.h> 38 #include <sys/ddi.h> 39 #include <sys/sunddi.h> 40 #include <sys/modctl.h> 41 42 #include <sys/ib/adapters/hermon/hermon.h> 43 44 static kstat_t *hermon_kstat_picN_create(hermon_state_t *state, int num_pic, 45 int num_evt, hermon_ks_mask_t *ev_array); 46 static kstat_t *hermon_kstat_cntr_create(hermon_state_t *state, int num_pic, 47 int (*update)(kstat_t *, int)); 48 static int hermon_kstat_cntr_update(kstat_t *ksp, int rw); 49 50 void hermon_kstat_perfcntr64_create(hermon_state_t *state, uint_t port_num); 51 static int hermon_kstat_perfcntr64_read(hermon_state_t *state, uint_t port, 52 int reset); 53 static void hermon_kstat_perfcntr64_thread_exit(hermon_ks_info_t *ksi); 54 static int hermon_kstat_perfcntr64_update(kstat_t *ksp, int rw); 55 56 /* 57 * Hermon IB Performance Events structure 58 * This structure is read-only and is used to setup the individual kstats 59 * and to initialize the tki_ib_perfcnt[] array for each Hermon instance. 60 */ 61 hermon_ks_mask_t hermon_ib_perfcnt_list[HERMON_CNTR_NUMENTRIES] = { 62 {"port_xmit_data", 0, 0}, 63 {"port_recv_data", 0, 0}, 64 {"port_xmit_pkts", 0, 0}, 65 {"port_recv_pkts", 0, 0}, 66 {"port_recv_err", 0, 0}, 67 {"port_xmit_discards", 0, 0}, 68 {"vl15_dropped", 0, 0}, 69 {"port_xmit_wait", 0, 0}, 70 {"port_recv_remote_phys_err", 0, 0}, 71 {"port_xmit_constraint_err", 0, 0}, 72 {"port_recv_constraint_err", 0, 0}, 73 {"symbol_err_counter", 0, 0}, 74 {"link_err_recovery_cnt", 0, 0}, 75 {"link_downed_cnt", 0, 0}, 76 {"excessive_buffer_overruns", 0, 0}, 77 {"local_link_integrity_err", 0, 0}, 78 {"clear_pic", 0, 0} 79 }; 80 81 /* 82 * Return the maximum of (x) and (y) 83 */ 84 #define MAX(x, y) (((x) > (y)) ? (x) : (y)) 85 86 /* 87 * Set (x) to the maximum of (x) and (y) 88 */ 89 #define SET_TO_MAX(x, y) \ 90 { \ 91 if ((x) < (y)) \ 92 (x) = (y); \ 93 } 94 95 /* 96 * hermon_kstat_init() 97 * Context: Only called from attach() path context 98 */ 99 int 100 hermon_kstat_init(hermon_state_t *state) 101 { 102 hermon_ks_info_t *ksi; 103 uint_t numports; 104 int i; 105 106 /* Allocate a kstat info structure */ 107 ksi = (hermon_ks_info_t *)kmem_zalloc(sizeof (hermon_ks_info_t), 108 KM_SLEEP); 109 if (ksi == NULL) { 110 return (DDI_FAILURE); 111 } 112 state->hs_ks_info = ksi; 113 114 /* 115 * Create as many "pic" and perfcntr64 kstats as we have IB ports. 116 * Enable all of the events specified in the "hermon_ib_perfcnt_list" 117 * structure. 118 */ 119 numports = state->hs_cfg_profile->cp_num_ports; 120 for (i = 0; i < numports; i++) { 121 ksi->hki_picN_ksp[i] = hermon_kstat_picN_create(state, i, 122 HERMON_CNTR_NUMENTRIES, hermon_ib_perfcnt_list); 123 if (ksi->hki_picN_ksp[i] == NULL) { 124 goto kstat_init_fail; 125 } 126 127 hermon_kstat_perfcntr64_create(state, i + 1); 128 if (ksi->hki_perfcntr64[i].hki64_ksp == NULL) { 129 goto kstat_init_fail; 130 } 131 } 132 133 /* Create the "counters" kstat too */ 134 ksi->hki_cntr_ksp = hermon_kstat_cntr_create(state, numports, 135 hermon_kstat_cntr_update); 136 if (ksi->hki_cntr_ksp == NULL) { 137 goto kstat_init_fail; 138 } 139 140 /* Initialize the control register and initial counter values */ 141 ksi->hki_pcr = 0; 142 ksi->hki_pic0 = 0; 143 ksi->hki_pic1 = 0; 144 145 /* 146 * Initialize the Hermon hki_ib_perfcnt[] array values using the 147 * default values in hermon_ib_perfcnt_list[] 148 */ 149 for (i = 0; i < HERMON_CNTR_NUMENTRIES; i++) { 150 ksi->hki_ib_perfcnt[i] = hermon_ib_perfcnt_list[i]; 151 } 152 153 mutex_init(&ksi->hki_perfcntr64_lock, NULL, MUTEX_DRIVER, NULL); 154 cv_init(&ksi->hki_perfcntr64_cv, NULL, CV_DRIVER, NULL); 155 156 return (DDI_SUCCESS); 157 158 159 kstat_init_fail: 160 161 /* Delete all the previously created kstats */ 162 if (ksi->hki_cntr_ksp != NULL) { 163 kstat_delete(ksi->hki_cntr_ksp); 164 } 165 for (i = 0; i < numports; i++) { 166 if (ksi->hki_picN_ksp[i] != NULL) { 167 kstat_delete(ksi->hki_picN_ksp[i]); 168 } 169 if (ksi->hki_perfcntr64[i].hki64_ksp != NULL) { 170 kstat_delete(ksi->hki_perfcntr64[i].hki64_ksp); 171 } 172 } 173 174 /* Free the kstat info structure */ 175 kmem_free(ksi, sizeof (hermon_ks_info_t)); 176 177 return (DDI_FAILURE); 178 } 179 180 181 /* 182 * hermon_kstat_init() 183 * Context: Only called from attach() and/or detach() path contexts 184 */ 185 void 186 hermon_kstat_fini(hermon_state_t *state) 187 { 188 hermon_ks_info_t *ksi; 189 uint_t numports; 190 int i; 191 192 /* Get pointer to kstat info */ 193 ksi = state->hs_ks_info; 194 195 /* 196 * Signal the perfcntr64_update_thread to exit and wait until the 197 * thread exits. 198 */ 199 mutex_enter(&ksi->hki_perfcntr64_lock); 200 hermon_kstat_perfcntr64_thread_exit(ksi); 201 mutex_exit(&ksi->hki_perfcntr64_lock); 202 203 /* Delete all the "pic" and perfcntr64 kstats (one per port) */ 204 numports = state->hs_cfg_profile->cp_num_ports; 205 for (i = 0; i < numports; i++) { 206 if (ksi->hki_picN_ksp[i] != NULL) { 207 kstat_delete(ksi->hki_picN_ksp[i]); 208 } 209 210 if (ksi->hki_perfcntr64[i].hki64_ksp != NULL) { 211 kstat_delete(ksi->hki_perfcntr64[i].hki64_ksp); 212 } 213 } 214 215 /* Delete the "counter" kstats (one per port) */ 216 kstat_delete(ksi->hki_cntr_ksp); 217 218 cv_destroy(&ksi->hki_perfcntr64_cv); 219 mutex_destroy(&ksi->hki_perfcntr64_lock); 220 221 /* Free the kstat info structure */ 222 kmem_free(ksi, sizeof (hermon_ks_info_t)); 223 } 224 225 226 /* 227 * hermon_kstat_picN_create() 228 * Context: Only called from attach() path context 229 */ 230 static kstat_t * 231 hermon_kstat_picN_create(hermon_state_t *state, int num_pic, int num_evt, 232 hermon_ks_mask_t *ev_array) 233 { 234 kstat_t *picN_ksp; 235 struct kstat_named *pic_named_data; 236 int drv_instance, i; 237 char *drv_name; 238 char pic_name[16]; 239 240 /* 241 * Create the "picN" kstat. In the steps, below we will attach 242 * all of our named event types to it. 243 */ 244 drv_name = (char *)ddi_driver_name(state->hs_dip); 245 drv_instance = ddi_get_instance(state->hs_dip); 246 (void) sprintf(pic_name, "pic%d", num_pic); 247 picN_ksp = kstat_create(drv_name, drv_instance, pic_name, "bus", 248 KSTAT_TYPE_NAMED, num_evt, NULL); 249 if (picN_ksp == NULL) { 250 return (NULL); 251 } 252 pic_named_data = (struct kstat_named *)(picN_ksp->ks_data); 253 254 /* 255 * Write event names and their associated pcr masks. The last entry 256 * in the array (clear_pic) is added separately below (as its pic 257 * value must be inverted). 258 */ 259 for (i = 0; i < num_evt - 1; i++) { 260 pic_named_data[i].value.ui64 = 261 ((uint64_t)i << (num_pic * HERMON_CNTR_SIZE)); 262 kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name, 263 KSTAT_DATA_UINT64); 264 } 265 266 /* Add the "clear_pic" entry */ 267 pic_named_data[i].value.ui64 = 268 ~((uint64_t)HERMON_CNTR_MASK << (num_pic * HERMON_CNTR_SIZE)); 269 kstat_named_init(&pic_named_data[i], ev_array[i].ks_evt_name, 270 KSTAT_DATA_UINT64); 271 272 /* Install the kstat */ 273 kstat_install(picN_ksp); 274 275 return (picN_ksp); 276 } 277 278 279 /* 280 * hermon_kstat_cntr_create() 281 * Context: Only called from attach() path context 282 */ 283 static kstat_t * 284 hermon_kstat_cntr_create(hermon_state_t *state, int num_pic, 285 int (*update)(kstat_t *, int)) 286 { 287 struct kstat *cntr_ksp; 288 struct kstat_named *cntr_named_data; 289 int drv_instance, i; 290 char *drv_name; 291 char pic_str[16]; 292 293 /* 294 * Create the "counters" kstat. In the steps, below we will attach 295 * all of our "pic" to it. Note: The size of this kstat is 296 * num_pic + 1 because it also contains the "%pcr". 297 */ 298 drv_name = (char *)ddi_driver_name(state->hs_dip); 299 drv_instance = ddi_get_instance(state->hs_dip); 300 cntr_ksp = kstat_create(drv_name, drv_instance, "counters", "bus", 301 KSTAT_TYPE_NAMED, num_pic + 1, KSTAT_FLAG_WRITABLE); 302 if (cntr_ksp == NULL) { 303 return (NULL); 304 } 305 cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data); 306 307 /* 308 * Initialize the named kstats (for the "pcr" and for the 309 * individual "pic" kstats) 310 */ 311 kstat_named_init(&cntr_named_data[0], "pcr", KSTAT_DATA_UINT64); 312 for (i = 0; i < num_pic; i++) { 313 (void) sprintf(pic_str, "pic%d", i); 314 kstat_named_init(&cntr_named_data[i+1], pic_str, 315 KSTAT_DATA_UINT64); 316 } 317 318 /* 319 * Store the Hermon softstate pointer in the kstat's private field so 320 * that it is available to the update function. 321 */ 322 cntr_ksp->ks_private = (void *)state; 323 cntr_ksp->ks_update = update; 324 325 /* Install the kstat */ 326 kstat_install(cntr_ksp); 327 328 return (cntr_ksp); 329 } 330 331 332 /* 333 * hermon_kstat_cntr_update() 334 * Context: Called from the kstat context 335 */ 336 static int 337 hermon_kstat_cntr_update(kstat_t *ksp, int rw) 338 { 339 hermon_state_t *state; 340 hermon_ks_mask_t *ib_perf; 341 hermon_ks_info_t *ksi; 342 struct kstat_named *data; 343 uint64_t pcr; 344 uint32_t tmp; 345 uint32_t oldval; 346 uint_t numports, indx; 347 int status; 348 hermon_hw_sm_perfcntr_t sm_perfcntr; 349 350 /* 351 * Extract the Hermon softstate pointer, kstat data, pointer to the 352 * kstat info structure, and pointer to the hki_ib_perfcnt[] array 353 * from the input parameters. Note: For warlock purposes, these 354 * parameters are all accessed only in this routine and are, 355 * therefore, protected by the lock used by the kstat framework. 356 */ 357 state = ksp->ks_private; 358 data = (struct kstat_named *)(ksp->ks_data); 359 ksi = state->hs_ks_info; 360 ib_perf = &ksi->hki_ib_perfcnt[0]; 361 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ksi)) 362 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*data)) 363 _NOTE(NOW_INVISIBLE_TO_OTHER_THREADS(*ib_perf)) 364 365 /* 366 * Depending on whether we are reading the "pic" counters or 367 * writing the "pcr" control register, we need to handle and 368 * fill in the kstat data appropriately. 369 * 370 * If this is a write to the "pcr", then extract the value from 371 * the kstat data and store it in the kstat info structure. 372 * 373 * Otherwise, if this is a read of the "pic" counter(s), then 374 * extract the register offset, size, and mask values from the 375 * ib_perf[] array. Then read the corresponding register and store 376 * it into the kstat data. Note: We only read/fill in pic1 if more 377 * than one port is configured. 378 */ 379 numports = state->hs_cfg_profile->cp_num_ports; 380 if (rw == KSTAT_WRITE) { 381 /* Update the stored "pcr" value */ 382 ksi->hki_pcr = data[0].value.ui64; 383 return (0); 384 } else { 385 /* 386 * Get the current "pcr" value and extract the lower 387 * portion (corresponding to the counters for "pic0") 388 */ 389 pcr = ksi->hki_pcr; 390 indx = pcr & HERMON_CNTR_MASK; 391 data[0].value.ui64 = pcr; 392 393 /* 394 * Fill in the "pic0" counter, corresponding to port 1. 395 * This involves reading in the current value in the register 396 * and calculating how many events have happened since this 397 * register was last polled. Then we save away the current 398 * value for the counter and increment the "pic0" total by 399 * the number of new events. 400 */ 401 oldval = ib_perf[indx].ks_old_pic0; 402 403 status = hermon_getperfcntr_cmd_post(state, 1, 404 HERMON_CMD_NOSLEEP_SPIN, &sm_perfcntr, 0); 405 if (status != HERMON_CMD_SUCCESS) { 406 return (-1); 407 } 408 switch (indx) { 409 case 0: /* port_xmit_data */ 410 tmp = sm_perfcntr.portxmdata; 411 break; 412 case 1: /* port_recv_data */ 413 tmp = sm_perfcntr.portrcdata; 414 break; 415 case 2: /* port_xmit_pkts */ 416 tmp = sm_perfcntr.portxmpkts; 417 break; 418 case 3: /* port_recv_pkts */ 419 tmp = sm_perfcntr.portrcpkts; 420 break; 421 case 4: /* port_recv_err */ 422 tmp = sm_perfcntr.portrcv; 423 break; 424 case 5: /* port_xmit_discards */ 425 tmp = sm_perfcntr.portxmdiscard; 426 break; 427 case 6: /* vl15_dropped */ 428 tmp = sm_perfcntr.vl15drop; 429 break; 430 case 7: /* port_xmit_wait */ 431 tmp = sm_perfcntr.portxmwait; 432 break; 433 case 8: /* port_recv_remote_phys_err */ 434 tmp = sm_perfcntr.portrcvrem; 435 break; 436 case 9: /* port_xmit_constraint_err */ 437 tmp = sm_perfcntr.portxmconstr; 438 break; 439 case 10: /* port_recv_constraint_err */ 440 tmp = sm_perfcntr.portrcconstr; 441 break; 442 case 11: /* symbol_err_counter */ 443 tmp = sm_perfcntr.symerr; 444 break; 445 case 12: /* link_err_recovery_cnt */ 446 tmp = sm_perfcntr.linkerrrec; 447 break; 448 case 13: /* link_downed_cnt */ 449 tmp = sm_perfcntr.linkdown; 450 break; 451 case 14: /* excessive_buffer_overruns */ 452 tmp = sm_perfcntr.xsbuffovrun; 453 break; 454 case 15: /* local_link_integrity_err */ 455 tmp = sm_perfcntr.locallinkint; 456 break; 457 case 16: /* clear_pic */ 458 tmp = 0; /* XXX */ 459 break; 460 default: 461 cmn_err(CE_CONT, "perf counter out of range\n"); 462 } 463 464 ib_perf[indx].ks_old_pic0 = tmp; 465 466 tmp = tmp - oldval; 467 ksi->hki_pic0 += tmp; 468 data[1].value.ui64 = ksi->hki_pic0; 469 470 /* 471 * If necessary, fill in the "pic1" counter for port 2. 472 * This works the same as above except that we extract the 473 * upper bits (corresponding to the counters for "pic1") 474 */ 475 if (numports == HERMON_MAX_PORTS) { 476 indx = pcr >> HERMON_CNTR_SIZE; 477 oldval = ib_perf[indx].ks_old_pic1; 478 479 status = hermon_getperfcntr_cmd_post(state, 2, 480 HERMON_CMD_NOSLEEP_SPIN, &sm_perfcntr, 0); 481 if (status != HERMON_CMD_SUCCESS) { 482 return (-1); 483 } 484 switch (indx) { 485 case 0: /* port_xmit_data */ 486 tmp = sm_perfcntr.portxmdata; 487 break; 488 case 1: /* port_recv_data */ 489 tmp = sm_perfcntr.portrcdata; 490 break; 491 case 2: /* port_xmit_pkts */ 492 tmp = sm_perfcntr.portxmpkts; 493 break; 494 case 3: /* port_recv_pkts */ 495 tmp = sm_perfcntr.portrcpkts; 496 break; 497 case 4: /* port_recv_err */ 498 tmp = sm_perfcntr.portrcv; 499 break; 500 case 5: /* port_xmit_discards */ 501 tmp = sm_perfcntr.portxmdiscard; 502 break; 503 case 6: /* vl15_dropped */ 504 tmp = sm_perfcntr.vl15drop; 505 break; 506 case 7: /* port_xmit_wait */ 507 tmp = sm_perfcntr.portxmwait; 508 break; 509 case 8: /* port_recv_remote_phys_err */ 510 tmp = sm_perfcntr.portrcvrem; 511 break; 512 case 9: /* port_xmit_constraint_err */ 513 tmp = sm_perfcntr.portxmconstr; 514 break; 515 case 10: /* port_recv_constraint_err */ 516 tmp = sm_perfcntr.portrcconstr; 517 break; 518 case 11: /* symbol_err_counter */ 519 tmp = sm_perfcntr.symerr; 520 break; 521 case 12: /* link_err_recovery_cnt */ 522 tmp = sm_perfcntr.linkerrrec; 523 break; 524 case 13: /* link_downed_cnt */ 525 tmp = sm_perfcntr.linkdown; 526 break; 527 case 14: /* excessive_buffer_overruns */ 528 tmp = sm_perfcntr.xsbuffovrun; 529 break; 530 case 15: /* local_link_integrity_err */ 531 tmp = sm_perfcntr.locallinkint; 532 break; 533 case 16: /* clear_pic */ 534 tmp = 0; /* XXX */ 535 break; 536 default: 537 cmn_err(CE_CONT, "perf counter out of range\n"); 538 } 539 540 ib_perf[indx].ks_old_pic1 = tmp; 541 542 tmp = tmp - oldval; 543 ksi->hki_pic1 += tmp; 544 data[2].value.ui64 = ksi->hki_pic1; 545 } 546 547 return (0); 548 } 549 } 550 551 /* 552 * 64 bit kstats for performance counters: 553 * 554 * Since the hardware as of now does not support 64 bit performance counters, 555 * we maintain 64 bit performance counters in software using the 32 bit 556 * hardware counters. 557 * 558 * We create a thread that, every one second, reads the values of 32 bit 559 * hardware counters and adds them to the 64 bit software counters. Immediately 560 * after reading, it resets the 32 bit hardware counters to zero (so that they 561 * start counting from zero again). At any time the current value of a counter 562 * is going to be the sum of the 64 bit software counter and the 32 bit 563 * hardware counter. 564 * 565 * Since this work need not be done if there is no consumer, by default 566 * we do not maintain 64 bit software counters. To enable this the consumer 567 * needs to write a non-zero value to the "enable" component of the of 568 * perf_counters kstat. Writing zero to this component will disable this work. 569 * 570 * If performance monitor is enabled in subnet manager, the SM could 571 * periodically reset the hardware counters by sending perf-MADs. So only 572 * one of either our software 64 bit counters or the SM performance monitor 573 * could be enabled at the same time. However, if both of them are enabled at 574 * the same time we still do our best by keeping track of the values of the 575 * last read 32 bit hardware counters. If the current read of a 32 bit hardware 576 * counter is less than the last read of the counter, we ignore the current 577 * value and go with the last read value. 578 */ 579 580 /* 581 * hermon_kstat_perfcntr64_create() 582 * Context: Only called from attach() path context 583 * 584 * Create "port#/perf_counters" kstat for the specified port number. 585 */ 586 void 587 hermon_kstat_perfcntr64_create(hermon_state_t *state, uint_t port_num) 588 { 589 hermon_ks_info_t *ksi = state->hs_ks_info; 590 struct kstat *cntr_ksp; 591 struct kstat_named *cntr_named_data; 592 int drv_instance; 593 char *drv_name; 594 char kname[32]; 595 596 ASSERT(port_num != 0); 597 598 drv_name = (char *)ddi_driver_name(state->hs_dip); 599 drv_instance = ddi_get_instance(state->hs_dip); 600 (void) snprintf(kname, sizeof (kname), "port%u/perf_counters", 601 port_num); 602 cntr_ksp = kstat_create(drv_name, drv_instance, kname, "ib", 603 KSTAT_TYPE_NAMED, HERMON_PERFCNTR64_NUM_COUNTERS, 604 KSTAT_FLAG_WRITABLE); 605 if (cntr_ksp == NULL) { 606 return; 607 } 608 cntr_named_data = (struct kstat_named *)(cntr_ksp->ks_data); 609 610 kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_ENABLE_IDX], 611 "enable", KSTAT_DATA_UINT32); 612 kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_XMIT_DATA_IDX], 613 "xmit_data", KSTAT_DATA_UINT64); 614 kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_RECV_DATA_IDX], 615 "recv_data", KSTAT_DATA_UINT64); 616 kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_XMIT_PKTS_IDX], 617 "xmit_pkts", KSTAT_DATA_UINT64); 618 kstat_named_init(&cntr_named_data[HERMON_PERFCNTR64_RECV_PKTS_IDX], 619 "recv_pkts", KSTAT_DATA_UINT64); 620 621 ksi->hki_perfcntr64[port_num - 1].hki64_ksp = cntr_ksp; 622 ksi->hki_perfcntr64[port_num - 1].hki64_port_num = port_num; 623 ksi->hki_perfcntr64[port_num - 1].hki64_state = state; 624 625 cntr_ksp->ks_private = &ksi->hki_perfcntr64[port_num - 1]; 626 cntr_ksp->ks_update = hermon_kstat_perfcntr64_update; 627 628 /* Install the kstat */ 629 kstat_install(cntr_ksp); 630 } 631 632 /* 633 * hermon_kstat_perfcntr64_read() 634 * 635 * Read the values of 32 bit hardware counters. 636 * 637 * If reset is true, reset the 32 bit hardware counters. Add the values of the 638 * 32 bit hardware counters to the 64 bit software counters. 639 * 640 * If reset is false, just save the values read from the 32 bit hardware 641 * counters in hki64_last_read[]. 642 * 643 * See the general comment on the 64 bit performance counters 644 * regarding the use of last read 32 bit hardware counter values. 645 */ 646 static int 647 hermon_kstat_perfcntr64_read(hermon_state_t *state, uint_t port, int reset) 648 { 649 hermon_ks_info_t *ksi = state->hs_ks_info; 650 hermon_perfcntr64_ks_info_t *ksi64 = &ksi->hki_perfcntr64[port - 1]; 651 int status, i; 652 uint32_t tmp; 653 hermon_hw_sm_perfcntr_t sm_perfcntr; 654 655 ASSERT(MUTEX_HELD(&ksi->hki_perfcntr64_lock)); 656 ASSERT(port != 0); 657 658 /* read the 32 bit hardware counters */ 659 status = hermon_getperfcntr_cmd_post(state, port, 660 HERMON_CMD_NOSLEEP_SPIN, &sm_perfcntr, 0); 661 if (status != HERMON_CMD_SUCCESS) { 662 return (status); 663 } 664 665 if (reset) { 666 /* reset the hardware counters */ 667 status = hermon_getperfcntr_cmd_post(state, port, 668 HERMON_CMD_NOSLEEP_SPIN, NULL, 1); 669 if (status != HERMON_CMD_SUCCESS) { 670 return (status); 671 } 672 673 /* 674 * Update 64 bit software counters 675 */ 676 tmp = MAX(sm_perfcntr.portxmdata, 677 ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_DATA_IDX]); 678 ksi64->hki64_counters[HERMON_PERFCNTR64_XMIT_DATA_IDX] += tmp; 679 680 tmp = MAX(sm_perfcntr.portrcdata, 681 ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_DATA_IDX]); 682 ksi64->hki64_counters[HERMON_PERFCNTR64_RECV_DATA_IDX] += tmp; 683 684 tmp = MAX(sm_perfcntr.portxmpkts, 685 ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_PKTS_IDX]); 686 ksi64->hki64_counters[HERMON_PERFCNTR64_XMIT_PKTS_IDX] += tmp; 687 688 tmp = MAX(sm_perfcntr.portrcpkts, 689 ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_PKTS_IDX]); 690 ksi64->hki64_counters[HERMON_PERFCNTR64_RECV_PKTS_IDX] += tmp; 691 692 for (i = 0; i < HERMON_PERFCNTR64_NUM_COUNTERS; i++) 693 ksi64->hki64_last_read[i] = 0; 694 695 } else { 696 /* 697 * Update ksi64->hki64_last_read[] 698 */ 699 SET_TO_MAX( 700 ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_DATA_IDX], 701 sm_perfcntr.portxmdata); 702 703 SET_TO_MAX( 704 ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_DATA_IDX], 705 sm_perfcntr.portrcdata); 706 707 SET_TO_MAX( 708 ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_PKTS_IDX], 709 sm_perfcntr.portxmpkts); 710 711 SET_TO_MAX( 712 ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_PKTS_IDX], 713 sm_perfcntr.portrcpkts); 714 } 715 716 return (HERMON_CMD_SUCCESS); 717 } 718 719 /* 720 * hermon_kstat_perfcntr64_update_thread() 721 * Context: Entry point for a kernel thread 722 * 723 * Maintain 64 bit performance counters in software using the 32 bit 724 * hardware counters. 725 */ 726 static void 727 hermon_kstat_perfcntr64_update_thread(void *arg) 728 { 729 hermon_state_t *state = (hermon_state_t *)arg; 730 hermon_ks_info_t *ksi = state->hs_ks_info; 731 uint_t i; 732 clock_t delta = drv_usectohz(1000000); 733 734 mutex_enter(&ksi->hki_perfcntr64_lock); 735 /* 736 * Every one second update the values 64 bit software counters 737 * for all ports. Exit if HERMON_PERFCNTR64_THREAD_EXIT flag is set. 738 */ 739 while (!(ksi->hki_perfcntr64_flags & HERMON_PERFCNTR64_THREAD_EXIT)) { 740 for (i = 0; i < state->hs_cfg_profile->cp_num_ports; i++) { 741 if (ksi->hki_perfcntr64[i].hki64_enabled) { 742 (void) hermon_kstat_perfcntr64_read(state, 743 i + 1, 1); 744 } 745 } 746 /* sleep for a second */ 747 (void) cv_reltimedwait(&ksi->hki_perfcntr64_cv, 748 &ksi->hki_perfcntr64_lock, delta, TR_CLOCK_TICK); 749 } 750 ksi->hki_perfcntr64_flags = 0; 751 mutex_exit(&ksi->hki_perfcntr64_lock); 752 } 753 754 /* 755 * hermon_kstat_perfcntr64_thread_create() 756 * Context: Called from the kstat context 757 * 758 * Create a thread that maintains 64 bit performance counters in software. 759 */ 760 static void 761 hermon_kstat_perfcntr64_thread_create(hermon_state_t *state) 762 { 763 hermon_ks_info_t *ksi = state->hs_ks_info; 764 kthread_t *thr; 765 766 ASSERT(MUTEX_HELD(&ksi->hki_perfcntr64_lock)); 767 768 /* 769 * One thread per hermon instance. Don't create a thread if already 770 * created. 771 */ 772 if (!(ksi->hki_perfcntr64_flags & HERMON_PERFCNTR64_THREAD_CREATED)) { 773 thr = thread_create(NULL, 0, 774 hermon_kstat_perfcntr64_update_thread, 775 state, 0, &p0, TS_RUN, minclsyspri); 776 ksi->hki_perfcntr64_thread_id = thr->t_did; 777 ksi->hki_perfcntr64_flags |= HERMON_PERFCNTR64_THREAD_CREATED; 778 } 779 } 780 781 /* 782 * hermon_kstat_perfcntr64_thread_exit() 783 * Context: Called from attach, detach or kstat context 784 */ 785 static void 786 hermon_kstat_perfcntr64_thread_exit(hermon_ks_info_t *ksi) 787 { 788 kt_did_t tid; 789 790 ASSERT(MUTEX_HELD(&ksi->hki_perfcntr64_lock)); 791 792 if (ksi->hki_perfcntr64_flags & HERMON_PERFCNTR64_THREAD_CREATED) { 793 /* 794 * Signal the thread to exit and wait until the thread exits. 795 */ 796 ksi->hki_perfcntr64_flags |= HERMON_PERFCNTR64_THREAD_EXIT; 797 tid = ksi->hki_perfcntr64_thread_id; 798 cv_signal(&ksi->hki_perfcntr64_cv); 799 800 mutex_exit(&ksi->hki_perfcntr64_lock); 801 thread_join(tid); 802 mutex_enter(&ksi->hki_perfcntr64_lock); 803 } 804 } 805 806 /* 807 * hermon_kstat_perfcntr64_update() 808 * Context: Called from the kstat context 809 * 810 * See the general comment on 64 bit kstats for performance counters: 811 */ 812 static int 813 hermon_kstat_perfcntr64_update(kstat_t *ksp, int rw) 814 { 815 hermon_state_t *state; 816 struct kstat_named *data; 817 hermon_ks_info_t *ksi; 818 hermon_perfcntr64_ks_info_t *ksi64; 819 int i, thr_exit; 820 821 ksi64 = ksp->ks_private; 822 state = ksi64->hki64_state; 823 ksi = state->hs_ks_info; 824 data = (struct kstat_named *)(ksp->ks_data); 825 826 mutex_enter(&ksi->hki_perfcntr64_lock); 827 828 /* 829 * 64 bit performance counters maintained by the software is not 830 * enabled by default. Enable them upon a writing a non-zero value 831 * to "enable" kstat. Disable them upon a writing zero to the 832 * "enable" kstat. 833 */ 834 if (rw == KSTAT_WRITE) { 835 if (data[HERMON_PERFCNTR64_ENABLE_IDX].value.ui32) { 836 if (ksi64->hki64_enabled == 0) { 837 /* 838 * Reset the hardware counters to ensure that 839 * the hardware counter doesn't max out 840 * (and hence stop counting) before we get 841 * a chance to reset the counter in 842 * hermon_kstat_perfcntr64_update_thread. 843 */ 844 if (hermon_getperfcntr_cmd_post(state, 845 ksi64->hki64_port_num, 846 HERMON_CMD_NOSLEEP_SPIN, NULL, 1) != 847 HERMON_CMD_SUCCESS) { 848 mutex_exit(&ksi->hki_perfcntr64_lock); 849 return (EIO); 850 } 851 852 /* Enable 64 bit software counters */ 853 ksi64->hki64_enabled = 1; 854 for (i = 0; 855 i < HERMON_PERFCNTR64_NUM_COUNTERS; i++) { 856 ksi64->hki64_counters[i] = 0; 857 ksi64->hki64_last_read[i] = 0; 858 } 859 hermon_kstat_perfcntr64_thread_create(state); 860 } 861 862 } else if (ksi64->hki64_enabled) { 863 /* Disable 64 bit software counters */ 864 ksi64->hki64_enabled = 0; 865 thr_exit = 1; 866 for (i = 0; i < state->hs_cfg_profile->cp_num_ports; 867 i++) { 868 if (ksi->hki_perfcntr64[i].hki64_enabled) { 869 thr_exit = 0; 870 break; 871 } 872 } 873 if (thr_exit) 874 hermon_kstat_perfcntr64_thread_exit(ksi); 875 } 876 } else if (ksi64->hki64_enabled) { 877 /* 878 * Read the counters and update kstats. 879 */ 880 if (hermon_kstat_perfcntr64_read(state, ksi64->hki64_port_num, 881 0) != HERMON_CMD_SUCCESS) { 882 mutex_exit(&ksi->hki_perfcntr64_lock); 883 return (EIO); 884 } 885 886 data[HERMON_PERFCNTR64_ENABLE_IDX].value.ui32 = 1; 887 888 data[HERMON_PERFCNTR64_XMIT_DATA_IDX].value.ui64 = 889 ksi64->hki64_counters[HERMON_PERFCNTR64_XMIT_DATA_IDX] + 890 ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_DATA_IDX]; 891 892 data[HERMON_PERFCNTR64_RECV_DATA_IDX].value.ui64 = 893 ksi64->hki64_counters[HERMON_PERFCNTR64_RECV_DATA_IDX] + 894 ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_DATA_IDX]; 895 896 data[HERMON_PERFCNTR64_XMIT_PKTS_IDX].value.ui64 = 897 ksi64->hki64_counters[HERMON_PERFCNTR64_XMIT_PKTS_IDX] + 898 ksi64->hki64_last_read[HERMON_PERFCNTR64_XMIT_PKTS_IDX]; 899 900 data[HERMON_PERFCNTR64_RECV_PKTS_IDX].value.ui64 = 901 ksi64->hki64_counters[HERMON_PERFCNTR64_RECV_PKTS_IDX] + 902 ksi64->hki64_last_read[HERMON_PERFCNTR64_RECV_PKTS_IDX]; 903 904 } else { 905 /* return 0 in kstats if not enabled */ 906 data[HERMON_PERFCNTR64_ENABLE_IDX].value.ui32 = 0; 907 for (i = 1; i < HERMON_PERFCNTR64_NUM_COUNTERS; i++) 908 data[i].value.ui64 = 0; 909 } 910 911 mutex_exit(&ksi->hki_perfcntr64_lock); 912 return (0); 913 } 914