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 2010 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <string.h> 29 #include <unistd.h> 30 #include <errno.h> 31 #include <inttypes.h> 32 33 #include <kstat.h> 34 35 #include <sys/nsctl/nsctl.h> 36 #include <sys/nsctl/sd_bcache.h> 37 38 #include "sdbc_stats.h" 39 40 #include "dsstat.h" 41 #include "common.h" 42 #include "report.h" 43 44 static sdbcstat_t *sdbc_top; 45 kstat_t *sdbc_global = NULL; 46 47 void sdbc_header(); 48 int sdbc_value_check(sdbcstat_t *); 49 int sdbc_validate(kstat_t *); 50 uint32_t sdbc_getdelta(sdbcstat_t *, char *); 51 52 void sdbc_addstat(sdbcstat_t *); 53 sdbcstat_t *sdbc_delstat(sdbcstat_t *); 54 void center(int, char *); 55 56 /* 57 * sdbc_discover() - looks for new statistics to be monitored. 58 * Verifies that any statistics found are now already being 59 * monitored. 60 * 61 */ 62 int 63 sdbc_discover(kstat_ctl_t *kc) 64 { 65 static int validated = 0; 66 67 kstat_t *ksp; 68 69 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 70 int kinst; 71 char kname[KSTAT_STRLEN + 1]; 72 sdbcstat_t *cur; 73 sdbcstat_t *sdbcstat = NULL; 74 kstat_t *io_ksp; 75 76 if (strcmp(ksp->ks_module, SDBC_KSTAT_MODULE) != 0 || 77 strncmp(ksp->ks_name, SDBC_KSTAT_CDSTATS, 2) != 0) 78 continue; 79 80 if (kstat_read(kc, ksp, NULL) == -1) 81 continue; 82 83 /* 84 * Validate kstat structure 85 */ 86 if (! validated) { 87 if (sdbc_validate(ksp)) 88 return (EINVAL); 89 90 validated++; 91 } 92 93 /* 94 * Duplicate check 95 */ 96 for (cur = sdbc_top; cur; cur = cur->next) { 97 char *cur_vname, *tst_vname; 98 99 cur_vname = kstat_value(cur->pre_set, 100 SDBC_CDKSTAT_VOL_NAME); 101 102 tst_vname = kstat_value(ksp, 103 SDBC_CDKSTAT_VOL_NAME); 104 105 if (strncmp(cur_vname, tst_vname, NAMED_LEN) == 0) 106 goto next; 107 } 108 109 /* 110 * Initialize new record 111 */ 112 sdbcstat = (sdbcstat_t *)calloc(1, sizeof (sdbcstat_t)); 113 114 kinst = ksp->ks_instance; 115 116 /* 117 * Set kstat 118 */ 119 sdbcstat->pre_set = kstat_retrieve(kc, ksp); 120 121 if (sdbcstat->pre_set == NULL) 122 goto next; 123 124 sdbcstat->collected |= GOT_SET_KSTAT; 125 126 /* 127 * I/O kstat 128 */ 129 (void) sprintf(kname, "%s%d", SDBC_IOKSTAT_CDSTATS, kinst); 130 131 io_ksp = kstat_lookup(kc, SDBC_KSTAT_MODULE, kinst, kname); 132 sdbcstat->pre_io = kstat_retrieve(kc, io_ksp); 133 134 if (sdbcstat->pre_io == NULL) 135 goto next; 136 137 sdbcstat->collected |= GOT_IO_KSTAT; 138 139 next: 140 /* 141 * Check if we got a complete set of stats 142 */ 143 if (sdbcstat == NULL) 144 continue; 145 146 if (SDBC_COMPLETE(sdbcstat->collected)) { 147 (void) sdbc_delstat(sdbcstat); 148 continue; 149 } 150 151 sdbc_addstat(sdbcstat); 152 } 153 154 if (sdbc_top == NULL) 155 return (EAGAIN); 156 157 return (0); 158 } 159 160 /* 161 * sdbc_update() - updates all of the statistics currently being monitored. 162 * 163 */ 164 int 165 sdbc_update(kstat_ctl_t *kc) 166 { 167 kstat_t *ksp; 168 sdbcstat_t *cur; 169 170 /* Update global kstat information */ 171 ksp = kstat_lookup(kc, SDBC_KSTAT_MODULE, -1, SDBC_KSTAT_GSTATS); 172 173 if (ksp == NULL) 174 return (EAGAIN); 175 176 if (sdbc_global) 177 kstat_free(sdbc_global); 178 179 sdbc_global = kstat_retrieve(kc, ksp); 180 181 for (cur = sdbc_top; cur != NULL; cur = cur->next) { 182 int kinst; 183 char *kname, *cname, *pname; 184 185 kstat_t *set_ksp, *io_ksp; 186 187 cur->collected = 0; 188 189 /* 190 * Age off old stats 191 */ 192 if (cur->cur_set != NULL) { 193 kstat_free(cur->pre_set); 194 kstat_free(cur->pre_io); 195 196 cur->pre_set = cur->cur_set; 197 cur->pre_io = cur->cur_io; 198 } 199 200 /* 201 * Update set kstat 202 */ 203 kinst = cur->pre_set->ks_instance; 204 kname = cur->pre_set->ks_name; 205 206 set_ksp = kstat_lookup(kc, SDBC_KSTAT_MODULE, kinst, kname); 207 208 if ((cur->cur_set = kstat_retrieve(kc, set_ksp)) == NULL) 209 continue; 210 211 cur->collected |= GOT_SET_KSTAT; 212 213 /* 214 * Validate set 215 */ 216 pname = kstat_value(cur->pre_set, SDBC_CDKSTAT_VOL_NAME); 217 cname = kstat_value(cur->cur_set, SDBC_CDKSTAT_VOL_NAME); 218 219 if (strncmp(pname, cname, NAMED_LEN) != 0) 220 continue; 221 222 /* 223 * Update I/O kstat 224 */ 225 kinst = cur->pre_io->ks_instance; 226 kname = cur->pre_io->ks_name; 227 228 io_ksp = kstat_lookup(kc, SDBC_KSTAT_MODULE, kinst, kname); 229 230 if ((cur->cur_io = kstat_retrieve(kc, io_ksp)) == NULL) 231 continue; 232 233 cur->collected |= GOT_IO_KSTAT; 234 } 235 236 return (0); 237 } 238 239 /* 240 * sdbc_report() - outputs statistics for the statistics currently being 241 * monitored. Deletes statistics for volumes that have been disabled. 242 * 243 */ 244 int 245 sdbc_report() 246 { 247 vslist_t *vslist = vs_top; 248 sdbcstat_t *cur, *pre = NULL; 249 250 if (sdbc_top == NULL) 251 return (0); 252 253 for (cur = sdbc_top; cur != NULL; ) { /* CSTYLED */ 254 static uint32_t linesout = 0; 255 uint32_t *offline; 256 257 char volname[NAMED_LEN + 1]; 258 char rmode[STAT_HDR_SIZE]; 259 char wmode[STAT_HDR_SIZE]; 260 261 /* Parse volume name */ 262 (void) strncpy(volname, kstat_value(cur->pre_set, 263 SDBC_CDKSTAT_VOL_NAME), NAMED_LEN); 264 volname[NAMED_LEN] = '\0'; 265 266 /* Check to see if the user specified this volume */ 267 for (vslist = vs_top; vslist != NULL; vslist = vslist->next) 268 if (strcmp(volname, vslist->volname) == 0) 269 break; 270 271 if (vs_top != NULL && vslist == NULL) 272 goto next; 273 274 /* Check if volume is offline and zflag applies */ 275 if (zflag && sdbc_value_check(cur) == 0) 276 goto next; 277 278 /* Output volume name */ 279 sdbc_header(); 280 281 (void) printf(DATA_C16, volname); 282 283 if (SDBC_COMPLETE(cur->collected)) { 284 sdbcstat_t *next = sdbc_delstat(cur); 285 286 if (! pre) 287 cur = sdbc_top = next; 288 else 289 cur = pre->next = next; 290 291 (void) printf(" <<volume disabled>>\n"); 292 continue; 293 } 294 295 offline = kstat_value(cur->cur_set, SDBC_CDKSTAT_FAILED); 296 if (*offline) { 297 (void) printf(" <<volume offline>>\n"); 298 linesout++; 299 goto next; 300 } 301 302 /* Type/status flags */ 303 if (dflags & FLAGS) { 304 305 uint32_t *dhint, *nhint; 306 uint32_t hints; 307 308 dhint = kstat_value(cur->cur_set, SDBC_CDKSTAT_CDHINTS); 309 nhint = kstat_value(sdbc_global, SDBC_GKSTAT_NODEHINTS); 310 311 if (! nhint) 312 return (EINVAL); 313 314 hints = *nhint; 315 hints &= (NSC_FORCED_WRTHRU | NSC_NO_FORCED_WRTHRU | 316 NSC_NOCACHE); 317 hints |= *dhint; 318 319 if (hints & NSC_NOCACHE) 320 (void) strcpy(rmode, "D"); 321 else 322 (void) strcpy(rmode, "C"); 323 324 if ((hints & NSC_FORCED_WRTHRU) || (hints & NSC_WRTHRU)) 325 (void) strcpy(wmode, "D"); 326 else 327 (void) strcpy(wmode, "C"); 328 329 (void) printf(DATA_C2, rmode); 330 (void) printf(DATA_C2, wmode); 331 } 332 333 /* Output set information */ 334 cd_report(cur); 335 336 next: 337 pre = cur; 338 cur = cur->next; 339 } 340 341 return (0); 342 } 343 344 /* 345 * sdbc_header() - outputs an appropriate header by referencing the 346 * global variables dflsgs 347 * 348 */ 349 void 350 sdbc_header() 351 { 352 int rcount = 0; 353 354 if (hflags == HEADERS_EXL) 355 if ((linesout % DISPLAY_LINES) != 0) 356 return; 357 358 if (hflags == HEADERS_BOR) 359 if (linesout != 0) 360 return; 361 362 if (hflags & HEADERS_ATT) 363 if (hflags & HEADERS_OUT) 364 return; 365 else 366 hflags |= HEADERS_OUT; 367 368 if (linesout) 369 (void) printf("\n"); 370 371 /* first line header */ 372 if (! (dflags & SUMMARY) && dflags != FLAGS) { 373 374 (void) printf(VOL_HDR_FMT, " "); 375 376 if (dflags & FLAGS) { 377 (void) printf(STAT_HDR_FMT, " "); 378 (void) printf(STAT_HDR_FMT, " "); 379 } 380 381 if (dflags & READ) { 382 int size; 383 384 size = KPS_HDR_SIZE * 2 + HIT_HDR_SIZE; 385 center(size, "- read -"); 386 rcount++; 387 } 388 389 if (dflags & WRITE) { 390 int size; 391 392 size = KPS_HDR_SIZE * 2 + HIT_HDR_SIZE; 393 center(size, "- write -"); 394 rcount++; 395 } 396 397 if (dflags != FLAGS) 398 (void) printf("\n"); 399 } 400 401 /* second line header */ 402 (void) printf(VOL_HDR_FMT, "volume"); 403 404 if (dflags & FLAGS) { 405 (void) printf(STAT_HDR_FMT, "rd"); 406 (void) printf(STAT_HDR_FMT, "wr"); 407 } 408 409 if (dflags & SUMMARY) { 410 (void) printf(KPS_HDR_FMT, "ckps"); 411 (void) printf(KPS_HDR_FMT, "dkps"); 412 (void) printf(HIT_HDR_FMT, HIT_HDR_TXT); 413 414 goto out; 415 } 416 417 if (dflags & READ) { 418 (void) printf(KPS_HDR_FMT, "ckps"); 419 (void) printf(KPS_HDR_FMT, "dkps"); 420 (void) printf(HIT_HDR_FMT, RHIT_HDR_TXT); 421 } 422 423 if (dflags & WRITE) { 424 (void) printf(KPS_HDR_FMT, "ckps"); 425 (void) printf(KPS_HDR_FMT, "dkps"); 426 (void) printf(HIT_HDR_FMT, WHIT_HDR_TXT); 427 } 428 429 if (dflags & DESTAGED) 430 (void) printf(KPS_HDR_FMT, "dstg"); 431 432 if (dflags & WRCANCEL) 433 (void) printf(KPS_HDR_FMT, "cwrl"); 434 435 out: 436 (void) printf("\n"); 437 } 438 439 /* 440 * sdbc_getstat() - find cache stat by name matching 441 * 442 * paraemters 443 * char *vn - the volume name to match against 444 * returns 445 * sdbcstat_t * - the matching strcture, NULL if not found 446 */ 447 sdbcstat_t * 448 sdbc_getstat(char *vn) 449 { 450 sdbcstat_t *cur, *pre = NULL; 451 452 for (cur = sdbc_top; cur; ) { /* CSTYLED */ 453 char *volname = 454 kstat_value(cur->pre_set, SDBC_CDKSTAT_VOL_NAME); 455 456 if (SDBC_COMPLETE(cur->collected)) { 457 sdbcstat_t *next = sdbc_delstat(cur); 458 459 if (! pre) 460 cur = sdbc_top = next; 461 else 462 cur = pre->next = next; 463 464 continue; 465 } 466 467 if (strncmp(volname, vn, NAMED_LEN) == 0) 468 return (cur); 469 470 pre = cur; 471 cur = cur->next; 472 } 473 474 return (NULL); 475 } 476 477 /* 478 * sdbc_addstat() - adds a fully populated sdbcstat_t structure 479 * to the linked list of currently monitored kstats. The structure 480 * will be added in alphabetical order, using the volume name as the 481 * key. 482 * 483 * parameters 484 * sdbcstat_t *sdbcstat - to be added to the list. 485 * 486 */ 487 void 488 sdbc_addstat(sdbcstat_t *sdbcstat) 489 { 490 sdbcstat_t *cur; 491 492 if (sdbc_top == NULL) { 493 sdbc_top = sdbcstat; 494 return; 495 } 496 497 for (cur = sdbc_top; cur != NULL; cur = cur->next) { 498 char *cur_vname, *nxt_vname, *tst_vname; 499 500 cur_vname = kstat_value(cur->pre_set, 501 SDBC_CDKSTAT_VOL_NAME); 502 tst_vname = kstat_value(sdbcstat->pre_set, 503 SDBC_CDKSTAT_VOL_NAME); 504 505 if (strncmp(cur_vname, tst_vname, NAMED_LEN) > 0) { 506 if (cur == sdbc_top) 507 sdbc_top = sdbcstat; 508 509 sdbcstat->next = cur; 510 511 return; 512 } 513 514 /* 515 * If we get to the last item in the list, then just 516 * add this one to the end 517 */ 518 if (cur->next == NULL) { 519 cur->next = sdbcstat; 520 return; 521 } 522 523 nxt_vname = kstat_value(cur->next->pre_set, 524 SDBC_CDKSTAT_VOL_NAME); 525 526 if (strncmp(nxt_vname, tst_vname, NAMED_LEN) > 0) { 527 sdbcstat->next = cur->next; 528 cur->next = sdbcstat; 529 return; 530 } 531 } 532 } 533 534 /* 535 * sdbc_delstat() - deallocate memory for the structure being 536 * passed in. 537 * 538 * parameters 539 * sdbcstat_t *sdbcstat - structure to be deallocated 540 * 541 * returns 542 * sdbcstat_t * - pointer to the "next" structures in the 543 * linked list. May be NULL if we are removing the last 544 * structure in the linked list. 545 */ 546 sdbcstat_t * 547 sdbc_delstat(sdbcstat_t *sdbcstat) 548 { 549 550 sdbcstat_t *next = sdbcstat->next; 551 552 kstat_free(sdbcstat->pre_set); 553 kstat_free(sdbcstat->pre_io); 554 kstat_free(sdbcstat->cur_set); 555 kstat_free(sdbcstat->cur_io); 556 557 free(sdbcstat); 558 sdbcstat = NULL; 559 560 return (next); 561 } 562 563 /* 564 * sdbc_value_check() - Checks for activity, supports -z switch 565 * 566 * parameters 567 * sdbcstat_t *sdbcstat - structure to be checked 568 * 569 * returns 570 * 1 - activity 571 * 0 - no activity 572 */ 573 int 574 sdbc_value_check(sdbcstat_t *sdbcstat) 575 { 576 if (SDBC_COMPLETE(sdbcstat->collected)) 577 return (1); 578 579 if (sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_CACHE_READ) != 0) 580 return (1); 581 582 if (sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_DISK_READ) != 0) 583 return (1); 584 585 if (sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_CACHE_WRITE) != 0) 586 return (1); 587 588 if (sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_DISK_WRITE) != 0) 589 return (1); 590 591 if (sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_WRCANCELNS) != 0) 592 return (1); 593 594 if (io_value_check(sdbcstat->pre_io->ks_data, 595 sdbcstat->cur_io->ks_data) != 0) 596 return (1); 597 598 return (0); 599 } 600 601 /* 602 * sdbc_validate() - validates the structure of the kstats by attempting to 603 * lookup fields used by this module 604 * 605 * parameters 606 * kstat_t *ksp - kstat to be examined 607 * 608 * returns 609 * 1 - one or more fields missing 610 * 0 - all fields present 611 */ 612 int 613 sdbc_validate(kstat_t *ksp) 614 { 615 if (! kstat_value(ksp, SDBC_CDKSTAT_VOL_NAME) || 616 ! kstat_value(ksp, SDBC_CDKSTAT_FAILED) || 617 ! kstat_value(ksp, SDBC_CDKSTAT_CDHINTS) || 618 ! kstat_value(ksp, SDBC_CDKSTAT_CACHE_READ) || 619 ! kstat_value(ksp, SDBC_CDKSTAT_DISK_READ) || 620 ! kstat_value(ksp, SDBC_CDKSTAT_CACHE_WRITE) || 621 ! kstat_value(ksp, SDBC_CDKSTAT_DISK_WRITE) || 622 ! kstat_value(ksp, SDBC_CDKSTAT_DESTAGED) || 623 ! kstat_value(ksp, SDBC_CDKSTAT_WRCANCELNS)) 624 return (1); 625 626 return (0); 627 } 628 629 /* 630 * sdbc_getvalues() - populates a values structure with data obtained from the 631 * kstat 632 * 633 * parameters 634 * sdbcstat_t *sdbcstat - pointer to the structure containing the kstats 635 * sdbcvals_t *vals - pointer to the structure that will receive the values 636 * int flags - flags that describe adjustments made to the values 637 * 638 * returns 639 * 1 - failure 640 * 0 - success 641 */ 642 int 643 sdbc_getvalues(sdbcstat_t *sdbcstat, sdbcvals_t *vals, int flags) 644 { 645 int divisor = 0; 646 int factors; 647 uint64_t hr_etime; 648 double etime; 649 650 kstat_io_t *cur; 651 kstat_io_t *pre; 652 653 if (sdbcstat == NULL) 654 return (1); 655 656 cur = sdbcstat->cur_io->ks_data; 657 pre = sdbcstat->pre_io->ks_data; 658 659 hr_etime = hrtime_delta(pre->rlastupdate, cur->rlastupdate); 660 etime = hr_etime / (double)NANOSEC; 661 662 /* read data */ 663 vals->cache_read = 664 FBA_SIZE(sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_CACHE_READ)); 665 vals->disk_read = 666 FBA_SIZE(sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_DISK_READ)); 667 668 669 vals->total_reads = vals->cache_read + vals->disk_read; 670 671 if (vals->cache_read == 0) 672 vals->read_hit = 0.0; 673 else 674 vals->read_hit = 675 ((float)vals->cache_read / vals->total_reads) * 100.0; 676 677 /* write data */ 678 vals->cache_write = 679 FBA_SIZE(sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_CACHE_WRITE)); 680 vals->disk_write = 681 FBA_SIZE(sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_DISK_WRITE)); 682 683 vals->total_writes = vals->cache_write + vals->disk_write; 684 685 vals->destaged = 686 FBA_SIZE(sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_DESTAGED)); 687 688 if (vals->cache_write == 0) 689 vals->write_hit = 0.0; 690 else 691 vals->write_hit = ((float)vals->cache_write / 692 (vals->total_writes - vals->destaged)) * 100.0; 693 694 /* miscellaneous */ 695 vals->write_cancellations = 696 FBA_SIZE(sdbc_getdelta(sdbcstat, SDBC_CDKSTAT_WRCANCELNS)); 697 698 vals->total_cache = vals->cache_read + vals->cache_write; 699 vals->total_disk = vals->disk_read + vals->disk_write; 700 701 /* total cache hit calculation */ 702 vals->cache_hit = 0; 703 factors = 0; 704 705 if (vals->cache_read != 0) { 706 vals->cache_hit += vals->read_hit; 707 factors++; 708 } 709 710 if (vals->cache_write != 0) { 711 vals->cache_hit += vals->write_hit; 712 factors++; 713 } 714 715 if (vals->cache_hit) 716 vals->cache_hit /= (float)factors; 717 718 /* adjustments */ 719 divisor = 1; 720 721 if (flags & SDBC_KBYTES) 722 divisor *= KILOBYTE; 723 if ((flags & SDBC_INTAVG) && (etime > 0)) 724 divisor *= etime; 725 726 if (divisor != 1) { 727 vals->cache_read /= divisor; 728 vals->disk_read /= divisor; 729 vals->total_reads /= divisor; 730 731 vals->cache_write /= divisor; 732 vals->disk_write /= divisor; 733 vals->total_writes /= divisor; 734 735 vals->total_cache /= divisor; 736 vals->total_disk /= divisor; 737 738 vals->destaged /= divisor; 739 vals->write_cancellations /= divisor; 740 } 741 742 return (0); 743 } 744 745 /* 746 * sdbc_getdelta() - calculates the difference between two kstat fields 747 * 748 * parameters 749 * sdbcstat_t *sdbcstat - the SDBC stat strcture containing the two fields 750 * char *name - the name of the fields 751 * returns 752 * uint32_t value of the differences adjusted for overflow of the data type 753 */ 754 uint32_t 755 sdbc_getdelta(sdbcstat_t *sdbcstat, char *name) 756 { 757 uint32_t *cur_val; 758 uint32_t *pre_val; 759 760 pre_val = kstat_value(sdbcstat->pre_set, name); 761 cur_val = kstat_value(sdbcstat->cur_set, name); 762 763 return (u32_delta(*pre_val, *cur_val)); 764 } 765 766 void 767 center(int size, char *hdr) 768 { 769 int lpad = 0; 770 int rpad = 0; 771 char fmt[10]; 772 773 if (size == 0) 774 return; 775 776 if (strlen(hdr) < size) { 777 lpad = (size - strlen(hdr)) / 2; 778 779 if (lpad * 2 < size) 780 lpad++; 781 782 rpad = size - (lpad + strlen(hdr)); 783 } 784 785 output: 786 (void) sprintf(fmt, "%%%ds%%s%%%ds", lpad, rpad); 787 (void) printf(fmt, " ", hdr, " "); 788 }