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 == NULL) 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 == NULL) || (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 && 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, 687 char *lname, int *l, char *tname, int *t, char *iname, int *i) 688 { 689 int num = 0; 690 691 num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z_]%d", lname, l, 692 tname, t, iname, i); 693 return ((num == 6) ? 1 : 0); 694 } 695 696 697 /* get the lun, target, and initiator name and instance */ 698 static void 699 get_path_info(struct iodev_snapshot *io, char *mod, size_t modlen, int *type, 700 int *inst, char *name, size_t size) 701 { 702 703 /* 704 * If it is iopath or ssd then pad the name with i/t/l so we can sort 705 * by alpha order and set type for IOPATH to DISK since we want to 706 * have it grouped with its ssd parent. The lun can be 5 digits, 707 * the target can be 4 digits, and the initiator can be 3 digits and 708 * the padding is done appropriately for string comparisons. 709 */ 710 if (disk_or_partition_or_iopath(io->is_type)) { 711 int i1, t1, l1; 712 char tname[KSTAT_STRLEN], iname[KSTAT_STRLEN]; 713 char *ptr, lname[KSTAT_STRLEN]; 714 715 i1 = t1 = l1 = 0; 716 (void) get_lti(io->is_name, lname, &l1, tname, &t1, iname, &i1); 717 *type = io->is_type; 718 if (io->is_type == IODEV_DISK) { 719 (void) snprintf(name, size, "%s%05d", lname, l1); 720 } else if (io->is_type == IODEV_PARTITION) { 721 ptr = strchr(io->is_name, ','); 722 (void) snprintf(name, size, "%s%05d%s", lname, l1, ptr); 723 } else { 724 (void) snprintf(name, size, "%s%05d.%s%04d.%s%03d", 725 lname, l1, tname, t1, iname, i1); 726 /* set to disk so we sort with disks */ 727 *type = IODEV_DISK; 728 } 729 (void) strlcpy(mod, lname, modlen); 730 *inst = l1; 731 } else { 732 (void) strlcpy(mod, io->is_module, modlen); 733 (void) strlcpy(name, io->is_name, size); 734 *type = io->is_type; 735 *inst = io->is_instance; 736 } 737 } 738 739 int 740 iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2) 741 { 742 int type1, type2; 743 int inst1, inst2; 744 char name1[KSTAT_STRLEN], name2[KSTAT_STRLEN]; 745 char mod1[KSTAT_STRLEN], mod2[KSTAT_STRLEN]; 746 747 get_path_info(io1, mod1, sizeof (mod1), &type1, &inst1, name1, 748 sizeof (name1)); 749 get_path_info(io2, mod2, sizeof (mod2), &type2, &inst2, name2, 750 sizeof (name2)); 751 if ((!disk_or_partition(type1)) || 752 (!disk_or_partition(type2))) { 753 /* neutral sort order between disk and part */ 754 if (type1 < type2) { 755 return (-1); 756 } 757 if (type1 > type2) { 758 return (1); 759 } 760 } 761 762 /* controller doesn't have ksp */ 763 if (io1->is_ksp && io2->is_ksp) { 764 if (strcmp(mod1, mod2) != 0) { 765 return (strcmp(mod1, mod2)); 766 } 767 if (inst1 < inst2) { 768 return (-1); 769 } 770 if (inst1 > inst2) { 771 return (1); 772 } 773 } else { 774 if (io1->is_id.id < io2->is_id.id) { 775 return (-1); 776 } 777 if (io1->is_id.id > io2->is_id.id) { 778 return (1); 779 } 780 } 781 782 return (strcmp(name1, name2)); 783 } 784 785 /* update the target reads and writes */ 786 static void 787 update_target(struct iodev_snapshot *tgt, struct iodev_snapshot *path) 788 { 789 tgt->is_stats.reads += path->is_stats.reads; 790 tgt->is_stats.writes += path->is_stats.writes; 791 tgt->is_stats.nread += path->is_stats.nread; 792 tgt->is_stats.nwritten += path->is_stats.nwritten; 793 tgt->is_stats.wcnt += path->is_stats.wcnt; 794 tgt->is_stats.rcnt += path->is_stats.rcnt; 795 796 /* 797 * Stash the t_delta in the crtime for use in show_disk 798 * NOTE: this can't be done in show_disk because the 799 * itl entry is removed for the old format 800 */ 801 tgt->is_crtime += hrtime_delta(path->is_crtime, path->is_snaptime); 802 tgt->is_snaptime += path->is_snaptime; 803 tgt->is_nr_children += 1; 804 } 805 806 /* 807 * Create a new synthetic device entry of the specified type. The supported 808 * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI. 809 */ 810 static struct iodev_snapshot * 811 make_extended_device(int type, struct iodev_snapshot *old) 812 { 813 struct iodev_snapshot *tptr = NULL; 814 char *ptr; 815 int lun, tgt, initiator; 816 char lun_name[KSTAT_STRLEN]; 817 char tgt_name[KSTAT_STRLEN]; 818 char initiator_name[KSTAT_STRLEN]; 819 820 if (old == NULL) 821 return (NULL); 822 if (get_lti(old->is_name, 823 lun_name, &lun, tgt_name, &tgt, initiator_name, &initiator) != 1) { 824 return (NULL); 825 } 826 tptr = safe_alloc(sizeof (*old)); 827 bzero(tptr, sizeof (*old)); 828 if (old->is_pretty != NULL) { 829 tptr->is_pretty = safe_alloc(strlen(old->is_pretty) + 1); 830 (void) strcpy(tptr->is_pretty, old->is_pretty); 831 } 832 bcopy(&old->is_parent_id, &tptr->is_parent_id, 833 sizeof (old->is_parent_id)); 834 835 tptr->is_type = type; 836 837 if (type == IODEV_IOPATH_LT) { 838 /* make new synthetic entry that is the LT */ 839 /* set the id to the target id */ 840 tptr->is_id.id = tgt; 841 (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid), 842 "%s%d", tgt_name, tgt); 843 (void) snprintf(tptr->is_name, sizeof (tptr->is_name), 844 "%s%d.%s%d", lun_name, lun, tgt_name, tgt); 845 846 if (old->is_pretty) { 847 ptr = strrchr(tptr->is_pretty, '.'); 848 if (ptr) 849 *ptr = '\0'; 850 } 851 } else if (type == IODEV_IOPATH_LI) { 852 /* make new synthetic entry that is the LI */ 853 /* set the id to the initiator number */ 854 tptr->is_id.id = initiator; 855 (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid), 856 "%s%d", initiator_name, initiator); 857 (void) snprintf(tptr->is_name, sizeof (tptr->is_name), 858 "%s%d.%s%d", lun_name, lun, initiator_name, initiator); 859 860 if (old->is_pretty) { 861 ptr = strchr(tptr->is_pretty, '.'); 862 if (ptr) 863 (void) snprintf(ptr + 1, 864 strlen(tptr->is_pretty) + 1, 865 "%s%d", initiator_name, initiator); 866 } 867 } 868 return (tptr); 869 } 870 871 /* 872 * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat 873 * is found - traverse the children looking for the same initiator and sum 874 * them up. Add an LI entry and delete all of the LTI entries with the same 875 * initiator. 876 */ 877 static int 878 create_li_delete_lti(struct snapshot *ss, struct iodev_snapshot *list) 879 { 880 struct iodev_snapshot *pos, *entry, *parent; 881 int lun, tgt, initiator; 882 char lun_name[KSTAT_STRLEN]; 883 char tgt_name[KSTAT_STRLEN]; 884 char initiator_name[KSTAT_STRLEN]; 885 int err; 886 887 for (entry = list; entry; entry = entry->is_next) { 888 if ((err = create_li_delete_lti(ss, entry->is_children)) != 0) 889 return (err); 890 891 if (entry->is_type == IODEV_IOPATH_LTI) { 892 parent = find_parent(ss, entry); 893 if (get_lti(entry->is_name, lun_name, &lun, 894 tgt_name, &tgt, initiator_name, &initiator) != 1) { 895 return (1); 896 } 897 898 pos = (parent == NULL) ? NULL : parent->is_children; 899 for (; pos; pos = pos->is_next) { 900 if (pos->is_id.id != -1 && 901 pos->is_id.id == initiator && 902 pos->is_type == IODEV_IOPATH_LI) { 903 /* found the same initiator */ 904 update_target(pos, entry); 905 list_del(&parent->is_children, entry); 906 free_iodev(entry); 907 parent->is_nr_children--; 908 entry = pos; 909 break; 910 } 911 } 912 913 if (!pos) { 914 /* make the first LI entry */ 915 pos = make_extended_device( 916 IODEV_IOPATH_LI, entry); 917 update_target(pos, entry); 918 919 if (parent) { 920 insert_before(&parent->is_children, 921 entry, pos); 922 list_del(&parent->is_children, entry); 923 free_iodev(entry); 924 } else { 925 insert_before(&ss->s_iodevs, entry, 926 pos); 927 list_del(&ss->s_iodevs, entry); 928 free_iodev(entry); 929 } 930 entry = pos; 931 } 932 } 933 } 934 return (0); 935 } 936 937 /* 938 * We have the LTI kstat, now add an entry for the LT that sums up all of 939 * the LTI's with the same target(t). 940 */ 941 static int 942 create_lt(struct snapshot *ss, struct iodev_snapshot *list) 943 { 944 struct iodev_snapshot *entry, *parent, *pos; 945 int lun, tgt, initiator; 946 char lun_name[KSTAT_STRLEN]; 947 char tgt_name[KSTAT_STRLEN]; 948 char initiator_name[KSTAT_STRLEN]; 949 int err; 950 951 for (entry = list; entry; entry = entry->is_next) { 952 if ((err = create_lt(ss, entry->is_children)) != 0) 953 return (err); 954 955 if (entry->is_type == IODEV_IOPATH_LTI) { 956 parent = find_parent(ss, entry); 957 if (get_lti(entry->is_name, lun_name, &lun, 958 tgt_name, &tgt, initiator_name, &initiator) != 1) { 959 return (1); 960 } 961 962 pos = (parent == NULL) ? NULL : parent->is_children; 963 for (; pos; pos = pos->is_next) { 964 if (pos->is_id.id != -1 && 965 pos->is_id.id == tgt && 966 pos->is_type == IODEV_IOPATH_LT) { 967 /* found the same target */ 968 update_target(pos, entry); 969 break; 970 } 971 } 972 973 if (!pos) { 974 pos = make_extended_device( 975 IODEV_IOPATH_LT, entry); 976 update_target(pos, entry); 977 978 if (parent) { 979 insert_before(&parent->is_children, 980 entry, pos); 981 parent->is_nr_children++; 982 } else { 983 insert_before(&ss->s_iodevs, 984 entry, pos); 985 } 986 } 987 } 988 } 989 return (0); 990 } 991 992 /* Find the longest is_name field to aid formatting of output */ 993 static int 994 iodevs_is_name_maxlen(struct iodev_snapshot *list) 995 { 996 struct iodev_snapshot *entry; 997 int max = 0, cmax, len; 998 999 for (entry = list; entry; entry = entry->is_next) { 1000 cmax = iodevs_is_name_maxlen(entry->is_children); 1001 max = (cmax > max) ? cmax : max; 1002 len = strlen(entry->is_name); 1003 max = (len > max) ? len : max; 1004 } 1005 return (max); 1006 } 1007 1008 int 1009 acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df) 1010 { 1011 kstat_t *ksp; 1012 struct iodev_snapshot *pos; 1013 struct iodev_snapshot *list = NULL; 1014 int err = 0; 1015 1016 ss->s_nr_iodevs = 0; 1017 ss->s_iodevs_is_name_maxlen = 0; 1018 1019 /* 1020 * Call cleanup_iodevs_snapshot() so that a cache miss in 1021 * lookup_ks_name() will result in a fresh snapshot. 1022 */ 1023 cleanup_iodevs_snapshot(); 1024 1025 for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 1026 enum iodev_type type; 1027 1028 if (ksp->ks_type != KSTAT_TYPE_IO) 1029 continue; 1030 1031 /* e.g. "usb_byte_count" is not handled */ 1032 if ((type = get_iodev_type(ksp)) == IODEV_UNKNOWN) 1033 continue; 1034 1035 if (df && !(type & df->if_allowed_types)) 1036 continue; 1037 1038 if ((pos = malloc(sizeof (struct iodev_snapshot))) == NULL) { 1039 err = errno; 1040 goto out; 1041 } 1042 1043 (void) memset(pos, 0, sizeof (struct iodev_snapshot)); 1044 1045 pos->is_type = type; 1046 pos->is_crtime = ksp->ks_crtime; 1047 pos->is_snaptime = ksp->ks_snaptime; 1048 pos->is_id.id = IODEV_NO_ID; 1049 pos->is_parent_id.id = IODEV_NO_ID; 1050 pos->is_ksp = ksp; 1051 pos->is_instance = ksp->ks_instance; 1052 1053 (void) strlcpy(pos->is_module, ksp->ks_module, KSTAT_STRLEN); 1054 (void) strlcpy(pos->is_name, ksp->ks_name, KSTAT_STRLEN); 1055 get_pretty_name(ss->s_types, pos, kc); 1056 1057 /* 1058 * We must insert in sort order so e.g. vmstat -l 1059 * chooses in order. 1060 */ 1061 insert_into(&list, pos); 1062 } 1063 1064 choose_iodevs(ss, list, df); 1065 1066 /* before acquire_stats for collate_controller()'s benefit */ 1067 if (ss->s_types & SNAP_IODEV_ERRORS) { 1068 if ((err = acquire_iodev_errors(ss, kc)) != 0) 1069 goto out; 1070 } 1071 1072 if ((err = acquire_iodev_stats(ss->s_iodevs, kc)) != 0) 1073 goto out; 1074 1075 if (ss->s_types & SNAP_IOPATHS_LTI) { 1076 /* 1077 * -Y: kstats are LTI, need to create a synthetic LT 1078 * for -Y output. 1079 */ 1080 if ((err = create_lt(ss, ss->s_iodevs)) != 0) { 1081 return (err); 1082 } 1083 } 1084 if (ss->s_types & SNAP_IOPATHS_LI) { 1085 /* 1086 * -X: kstats are LTI, need to create a synthetic LI and 1087 * delete the LTI for -X output 1088 */ 1089 if ((err = create_li_delete_lti(ss, ss->s_iodevs)) != 0) { 1090 return (err); 1091 } 1092 } 1093 1094 /* determine width of longest is_name */ 1095 ss->s_iodevs_is_name_maxlen = iodevs_is_name_maxlen(ss->s_iodevs); 1096 1097 err = 0; 1098 out: 1099 return (err); 1100 } 1101 1102 void 1103 free_iodev(struct iodev_snapshot *iodev) 1104 { 1105 while (iodev->is_children) { 1106 struct iodev_snapshot *tmp = iodev->is_children; 1107 iodev->is_children = iodev->is_children->is_next; 1108 free_iodev(tmp); 1109 } 1110 1111 if (iodev->avl_list) { 1112 avl_remove(iodev->avl_list, iodev); 1113 if (avl_numnodes(iodev->avl_list) == 0) { 1114 avl_destroy(iodev->avl_list); 1115 free(iodev->avl_list); 1116 } 1117 } 1118 1119 free(iodev->is_errors.ks_data); 1120 free(iodev->is_pretty); 1121 free(iodev->is_dname); 1122 free(iodev->is_devid); 1123 free(iodev); 1124 } 1125