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