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