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