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