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 }