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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include "statcommon.h" 27 #include "dsr.h" 28 29 #include <sys/dklabel.h> 30 #include <sys/dktp/fdisk.h> 31 #include <stdlib.h> 32 #include <stdarg.h> 33 #include <unistd.h> 34 #include <strings.h> 35 #include <errno.h> 36 #include <limits.h> 37 38 static void insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev); 39 40 static struct iodev_snapshot * 41 make_controller(int cid) 42 { 43 struct iodev_snapshot *new; 44 45 new = safe_alloc(sizeof (struct iodev_snapshot)); 46 (void) memset(new, 0, sizeof (struct iodev_snapshot)); 47 new->is_type = IODEV_CONTROLLER; 48 new->is_id.id = cid; 49 new->is_parent_id.id = IODEV_NO_ID; 50 51 (void) snprintf(new->is_name, sizeof (new->is_name), "c%d", cid); 52 53 return (new); 54 } 55 56 static struct iodev_snapshot * 57 find_iodev_by_name(struct iodev_snapshot *list, const char *name) 58 { 59 struct iodev_snapshot *pos; 60 struct iodev_snapshot *pos2; 61 62 for (pos = list; pos; pos = pos->is_next) { 63 if (strcmp(pos->is_name, name) == 0) 64 return (pos); 65 66 pos2 = find_iodev_by_name(pos->is_children, name); 67 if (pos2 != NULL) 68 return (pos2); 69 } 70 71 return (NULL); 72 } 73 74 static enum iodev_type 75 parent_iodev_type(enum iodev_type type) 76 { 77 switch (type) { 78 case IODEV_CONTROLLER: return (0); 79 case IODEV_IOPATH_LT: return (0); 80 case IODEV_IOPATH_LI: return (0); 81 case IODEV_NFS: return (0); 82 case IODEV_TAPE: return (0); 83 case IODEV_IOPATH_LTI: return (IODEV_DISK); 84 case IODEV_DISK: return (IODEV_CONTROLLER); 85 case IODEV_PARTITION: return (IODEV_DISK); 86 } 87 return (IODEV_UNKNOWN); 88 } 89 90 static int 91 id_match(struct iodev_id *id1, struct iodev_id *id2) 92 { 93 return (id1->id == id2->id && 94 strcmp(id1->tid, id2->tid) == 0); 95 } 96 97 static struct iodev_snapshot * 98 find_parent(struct snapshot *ss, struct iodev_snapshot *iodev) 99 { 100 enum iodev_type parent_type = parent_iodev_type(iodev->is_type); 101 struct iodev_snapshot *pos; 102 struct iodev_snapshot *pos2; 103 104 if (parent_type == 0 || parent_type == IODEV_UNKNOWN) 105 return (NULL); 106 107 if (iodev->is_parent_id.id == IODEV_NO_ID && 108 iodev->is_parent_id.tid[0] == '\0') 109 return (NULL); 110 111 if (parent_type == IODEV_CONTROLLER) { 112 for (pos = ss->s_iodevs; pos; pos = pos->is_next) { 113 if (pos->is_type != IODEV_CONTROLLER) 114 continue; 115 if (pos->is_id.id != iodev->is_parent_id.id) 116 continue; 117 return (pos); 118 } 119 120 if (!(ss->s_types & SNAP_CONTROLLERS)) 121 return (NULL); 122 123 pos = make_controller(iodev->is_parent_id.id); 124 insert_iodev(ss, pos); 125 return (pos); 126 } 127 128 /* IODEV_DISK parent */ 129 for (pos = ss->s_iodevs; pos; pos = pos->is_next) { 130 if (id_match(&iodev->is_parent_id, &pos->is_id) && 131 pos->is_type == IODEV_DISK) 132 return (pos); 133 if (pos->is_type != IODEV_CONTROLLER) 134 continue; 135 for (pos2 = pos->is_children; pos2; pos2 = pos2->is_next) { 136 if (pos2->is_type != IODEV_DISK) 137 continue; 138 if (id_match(&iodev->is_parent_id, &pos2->is_id)) 139 return (pos2); 140 } 141 } 142 143 return (NULL); 144 } 145 146 /* 147 * Introduce an index into the list to speed up insert_into looking for the 148 * right position in the list. This index is an AVL tree of all the 149 * iodev_snapshot in the list. 150 */ 151 152 #define offsetof(s, m) (size_t)(&(((s *)0)->m)) /* for avl_create */ 153 154 static int 155 avl_iodev_cmp(const void* is1, const void* is2) 156 { 157 int c = iodev_cmp((struct iodev_snapshot *)is1, 158 (struct iodev_snapshot *)is2); 159 160 if (c > 0) 161 return (1); 162 163 if (c < 0) 164 return (-1); 165 166 return (0); 167 } 168 169 static void 170 ix_new_list(struct iodev_snapshot *elem) 171 { 172 avl_tree_t *l = malloc(sizeof (avl_tree_t)); 173 174 elem->avl_list = l; 175 if (l == NULL) 176 return; 177 178 avl_create(l, avl_iodev_cmp, sizeof (struct iodev_snapshot), 179 offsetof(struct iodev_snapshot, avl_link)); 180 181 avl_add(l, elem); 182 } 183 184 static void 185 ix_list_del(struct iodev_snapshot *elem) 186 { 187 avl_tree_t *l = elem->avl_list; 188 189 if (l == NULL) 190 return; 191 192 elem->avl_list = NULL; 193 194 avl_remove(l, elem); 195 if (avl_numnodes(l) == 0) { 196 avl_destroy(l); 197 free(l); 198 } 199 } 200 201 static void 202 ix_insert_here(struct iodev_snapshot *pos, struct iodev_snapshot *elem, int ba) 203 { 204 avl_tree_t *l = pos->avl_list; 205 elem->avl_list = l; 206 207 if (l == NULL) 208 return; 209 210 avl_insert_here(l, elem, pos, ba); 211 } 212 213 static void 214 list_del(struct iodev_snapshot **list, struct iodev_snapshot *pos) 215 { 216 ix_list_del(pos); 217 218 if (*list == pos) 219 *list = pos->is_next; 220 if (pos->is_next) 221 pos->is_next->is_prev = pos->is_prev; 222 if (pos->is_prev) 223 pos->is_prev->is_next = pos->is_next; 224 pos->is_prev = pos->is_next = NULL; 225 } 226 227 static void 228 insert_before(struct iodev_snapshot **list, struct iodev_snapshot *pos, 229 struct iodev_snapshot *new) 230 { 231 if (pos == NULL) { 232 new->is_prev = new->is_next = NULL; 233 *list = new; 234 ix_new_list(new); 235 return; 236 } 237 238 new->is_next = pos; 239 new->is_prev = pos->is_prev; 240 if (pos->is_prev) 241 pos->is_prev->is_next = new; 242 else 243 *list = new; 244 pos->is_prev = new; 245 246 ix_insert_here(pos, new, AVL_BEFORE); 247 } 248 249 static void 250 insert_after(struct iodev_snapshot **list, struct iodev_snapshot *pos, 251 struct iodev_snapshot *new) 252 { 253 if (pos == NULL) { 254 new->is_prev = new->is_next = NULL; 255 *list = new; 256 ix_new_list(new); 257 return; 258 } 259 260 new->is_next = pos->is_next; 261 new->is_prev = pos; 262 if (pos->is_next) 263 pos->is_next->is_prev = new; 264 pos->is_next = new; 265 266 ix_insert_here(pos, new, AVL_AFTER); 267 } 268 269 static void 270 insert_into(struct iodev_snapshot **list, struct iodev_snapshot *iodev) 271 { 272 struct iodev_snapshot *tmp = *list; 273 avl_tree_t *l; 274 void *p; 275 avl_index_t where; 276 277 if (*list == NULL) { 278 *list = iodev; 279 ix_new_list(iodev); 280 return; 281 } 282 283 /* 284 * Optimize the search: instead of walking the entire list 285 * (which can contain thousands of nodes), search in the AVL 286 * tree the nearest node and reposition the startup point to 287 * this node rather than always starting from the beginning 288 * of the list. 289 */ 290 l = tmp->avl_list; 291 if (l != NULL) { 292 p = avl_find(l, iodev, &where); 293 if (p == NULL) { 294 p = avl_nearest(l, where, AVL_BEFORE); 295 } 296 if (p != NULL) { 297 tmp = (struct iodev_snapshot *)p; 298 } 299 } 300 301 for (;;) { 302 if (iodev_cmp(tmp, iodev) > 0) { 303 insert_before(list, tmp, iodev); 304 return; 305 } 306 307 if (tmp->is_next == NULL) 308 break; 309 310 tmp = tmp->is_next; 311 } 312 313 insert_after(list, tmp, iodev); 314 } 315 316 static int 317 disk_or_partition(enum iodev_type type) 318 { 319 return (type == IODEV_DISK || type == IODEV_PARTITION); 320 } 321 322 static int 323 disk_or_partition_or_iopath(enum iodev_type type) 324 { 325 return (type == IODEV_DISK || type == IODEV_PARTITION || 326 type == IODEV_IOPATH_LTI); 327 } 328 329 static void 330 insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev) 331 { 332 struct iodev_snapshot *parent = find_parent(ss, iodev); 333 struct iodev_snapshot **list; 334 335 if (parent != NULL) { 336 list = &parent->is_children; 337 parent->is_nr_children++; 338 } else { 339 list = &ss->s_iodevs; 340 ss->s_nr_iodevs++; 341 } 342 343 insert_into(list, iodev); 344 } 345 346 /* return 1 if dev passes filter */ 347 static int 348 iodev_match(struct iodev_snapshot *dev, struct iodev_filter *df) 349 { 350 int is_floppy = (strncmp(dev->is_name, "fd", 2) == 0); 351 char *isn, *ispn, *ifn; 352 char *path; 353 int ifnl; 354 size_t i; 355 356 /* no filter, pass */ 357 if (df == NULL) 358 return (1); /* pass */ 359 360 /* no filtered names, pass if not floppy and skipped */ 361 if (df->if_nr_names == NULL) 362 return (!(df->if_skip_floppy && is_floppy)); 363 364 isn = dev->is_name; 365 ispn = dev->is_pretty; 366 for (i = 0; i < df->if_nr_names; i++) { 367 ifn = df->if_names[i]; 368 ifnl = strlen(ifn); 369 path = strchr(ifn, '.'); 370 371 if ((strcmp(isn, ifn) == 0) || 372 (ispn && (strcmp(ispn, ifn) == 0))) 373 return (1); /* pass */ 374 375 /* if filter is a path allow partial match */ 376 if (path && 377 ((strncmp(isn, ifn, ifnl) == 0) || 378 (ispn && (strncmp(ispn, ifn, ifnl) == 0)))) 379 return (1); /* pass */ 380 } 381 382 return (0); /* fail */ 383 } 384 385 /* return 1 if path is an mpxio path associated with dev */ 386 static int 387 iodev_path_match(struct iodev_snapshot *dev, struct iodev_snapshot *path) 388 { 389 char *dn, *pn; 390 int dnl; 391 392 dn = dev->is_name; 393 pn = path->is_name; 394 dnl = strlen(dn); 395 396 if ((strncmp(pn, dn, dnl) == 0) && (pn[dnl] == '.')) 397 return (1); /* yes */ 398 399 return (0); /* no */ 400 } 401 402 /* select which I/O devices to collect stats for */ 403 static void 404 choose_iodevs(struct snapshot *ss, struct iodev_snapshot *iodevs, 405 struct iodev_filter *df) 406 { 407 struct iodev_snapshot *pos, *ppos, *tmp, *ptmp; 408 int nr_iodevs; 409 int nr_iodevs_orig; 410 411 nr_iodevs = df ? df->if_max_iodevs : UNLIMITED_IODEVS; 412 nr_iodevs_orig = nr_iodevs; 413 414 if (nr_iodevs == UNLIMITED_IODEVS) 415 nr_iodevs = INT_MAX; 416 417 /* add the full matches */ 418 pos = iodevs; 419 while (pos && nr_iodevs) { 420 tmp = pos; 421 pos = pos->is_next; 422 423 if (!iodev_match(tmp, df)) 424 continue; /* failed full match */ 425 426 list_del(&iodevs, tmp); 427 insert_iodev(ss, tmp); 428 429 /* 430 * Add all mpxio paths associated with match above. Added 431 * paths don't count against nr_iodevs. 432 */ 433 if (strchr(tmp->is_name, '.') == NULL) { 434 ppos = iodevs; 435 while (ppos) { 436 ptmp = ppos; 437 ppos = ppos->is_next; 438 439 if (!iodev_path_match(tmp, ptmp)) 440 continue; /* not an mpxio path */ 441 442 list_del(&iodevs, ptmp); 443 insert_iodev(ss, ptmp); 444 if (pos == ptmp) 445 pos = ppos; 446 } 447 } 448 449 nr_iodevs--; 450 } 451 452 /* 453 * If we had a filter, and *nothing* passed the filter then we 454 * don't want to fill the remaining slots - it is just confusing 455 * if we don that, it makes it look like the filter code is broken. 456 */ 457 if ((df->if_nr_names == NULL) || (nr_iodevs != nr_iodevs_orig)) { 458 /* now insert any iodevs into the remaining slots */ 459 pos = iodevs; 460 while (pos && nr_iodevs) { 461 tmp = pos; 462 pos = pos->is_next; 463 464 if (df && df->if_skip_floppy && 465 strncmp(tmp->is_name, "fd", 2) == 0) 466 continue; 467 468 list_del(&iodevs, tmp); 469 insert_iodev(ss, tmp); 470 471 --nr_iodevs; 472 } 473 } 474 475 /* clear the unwanted ones */ 476 pos = iodevs; 477 while (pos) { 478 struct iodev_snapshot *tmp = pos; 479 pos = pos->is_next; 480 free_iodev(tmp); 481 } 482 } 483 484 static int 485 collate_controller(struct iodev_snapshot *controller, 486 struct iodev_snapshot *disk) 487 { 488 controller->is_stats.nread += disk->is_stats.nread; 489 controller->is_stats.nwritten += disk->is_stats.nwritten; 490 controller->is_stats.reads += disk->is_stats.reads; 491 controller->is_stats.writes += disk->is_stats.writes; 492 controller->is_stats.wtime += disk->is_stats.wtime; 493 controller->is_stats.wlentime += disk->is_stats.wlentime; 494 controller->is_stats.rtime += disk->is_stats.rtime; 495 controller->is_stats.rlentime += disk->is_stats.rlentime; 496 controller->is_crtime += disk->is_crtime; 497 controller->is_snaptime += disk->is_snaptime; 498 if (kstat_add(&disk->is_errors, &controller->is_errors)) 499 return (errno); 500 return (0); 501 } 502 503 static int 504 acquire_iodev_stats(struct iodev_snapshot *list, kstat_ctl_t *kc) 505 { 506 struct iodev_snapshot *pos; 507 int err = 0; 508 509 for (pos = list; pos; pos = pos->is_next) { 510 /* controllers don't have stats (yet) */ 511 if (pos->is_ksp != NULL) { 512 if (kstat_read(kc, pos->is_ksp, &pos->is_stats) == -1) 513 return (errno); 514 /* make sure crtime/snaptime is updated */ 515 pos->is_crtime = pos->is_ksp->ks_crtime; 516 pos->is_snaptime = pos->is_ksp->ks_snaptime; 517 } 518 519 if ((err = acquire_iodev_stats(pos->is_children, kc))) 520 return (err); 521 522 if (pos->is_type == IODEV_CONTROLLER) { 523 struct iodev_snapshot *pos2 = pos->is_children; 524 525 for (; pos2; pos2 = pos2->is_next) { 526 if ((err = collate_controller(pos, pos2))) 527 return (err); 528 } 529 } 530 } 531 532 return (0); 533 } 534 535 static int 536 acquire_iodev_errors(struct snapshot *ss, kstat_ctl_t *kc) 537 { 538 kstat_t *ksp; 539 540 if (!(ss->s_types && SNAP_IODEV_ERRORS)) 541 return (0); 542 543 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 544 char kstat_name[KSTAT_STRLEN]; 545 char *dname = kstat_name; 546 char *ename = ksp->ks_name; 547 struct iodev_snapshot *iodev; 548 549 if (ksp->ks_type != KSTAT_TYPE_NAMED) 550 continue; 551 if (strncmp(ksp->ks_class, "device_error", 12) != 0 && 552 strncmp(ksp->ks_class, "iopath_error", 12) != 0) 553 continue; 554 555 /* 556 * Some drivers may not follow the naming convention 557 * for error kstats (i.e., drivername,err) so 558 * be sure we don't walk off the end. 559 */ 560 while (*ename && *ename != ',') { 561 *dname = *ename; 562 dname++; 563 ename++; 564 } 565 *dname = '\0'; 566 567 iodev = find_iodev_by_name(ss->s_iodevs, kstat_name); 568 569 if (iodev == NULL) 570 continue; 571 572 if (kstat_read(kc, ksp, NULL) == -1) 573 return (errno); 574 if (kstat_copy(ksp, &iodev->is_errors) == -1) 575 return (errno); 576 } 577 578 return (0); 579 } 580 581 static void 582 get_ids(struct iodev_snapshot *iodev, const char *pretty) 583 { 584 int ctr, disk, slice, ret; 585 char *target; 586 const char *p1; 587 const char *p2; 588 589 if (pretty == NULL) 590 return; 591 592 if (sscanf(pretty, "c%d", &ctr) != 1) 593 return; 594 595 p1 = pretty; 596 while (*p1 && *p1 != 't') 597 ++p1; 598 599 if (!*p1) 600 return; 601 ++p1; 602 603 p2 = p1; 604 while (*p2 && *p2 != 'd') 605 ++p2; 606 607 if (!*p2 || p2 == p1) 608 return; 609 610 target = safe_alloc(1 + p2 - p1); 611 (void) strlcpy(target, p1, 1 + p2 - p1); 612 613 ret = sscanf(p2, "d%d%*[sp]%d", &disk, &slice); 614 615 if (ret == 2 && iodev->is_type == IODEV_PARTITION) { 616 iodev->is_id.id = slice; 617 iodev->is_parent_id.id = disk; 618 (void) strlcpy(iodev->is_parent_id.tid, target, KSTAT_STRLEN); 619 } else if (ret == 1) { 620 if (iodev->is_type == IODEV_DISK) { 621 iodev->is_id.id = disk; 622 (void) strlcpy(iodev->is_id.tid, target, KSTAT_STRLEN); 623 iodev->is_parent_id.id = ctr; 624 } else if (iodev->is_type == IODEV_IOPATH_LTI) { 625 iodev->is_parent_id.id = disk; 626 (void) strlcpy(iodev->is_parent_id.tid, 627 target, KSTAT_STRLEN); 628 } 629 } 630 631 free(target); 632 } 633 634 static void 635 get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev, 636 kstat_ctl_t *kc) 637 { 638 disk_list_t *dl; 639 char *pretty = NULL; 640 641 if (iodev->is_type == IODEV_NFS) { 642 if (!(types & SNAP_IODEV_PRETTY)) 643 return; 644 645 iodev->is_pretty = lookup_nfs_name(iodev->is_name, kc); 646 return; 647 } 648 649 /* lookup/translate the kstat name */ 650 dl = lookup_ks_name(iodev->is_name, (types & SNAP_IODEV_DEVID) ? 1 : 0); 651 if (dl == NULL) 652 return; 653 654 if (dl->dsk) 655 pretty = safe_strdup(dl->dsk); 656 657 if (types & SNAP_IODEV_PRETTY) { 658 if (dl->dname) 659 iodev->is_dname = safe_strdup(dl->dname); 660 } 661 662 if (dl->devidstr) 663 iodev->is_devid = safe_strdup(dl->devidstr); 664 665 get_ids(iodev, pretty); 666 667 /* 668 * we fill in pretty name wether it is asked for or not because 669 * it could be used in a filter by match_iodevs. 670 */ 671 iodev->is_pretty = pretty; 672 } 673 674 static enum iodev_type 675 get_iodev_type(kstat_t *ksp) 676 { 677 if (strcmp(ksp->ks_class, "disk") == 0) 678 return (IODEV_DISK); 679 if (strcmp(ksp->ks_class, "partition") == 0) 680 return (IODEV_PARTITION); 681 if (strcmp(ksp->ks_class, "nfs") == 0) 682 return (IODEV_NFS); 683 if (strcmp(ksp->ks_class, "iopath") == 0) 684 return (IODEV_IOPATH_LTI); 685 if (strcmp(ksp->ks_class, "tape") == 0) 686 return (IODEV_TAPE); 687 return (IODEV_UNKNOWN); 688 } 689 690 /* get the lun/target/initiator from the name, return 1 on success */ 691 static int 692 get_lti(char *s, 693 char *lname, int *l, char *tname, int *t, char *iname, int *i) 694 { 695 int num = 0; 696 697 num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z_]%d", lname, l, 698 tname, t, iname, i); 699 return ((num == 6) ? 1 : 0); 700 } 701 702 703 /* get the lun, target, and initiator name and instance */ 704 static void 705 get_path_info(struct iodev_snapshot *io, char *mod, int *type, int *inst, 706 char *name, size_t size) 707 { 708 709 /* 710 * If it is iopath or ssd then pad the name with i/t/l so we can sort 711 * by alpha order and set type for IOPATH to DISK since we want to 712 * have it grouped with its ssd parent. The lun can be 5 digits, 713 * the target can be 4 digits, and the initiator can be 3 digits and 714 * the padding is done appropriately for string comparisons. 715 */ 716 if (disk_or_partition_or_iopath(io->is_type)) { 717 int i1, t1, l1; 718 char tname[KSTAT_STRLEN], iname[KSTAT_STRLEN]; 719 char *ptr, lname[KSTAT_STRLEN]; 720 721 i1 = t1 = l1 = 0; 722 (void) get_lti(io->is_name, lname, &l1, tname, &t1, iname, &i1); 723 *type = io->is_type; 724 if (io->is_type == IODEV_DISK) { 725 (void) snprintf(name, size, "%s%05d", lname, l1); 726 } else if (io->is_type == IODEV_PARTITION) { 727 ptr = strchr(io->is_name, ','); 728 (void) snprintf(name, size, "%s%05d%s", lname, l1, ptr); 729 } else { 730 (void) snprintf(name, size, "%s%05d.%s%04d.%s%03d", 731 lname, l1, tname, t1, iname, i1); 732 /* set to disk so we sort with disks */ 733 *type = IODEV_DISK; 734 } 735 (void) strcpy(mod, lname); 736 *inst = l1; 737 } else { 738 (void) strcpy(mod, io->is_module); 739 (void) strcpy(name, io->is_name); 740 *type = io->is_type; 741 *inst = io->is_instance; 742 } 743 } 744 745 int 746 iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2) 747 { 748 int type1, type2; 749 int inst1, inst2; 750 char name1[KSTAT_STRLEN], name2[KSTAT_STRLEN]; 751 char mod1[KSTAT_STRLEN], mod2[KSTAT_STRLEN]; 752 753 get_path_info(io1, mod1, &type1, &inst1, name1, sizeof (name1)); 754 get_path_info(io2, mod2, &type2, &inst2, name2, sizeof (name2)); 755 if ((!disk_or_partition(type1)) || 756 (!disk_or_partition(type2))) { 757 /* neutral sort order between disk and part */ 758 if (type1 < type2) { 759 return (-1); 760 } 761 if (type1 > type2) { 762 return (1); 763 } 764 } 765 766 /* controller doesn't have ksp */ 767 if (io1->is_ksp && io2->is_ksp) { 768 if (strcmp(mod1, mod2) != 0) { 769 return (strcmp(mod1, mod2)); 770 } 771 if (inst1 < inst2) { 772 return (-1); 773 } 774 if (inst1 > inst2) { 775 return (1); 776 } 777 } else { 778 if (io1->is_id.id < io2->is_id.id) { 779 return (-1); 780 } 781 if (io1->is_id.id > io2->is_id.id) { 782 return (1); 783 } 784 } 785 786 return (strcmp(name1, name2)); 787 } 788 789 /* update the target reads and writes */ 790 static void 791 update_target(struct iodev_snapshot *tgt, struct iodev_snapshot *path) 792 { 793 tgt->is_stats.reads += path->is_stats.reads; 794 tgt->is_stats.writes += path->is_stats.writes; 795 tgt->is_stats.nread += path->is_stats.nread; 796 tgt->is_stats.nwritten += path->is_stats.nwritten; 797 tgt->is_stats.wcnt += path->is_stats.wcnt; 798 tgt->is_stats.rcnt += path->is_stats.rcnt; 799 800 /* 801 * Stash the t_delta in the crtime for use in show_disk 802 * NOTE: this can't be done in show_disk because the 803 * itl entry is removed for the old format 804 */ 805 tgt->is_crtime += hrtime_delta(path->is_crtime, path->is_snaptime); 806 tgt->is_snaptime += path->is_snaptime; 807 tgt->is_nr_children += 1; 808 } 809 810 /* 811 * Create a new synthetic device entry of the specified type. The supported 812 * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI. 813 */ 814 static struct iodev_snapshot * 815 make_extended_device(int type, struct iodev_snapshot *old) 816 { 817 struct iodev_snapshot *tptr = NULL; 818 char *ptr; 819 int lun, tgt, initiator; 820 char lun_name[KSTAT_STRLEN]; 821 char tgt_name[KSTAT_STRLEN]; 822 char initiator_name[KSTAT_STRLEN]; 823 824 if (old == NULL) 825 return (NULL); 826 if (get_lti(old->is_name, 827 lun_name, &lun, tgt_name, &tgt, initiator_name, &initiator) != 1) { 828 return (NULL); 829 } 830 tptr = safe_alloc(sizeof (*old)); 831 bzero(tptr, sizeof (*old)); 832 if (old->is_pretty != NULL) { 833 tptr->is_pretty = safe_alloc(strlen(old->is_pretty) + 1); 834 (void) strcpy(tptr->is_pretty, old->is_pretty); 835 } 836 bcopy(&old->is_parent_id, &tptr->is_parent_id, 837 sizeof (old->is_parent_id)); 838 839 tptr->is_type = type; 840 841 if (type == IODEV_IOPATH_LT) { 842 /* make new synthetic entry that is the LT */ 843 /* set the id to the target id */ 844 tptr->is_id.id = tgt; 845 (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid), 846 "%s%d", tgt_name, tgt); 847 (void) snprintf(tptr->is_name, sizeof (tptr->is_name), 848 "%s%d.%s%d", lun_name, lun, tgt_name, tgt); 849 850 if (old->is_pretty) { 851 ptr = strrchr(tptr->is_pretty, '.'); 852 if (ptr) 853 *ptr = '\0'; 854 } 855 } else if (type == IODEV_IOPATH_LI) { 856 /* make new synthetic entry that is the LI */ 857 /* set the id to the initiator number */ 858 tptr->is_id.id = initiator; 859 (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid), 860 "%s%d", initiator_name, initiator); 861 (void) snprintf(tptr->is_name, sizeof (tptr->is_name), 862 "%s%d.%s%d", lun_name, lun, initiator_name, initiator); 863 864 if (old->is_pretty) { 865 ptr = strchr(tptr->is_pretty, '.'); 866 if (ptr) 867 (void) snprintf(ptr + 1, 868 strlen(tptr->is_pretty) + 1, 869 "%s%d", initiator_name, initiator); 870 } 871 } 872 return (tptr); 873 } 874 875 /* 876 * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat 877 * is found - traverse the children looking for the same initiator and sum 878 * them up. Add an LI entry and delete all of the LTI entries with the same 879 * initiator. 880 */ 881 static int 882 create_li_delete_lti(struct snapshot *ss, struct iodev_snapshot *list) 883 { 884 struct iodev_snapshot *pos, *entry, *parent; 885 int lun, tgt, initiator; 886 char lun_name[KSTAT_STRLEN]; 887 char tgt_name[KSTAT_STRLEN]; 888 char initiator_name[KSTAT_STRLEN]; 889 int err; 890 891 for (entry = list; entry; entry = entry->is_next) { 892 if ((err = create_li_delete_lti(ss, entry->is_children)) != 0) 893 return (err); 894 895 if (entry->is_type == IODEV_IOPATH_LTI) { 896 parent = find_parent(ss, entry); 897 if (get_lti(entry->is_name, lun_name, &lun, 898 tgt_name, &tgt, initiator_name, &initiator) != 1) { 899 return (1); 900 } 901 902 pos = (parent == NULL) ? NULL : parent->is_children; 903 for (; pos; pos = pos->is_next) { 904 if (pos->is_id.id != -1 && 905 pos->is_id.id == initiator && 906 pos->is_type == IODEV_IOPATH_LI) { 907 /* found the same initiator */ 908 update_target(pos, entry); 909 list_del(&parent->is_children, entry); 910 free_iodev(entry); 911 parent->is_nr_children--; 912 entry = pos; 913 break; 914 } 915 } 916 917 if (!pos) { 918 /* make the first LI entry */ 919 pos = make_extended_device( 920 IODEV_IOPATH_LI, entry); 921 update_target(pos, entry); 922 923 if (parent) { 924 insert_before(&parent->is_children, 925 entry, pos); 926 list_del(&parent->is_children, entry); 927 free_iodev(entry); 928 } else { 929 insert_before(&ss->s_iodevs, entry, 930 pos); 931 list_del(&ss->s_iodevs, entry); 932 free_iodev(entry); 933 } 934 entry = pos; 935 } 936 } 937 } 938 return (0); 939 } 940 941 /* 942 * We have the LTI kstat, now add an entry for the LT that sums up all of 943 * the LTI's with the same target(t). 944 */ 945 static int 946 create_lt(struct snapshot *ss, struct iodev_snapshot *list) 947 { 948 struct iodev_snapshot *entry, *parent, *pos; 949 int lun, tgt, initiator; 950 char lun_name[KSTAT_STRLEN]; 951 char tgt_name[KSTAT_STRLEN]; 952 char initiator_name[KSTAT_STRLEN]; 953 int err; 954 955 for (entry = list; entry; entry = entry->is_next) { 956 if ((err = create_lt(ss, entry->is_children)) != 0) 957 return (err); 958 959 if (entry->is_type == IODEV_IOPATH_LTI) { 960 parent = find_parent(ss, entry); 961 if (get_lti(entry->is_name, lun_name, &lun, 962 tgt_name, &tgt, initiator_name, &initiator) != 1) { 963 return (1); 964 } 965 966 pos = (parent == NULL) ? NULL : parent->is_children; 967 for (; pos; pos = pos->is_next) { 968 if (pos->is_id.id != -1 && 969 pos->is_id.id == tgt && 970 pos->is_type == IODEV_IOPATH_LT) { 971 /* found the same target */ 972 update_target(pos, entry); 973 break; 974 } 975 } 976 977 if (!pos) { 978 pos = make_extended_device( 979 IODEV_IOPATH_LT, entry); 980 update_target(pos, entry); 981 982 if (parent) { 983 insert_before(&parent->is_children, 984 entry, pos); 985 parent->is_nr_children++; 986 } else { 987 insert_before(&ss->s_iodevs, 988 entry, pos); 989 } 990 } 991 } 992 } 993 return (0); 994 } 995 996 /* Find the longest is_name field to aid formatting of output */ 997 static int 998 iodevs_is_name_maxlen(struct iodev_snapshot *list) 999 { 1000 struct iodev_snapshot *entry; 1001 int max = 0, cmax, len; 1002 1003 for (entry = list; entry; entry = entry->is_next) { 1004 cmax = iodevs_is_name_maxlen(entry->is_children); 1005 max = (cmax > max) ? cmax : max; 1006 len = strlen(entry->is_name); 1007 max = (len > max) ? len : max; 1008 } 1009 return (max); 1010 } 1011 1012 int 1013 acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df) 1014 { 1015 kstat_t *ksp; 1016 struct iodev_snapshot *pos; 1017 struct iodev_snapshot *list = NULL; 1018 int err = 0; 1019 1020 ss->s_nr_iodevs = 0; 1021 ss->s_iodevs_is_name_maxlen = 0; 1022 1023 /* 1024 * Call cleanup_iodevs_snapshot() so that a cache miss in 1025 * lookup_ks_name() will result in a fresh snapshot. 1026 */ 1027 cleanup_iodevs_snapshot(); 1028 1029 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 1030 enum iodev_type type; 1031 1032 if (ksp->ks_type != KSTAT_TYPE_IO) 1033 continue; 1034 1035 /* e.g. "usb_byte_count" is not handled */ 1036 if ((type = get_iodev_type(ksp)) == IODEV_UNKNOWN) 1037 continue; 1038 1039 if (df && !(type & df->if_allowed_types)) 1040 continue; 1041 1042 if ((pos = malloc(sizeof (struct iodev_snapshot))) == NULL) { 1043 err = errno; 1044 goto out; 1045 } 1046 1047 (void) memset(pos, 0, sizeof (struct iodev_snapshot)); 1048 1049 pos->is_type = type; 1050 pos->is_crtime = ksp->ks_crtime; 1051 pos->is_snaptime = ksp->ks_snaptime; 1052 pos->is_id.id = IODEV_NO_ID; 1053 pos->is_parent_id.id = IODEV_NO_ID; 1054 pos->is_ksp = ksp; 1055 pos->is_instance = ksp->ks_instance; 1056 1057 (void) strlcpy(pos->is_module, ksp->ks_module, KSTAT_STRLEN); 1058 (void) strlcpy(pos->is_name, ksp->ks_name, KSTAT_STRLEN); 1059 get_pretty_name(ss->s_types, pos, kc); 1060 1061 /* 1062 * We must insert in sort order so e.g. vmstat -l 1063 * chooses in order. 1064 */ 1065 insert_into(&list, pos); 1066 } 1067 1068 choose_iodevs(ss, list, df); 1069 1070 /* before acquire_stats for collate_controller()'s benefit */ 1071 if (ss->s_types & SNAP_IODEV_ERRORS) { 1072 if ((err = acquire_iodev_errors(ss, kc)) != 0) 1073 goto out; 1074 } 1075 1076 if ((err = acquire_iodev_stats(ss->s_iodevs, kc)) != 0) 1077 goto out; 1078 1079 if (ss->s_types & SNAP_IOPATHS_LTI) { 1080 /* 1081 * -Y: kstats are LTI, need to create a synthetic LT 1082 * for -Y output. 1083 */ 1084 if ((err = create_lt(ss, ss->s_iodevs)) != 0) { 1085 return (err); 1086 } 1087 } 1088 if (ss->s_types & SNAP_IOPATHS_LI) { 1089 /* 1090 * -X: kstats are LTI, need to create a synthetic LI and 1091 * delete the LTI for -X output 1092 */ 1093 if ((err = create_li_delete_lti(ss, ss->s_iodevs)) != 0) { 1094 return (err); 1095 } 1096 } 1097 1098 /* determine width of longest is_name */ 1099 ss->s_iodevs_is_name_maxlen = iodevs_is_name_maxlen(ss->s_iodevs); 1100 1101 err = 0; 1102 out: 1103 return (err); 1104 } 1105 1106 void 1107 free_iodev(struct iodev_snapshot *iodev) 1108 { 1109 while (iodev->is_children) { 1110 struct iodev_snapshot *tmp = iodev->is_children; 1111 iodev->is_children = iodev->is_children->is_next; 1112 free_iodev(tmp); 1113 } 1114 1115 if (iodev->avl_list) { 1116 avl_remove(iodev->avl_list, iodev); 1117 if (avl_numnodes(iodev->avl_list) == 0) { 1118 avl_destroy(iodev->avl_list); 1119 free(iodev->avl_list); 1120 } 1121 } 1122 1123 free(iodev->is_errors.ks_data); 1124 free(iodev->is_pretty); 1125 free(iodev->is_dname); 1126 free(iodev->is_devid); 1127 free(iodev); 1128 } 1129