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