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