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 if (!(ss->s_types && SNAP_IODEV_ERRORS)) 540 return (0); 541 542 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 543 char kstat_name[KSTAT_STRLEN]; 544 char *dname = kstat_name; 545 char *ename = ksp->ks_name; 546 struct iodev_snapshot *iodev; 547 548 if (ksp->ks_type != KSTAT_TYPE_NAMED) 549 continue; 550 if (strncmp(ksp->ks_class, "device_error", 12) != 0 && 551 strncmp(ksp->ks_class, "iopath_error", 12) != 0) 552 continue; 553 554 /* 555 * Some drivers may not follow the naming convention 556 * for error kstats (i.e., drivername,err) so 557 * be sure we don't walk off the end. 558 */ 559 while (*ename && *ename != ',') { 560 *dname = *ename; 561 dname++; 562 ename++; 563 } 564 *dname = '\0'; 565 566 iodev = find_iodev_by_name(ss->s_iodevs, kstat_name); 567 568 if (iodev == NULL) 569 continue; 570 571 if (kstat_read(kc, ksp, NULL) == -1) 572 return (errno); 573 if (kstat_copy(ksp, &iodev->is_errors) == -1) 574 return (errno); 575 } 576 577 return (0); 578 } 579 580 static void 581 get_ids(struct iodev_snapshot *iodev, const char *pretty) 582 { 583 int ctr, disk, slice, ret; 584 char *target; 585 const char *p1; 586 const char *p2; 587 588 if (pretty == NULL) 589 return; 590 591 if (sscanf(pretty, "c%d", &ctr) != 1) 592 return; 593 594 p1 = pretty; 595 while (*p1 && *p1 != 't') 596 ++p1; 597 598 if (!*p1) 599 return; 600 ++p1; 601 602 p2 = p1; 603 while (*p2 && *p2 != 'd') 604 ++p2; 605 606 if (!*p2 || p2 == p1) 607 return; 608 609 target = safe_alloc(1 + p2 - p1); 610 (void) strlcpy(target, p1, 1 + p2 - p1); 611 612 ret = sscanf(p2, "d%d%*[sp]%d", &disk, &slice); 613 614 if (ret == 2 && iodev->is_type == IODEV_PARTITION) { 615 iodev->is_id.id = slice; 616 iodev->is_parent_id.id = disk; 617 (void) strlcpy(iodev->is_parent_id.tid, target, KSTAT_STRLEN); 618 } else if (ret == 1) { 619 if (iodev->is_type == IODEV_DISK) { 620 iodev->is_id.id = disk; 621 (void) strlcpy(iodev->is_id.tid, target, KSTAT_STRLEN); 622 iodev->is_parent_id.id = ctr; 623 } else if (iodev->is_type == IODEV_IOPATH_LTI) { 624 iodev->is_parent_id.id = disk; 625 (void) strlcpy(iodev->is_parent_id.tid, 626 target, KSTAT_STRLEN); 627 } 628 } 629 630 free(target); 631 } 632 633 static void 634 get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev, 635 kstat_ctl_t *kc) 636 { 637 disk_list_t *dl; 638 char *pretty = NULL; 639 640 if (iodev->is_type == IODEV_NFS) { 641 if (!(types & SNAP_IODEV_PRETTY)) 642 return; 643 644 iodev->is_pretty = lookup_nfs_name(iodev->is_name, kc); 645 return; 646 } 647 648 /* lookup/translate the kstat name */ 649 dl = lookup_ks_name(iodev->is_name, (types & SNAP_IODEV_DEVID) ? 1 : 0); 650 if (dl == NULL) 651 return; 652 653 if (dl->dsk) 654 pretty = safe_strdup(dl->dsk); 655 656 if (types & SNAP_IODEV_PRETTY) { 657 if (dl->dname) 658 iodev->is_dname = safe_strdup(dl->dname); 659 } 660 661 if (dl->devidstr) 662 iodev->is_devid = safe_strdup(dl->devidstr); 663 664 get_ids(iodev, pretty); 665 666 /* 667 * we fill in pretty name wether it is asked for or not because 668 * it could be used in a filter by match_iodevs. 669 */ 670 iodev->is_pretty = pretty; 671 } 672 673 static enum iodev_type 674 get_iodev_type(kstat_t *ksp) 675 { 676 if (strcmp(ksp->ks_class, "disk") == 0) 677 return (IODEV_DISK); 678 if (strcmp(ksp->ks_class, "partition") == 0) 679 return (IODEV_PARTITION); 680 if (strcmp(ksp->ks_class, "nfs") == 0) 681 return (IODEV_NFS); 682 if (strcmp(ksp->ks_class, "iopath") == 0) 683 return (IODEV_IOPATH_LTI); 684 if (strcmp(ksp->ks_class, "tape") == 0) 685 return (IODEV_TAPE); 686 return (IODEV_UNKNOWN); 687 } 688 689 /* get the lun/target/initiator from the name, return 1 on success */ 690 static int 691 get_lti(char *s, 692 char *lname, int *l, char *tname, int *t, char *iname, int *i) 693 { 694 int num = 0; 695 696 num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z_]%d", lname, l, 697 tname, t, iname, i); 698 return ((num == 6) ? 1 : 0); 699 } 700 701 702 /* get the lun, target, and initiator name and instance */ 703 static void 704 get_path_info(struct iodev_snapshot *io, char *mod, size_t modlen, int *type, 705 int *inst, char *name, size_t size) 706 { 707 708 /* 709 * If it is iopath or ssd then pad the name with i/t/l so we can sort 710 * by alpha order and set type for IOPATH to DISK since we want to 711 * have it grouped with its ssd parent. The lun can be 5 digits, 712 * the target can be 4 digits, and the initiator can be 3 digits and 713 * the padding is done appropriately for string comparisons. 714 */ 715 if (disk_or_partition_or_iopath(io->is_type)) { 716 int i1, t1, l1; 717 char tname[KSTAT_STRLEN], iname[KSTAT_STRLEN]; 718 char *ptr, lname[KSTAT_STRLEN]; 719 720 i1 = t1 = l1 = 0; 721 (void) get_lti(io->is_name, lname, &l1, tname, &t1, iname, &i1); 722 *type = io->is_type; 723 if (io->is_type == IODEV_DISK) { 724 (void) snprintf(name, size, "%s%05d", lname, l1); 725 } else if (io->is_type == IODEV_PARTITION) { 726 ptr = strchr(io->is_name, ','); 727 (void) snprintf(name, size, "%s%05d%s", lname, l1, ptr); 728 } else { 729 (void) snprintf(name, size, "%s%05d.%s%04d.%s%03d", 730 lname, l1, tname, t1, iname, i1); 731 /* set to disk so we sort with disks */ 732 *type = IODEV_DISK; 733 } 734 (void) strlcpy(mod, lname, modlen); 735 *inst = l1; 736 } else { 737 (void) strlcpy(mod, io->is_module, modlen); 738 (void) strlcpy(name, io->is_name, size); 739 *type = io->is_type; 740 *inst = io->is_instance; 741 } 742 } 743 744 int 745 iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2) 746 { 747 int type1, type2; 748 int inst1, inst2; 749 char name1[KSTAT_STRLEN], name2[KSTAT_STRLEN]; 750 char mod1[KSTAT_STRLEN], mod2[KSTAT_STRLEN]; 751 752 get_path_info(io1, mod1, sizeof (mod1), &type1, &inst1, name1, 753 sizeof (name1)); 754 get_path_info(io2, mod2, sizeof (mod2), &type2, &inst2, name2, 755 sizeof (name2)); 756 if ((!disk_or_partition(type1)) || 757 (!disk_or_partition(type2))) { 758 /* neutral sort order between disk and part */ 759 if (type1 < type2) { 760 return (-1); 761 } 762 if (type1 > type2) { 763 return (1); 764 } 765 } 766 767 /* controller doesn't have ksp */ 768 if (io1->is_ksp && io2->is_ksp) { 769 if (strcmp(mod1, mod2) != 0) { 770 return (strcmp(mod1, mod2)); 771 } 772 if (inst1 < inst2) { 773 return (-1); 774 } 775 if (inst1 > inst2) { 776 return (1); 777 } 778 } else { 779 if (io1->is_id.id < io2->is_id.id) { 780 return (-1); 781 } 782 if (io1->is_id.id > io2->is_id.id) { 783 return (1); 784 } 785 } 786 787 return (strcmp(name1, name2)); 788 } 789 790 /* update the target reads and writes */ 791 static void 792 update_target(struct iodev_snapshot *tgt, struct iodev_snapshot *path) 793 { 794 tgt->is_stats.reads += path->is_stats.reads; 795 tgt->is_stats.writes += path->is_stats.writes; 796 tgt->is_stats.nread += path->is_stats.nread; 797 tgt->is_stats.nwritten += path->is_stats.nwritten; 798 tgt->is_stats.wcnt += path->is_stats.wcnt; 799 tgt->is_stats.rcnt += path->is_stats.rcnt; 800 801 /* 802 * Stash the t_delta in the crtime for use in show_disk 803 * NOTE: this can't be done in show_disk because the 804 * itl entry is removed for the old format 805 */ 806 tgt->is_crtime += hrtime_delta(path->is_crtime, path->is_snaptime); 807 tgt->is_snaptime += path->is_snaptime; 808 tgt->is_nr_children += 1; 809 } 810 811 /* 812 * Create a new synthetic device entry of the specified type. The supported 813 * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI. 814 */ 815 static struct iodev_snapshot * 816 make_extended_device(int type, struct iodev_snapshot *old) 817 { 818 struct iodev_snapshot *tptr = NULL; 819 char *ptr; 820 int lun, tgt, initiator; 821 char lun_name[KSTAT_STRLEN]; 822 char tgt_name[KSTAT_STRLEN]; 823 char initiator_name[KSTAT_STRLEN]; 824 825 if (old == NULL) 826 return (NULL); 827 if (get_lti(old->is_name, 828 lun_name, &lun, tgt_name, &tgt, initiator_name, &initiator) != 1) { 829 return (NULL); 830 } 831 tptr = safe_alloc(sizeof (*old)); 832 bzero(tptr, sizeof (*old)); 833 if (old->is_pretty != NULL) { 834 tptr->is_pretty = safe_alloc(strlen(old->is_pretty) + 1); 835 (void) strcpy(tptr->is_pretty, old->is_pretty); 836 } 837 bcopy(&old->is_parent_id, &tptr->is_parent_id, 838 sizeof (old->is_parent_id)); 839 840 tptr->is_type = type; 841 842 if (type == IODEV_IOPATH_LT) { 843 /* make new synthetic entry that is the LT */ 844 /* set the id to the target id */ 845 tptr->is_id.id = tgt; 846 (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid), 847 "%s%d", tgt_name, tgt); 848 (void) snprintf(tptr->is_name, sizeof (tptr->is_name), 849 "%s%d.%s%d", lun_name, lun, tgt_name, tgt); 850 851 if (old->is_pretty) { 852 ptr = strrchr(tptr->is_pretty, '.'); 853 if (ptr) 854 *ptr = '\0'; 855 } 856 } else if (type == IODEV_IOPATH_LI) { 857 /* make new synthetic entry that is the LI */ 858 /* set the id to the initiator number */ 859 tptr->is_id.id = initiator; 860 (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid), 861 "%s%d", initiator_name, initiator); 862 (void) snprintf(tptr->is_name, sizeof (tptr->is_name), 863 "%s%d.%s%d", lun_name, lun, initiator_name, initiator); 864 865 if (old->is_pretty) { 866 ptr = strchr(tptr->is_pretty, '.'); 867 if (ptr) 868 (void) snprintf(ptr + 1, 869 strlen(tptr->is_pretty) + 1, 870 "%s%d", initiator_name, initiator); 871 } 872 } 873 return (tptr); 874 } 875 876 /* 877 * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat 878 * is found - traverse the children looking for the same initiator and sum 879 * them up. Add an LI entry and delete all of the LTI entries with the same 880 * initiator. 881 */ 882 static int 883 create_li_delete_lti(struct snapshot *ss, struct iodev_snapshot *list) 884 { 885 struct iodev_snapshot *pos, *entry, *parent; 886 int lun, tgt, initiator; 887 char lun_name[KSTAT_STRLEN]; 888 char tgt_name[KSTAT_STRLEN]; 889 char initiator_name[KSTAT_STRLEN]; 890 int err; 891 892 for (entry = list; entry; entry = entry->is_next) { 893 if ((err = create_li_delete_lti(ss, entry->is_children)) != 0) 894 return (err); 895 896 if (entry->is_type == IODEV_IOPATH_LTI) { 897 parent = find_parent(ss, entry); 898 if (get_lti(entry->is_name, lun_name, &lun, 899 tgt_name, &tgt, initiator_name, &initiator) != 1) { 900 return (1); 901 } 902 903 pos = (parent == NULL) ? NULL : parent->is_children; 904 for (; pos; pos = pos->is_next) { 905 if (pos->is_id.id != -1 && 906 pos->is_id.id == initiator && 907 pos->is_type == IODEV_IOPATH_LI) { 908 /* found the same initiator */ 909 update_target(pos, entry); 910 list_del(&parent->is_children, entry); 911 free_iodev(entry); 912 parent->is_nr_children--; 913 entry = pos; 914 break; 915 } 916 } 917 918 if (!pos) { 919 /* make the first LI entry */ 920 pos = make_extended_device( 921 IODEV_IOPATH_LI, entry); 922 update_target(pos, entry); 923 924 if (parent) { 925 insert_before(&parent->is_children, 926 entry, pos); 927 list_del(&parent->is_children, entry); 928 free_iodev(entry); 929 } else { 930 insert_before(&ss->s_iodevs, entry, 931 pos); 932 list_del(&ss->s_iodevs, entry); 933 free_iodev(entry); 934 } 935 entry = pos; 936 } 937 } 938 } 939 return (0); 940 } 941 942 /* 943 * We have the LTI kstat, now add an entry for the LT that sums up all of 944 * the LTI's with the same target(t). 945 */ 946 static int 947 create_lt(struct snapshot *ss, struct iodev_snapshot *list) 948 { 949 struct iodev_snapshot *entry, *parent, *pos; 950 int lun, tgt, initiator; 951 char lun_name[KSTAT_STRLEN]; 952 char tgt_name[KSTAT_STRLEN]; 953 char initiator_name[KSTAT_STRLEN]; 954 int err; 955 956 for (entry = list; entry; entry = entry->is_next) { 957 if ((err = create_lt(ss, entry->is_children)) != 0) 958 return (err); 959 960 if (entry->is_type == IODEV_IOPATH_LTI) { 961 parent = find_parent(ss, entry); 962 if (get_lti(entry->is_name, lun_name, &lun, 963 tgt_name, &tgt, initiator_name, &initiator) != 1) { 964 return (1); 965 } 966 967 pos = (parent == NULL) ? NULL : parent->is_children; 968 for (; pos; pos = pos->is_next) { 969 if (pos->is_id.id != -1 && 970 pos->is_id.id == tgt && 971 pos->is_type == IODEV_IOPATH_LT) { 972 /* found the same target */ 973 update_target(pos, entry); 974 break; 975 } 976 } 977 978 if (!pos) { 979 pos = make_extended_device( 980 IODEV_IOPATH_LT, entry); 981 update_target(pos, entry); 982 983 if (parent) { 984 insert_before(&parent->is_children, 985 entry, pos); 986 parent->is_nr_children++; 987 } else { 988 insert_before(&ss->s_iodevs, 989 entry, pos); 990 } 991 } 992 } 993 } 994 return (0); 995 } 996 997 /* Find the longest is_name field to aid formatting of output */ 998 static int 999 iodevs_is_name_maxlen(struct iodev_snapshot *list) 1000 { 1001 struct iodev_snapshot *entry; 1002 int max = 0, cmax, len; 1003 1004 for (entry = list; entry; entry = entry->is_next) { 1005 cmax = iodevs_is_name_maxlen(entry->is_children); 1006 max = (cmax > max) ? cmax : max; 1007 len = strlen(entry->is_name); 1008 max = (len > max) ? len : max; 1009 } 1010 return (max); 1011 } 1012 1013 int 1014 acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df) 1015 { 1016 kstat_t *ksp; 1017 struct iodev_snapshot *pos; 1018 struct iodev_snapshot *list = NULL; 1019 int err = 0; 1020 1021 ss->s_nr_iodevs = 0; 1022 ss->s_iodevs_is_name_maxlen = 0; 1023 1024 /* 1025 * Call cleanup_iodevs_snapshot() so that a cache miss in 1026 * lookup_ks_name() will result in a fresh snapshot. 1027 */ 1028 cleanup_iodevs_snapshot(); 1029 1030 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 1031 enum iodev_type type; 1032 1033 if (ksp->ks_type != KSTAT_TYPE_IO) 1034 continue; 1035 1036 /* e.g. "usb_byte_count" is not handled */ 1037 if ((type = get_iodev_type(ksp)) == IODEV_UNKNOWN) 1038 continue; 1039 1040 if (df && !(type & df->if_allowed_types)) 1041 continue; 1042 1043 if ((pos = malloc(sizeof (struct iodev_snapshot))) == NULL) { 1044 err = errno; 1045 goto out; 1046 } 1047 1048 (void) memset(pos, 0, sizeof (struct iodev_snapshot)); 1049 1050 pos->is_type = type; 1051 pos->is_crtime = ksp->ks_crtime; 1052 pos->is_snaptime = ksp->ks_snaptime; 1053 pos->is_id.id = IODEV_NO_ID; 1054 pos->is_parent_id.id = IODEV_NO_ID; 1055 pos->is_ksp = ksp; 1056 pos->is_instance = ksp->ks_instance; 1057 1058 (void) strlcpy(pos->is_module, ksp->ks_module, KSTAT_STRLEN); 1059 (void) strlcpy(pos->is_name, ksp->ks_name, KSTAT_STRLEN); 1060 get_pretty_name(ss->s_types, pos, kc); 1061 1062 /* 1063 * We must insert in sort order so e.g. vmstat -l 1064 * chooses in order. 1065 */ 1066 insert_into(&list, pos); 1067 } 1068 1069 choose_iodevs(ss, list, df); 1070 1071 /* before acquire_stats for collate_controller()'s benefit */ 1072 if (ss->s_types & SNAP_IODEV_ERRORS) { 1073 if ((err = acquire_iodev_errors(ss, kc)) != 0) 1074 goto out; 1075 } 1076 1077 if ((err = acquire_iodev_stats(ss->s_iodevs, kc)) != 0) 1078 goto out; 1079 1080 if (ss->s_types & SNAP_IOPATHS_LTI) { 1081 /* 1082 * -Y: kstats are LTI, need to create a synthetic LT 1083 * for -Y output. 1084 */ 1085 if ((err = create_lt(ss, ss->s_iodevs)) != 0) { 1086 return (err); 1087 } 1088 } 1089 if (ss->s_types & SNAP_IOPATHS_LI) { 1090 /* 1091 * -X: kstats are LTI, need to create a synthetic LI and 1092 * delete the LTI for -X output 1093 */ 1094 if ((err = create_li_delete_lti(ss, ss->s_iodevs)) != 0) { 1095 return (err); 1096 } 1097 } 1098 1099 /* determine width of longest is_name */ 1100 ss->s_iodevs_is_name_maxlen = iodevs_is_name_maxlen(ss->s_iodevs); 1101 1102 err = 0; 1103 out: 1104 return (err); 1105 } 1106 1107 void 1108 free_iodev(struct iodev_snapshot *iodev) 1109 { 1110 while (iodev->is_children) { 1111 struct iodev_snapshot *tmp = iodev->is_children; 1112 iodev->is_children = iodev->is_children->is_next; 1113 free_iodev(tmp); 1114 } 1115 1116 if (iodev->avl_list) { 1117 avl_remove(iodev->avl_list, iodev); 1118 if (avl_numnodes(iodev->avl_list) == 0) { 1119 avl_destroy(iodev->avl_list); 1120 free(iodev->avl_list); 1121 } 1122 } 1123 1124 free(iodev->is_errors.ks_data); 1125 free(iodev->is_pretty); 1126 free(iodev->is_dname); 1127 free(iodev->is_devid); 1128 free(iodev); 1129 } 1130