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