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