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