xref: /illumos-gate/usr/src/cmd/stat/common/acquire_iodevs.c (revision 4be70790cfe8d92fa6249ff02f1e5f1268ae2b8e)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5a08731ecScth  * Common Development and Distribution License (the "License").
6a08731ecScth  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*4be70790Swroche  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include "statcommon.h"
297c478bd9Sstevel@tonic-gate #include "dsr.h"
307c478bd9Sstevel@tonic-gate 
317c478bd9Sstevel@tonic-gate #include <sys/dklabel.h>
327c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
337c478bd9Sstevel@tonic-gate #include <stdlib.h>
347c478bd9Sstevel@tonic-gate #include <stdarg.h>
357c478bd9Sstevel@tonic-gate #include <unistd.h>
367c478bd9Sstevel@tonic-gate #include <strings.h>
377c478bd9Sstevel@tonic-gate #include <errno.h>
387c478bd9Sstevel@tonic-gate #include <limits.h>
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate static void insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev);
417c478bd9Sstevel@tonic-gate 
427c478bd9Sstevel@tonic-gate static struct iodev_snapshot *
437c478bd9Sstevel@tonic-gate make_controller(int cid)
447c478bd9Sstevel@tonic-gate {
457c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *new;
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate 	new = safe_alloc(sizeof (struct iodev_snapshot));
487c478bd9Sstevel@tonic-gate 	(void) memset(new, 0, sizeof (struct iodev_snapshot));
497c478bd9Sstevel@tonic-gate 	new->is_type = IODEV_CONTROLLER;
507c478bd9Sstevel@tonic-gate 	new->is_id.id = cid;
517c478bd9Sstevel@tonic-gate 	new->is_parent_id.id = IODEV_NO_ID;
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate 	(void) snprintf(new->is_name, sizeof (new->is_name), "c%d", cid);
547c478bd9Sstevel@tonic-gate 
557c478bd9Sstevel@tonic-gate 	return (new);
567c478bd9Sstevel@tonic-gate }
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate static struct iodev_snapshot *
597c478bd9Sstevel@tonic-gate find_iodev_by_name(struct iodev_snapshot *list, const char *name)
607c478bd9Sstevel@tonic-gate {
617c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos;
627c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos2;
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	for (pos = list; pos; pos = pos->is_next) {
657c478bd9Sstevel@tonic-gate 		if (strcmp(pos->is_name, name) == 0)
667c478bd9Sstevel@tonic-gate 			return (pos);
677c478bd9Sstevel@tonic-gate 
687c478bd9Sstevel@tonic-gate 		pos2 = find_iodev_by_name(pos->is_children, name);
697c478bd9Sstevel@tonic-gate 		if (pos2 != NULL)
707c478bd9Sstevel@tonic-gate 			return (pos2);
717c478bd9Sstevel@tonic-gate 	}
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate 	return (NULL);
747c478bd9Sstevel@tonic-gate }
757c478bd9Sstevel@tonic-gate 
767c478bd9Sstevel@tonic-gate static enum iodev_type
777c478bd9Sstevel@tonic-gate parent_iodev_type(enum iodev_type type)
787c478bd9Sstevel@tonic-gate {
797c478bd9Sstevel@tonic-gate 	switch (type) {
807c478bd9Sstevel@tonic-gate 		case IODEV_CONTROLLER: return (0);
8137fbbce5Scth 		case IODEV_IOPATH_LT: return (0);
8237fbbce5Scth 		case IODEV_IOPATH_LI: return (0);
837c478bd9Sstevel@tonic-gate 		case IODEV_NFS: return (0);
847c478bd9Sstevel@tonic-gate 		case IODEV_TAPE: return (0);
8537fbbce5Scth 		case IODEV_IOPATH_LTI: return (IODEV_DISK);
867c478bd9Sstevel@tonic-gate 		case IODEV_DISK: return (IODEV_CONTROLLER);
877c478bd9Sstevel@tonic-gate 		case IODEV_PARTITION: return (IODEV_DISK);
887c478bd9Sstevel@tonic-gate 	}
897c478bd9Sstevel@tonic-gate 	return (IODEV_UNKNOWN);
907c478bd9Sstevel@tonic-gate }
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate static int
937c478bd9Sstevel@tonic-gate id_match(struct iodev_id *id1, struct iodev_id *id2)
947c478bd9Sstevel@tonic-gate {
957c478bd9Sstevel@tonic-gate 	return (id1->id == id2->id &&
967c478bd9Sstevel@tonic-gate 	    strcmp(id1->tid, id2->tid) == 0);
977c478bd9Sstevel@tonic-gate }
987c478bd9Sstevel@tonic-gate 
997c478bd9Sstevel@tonic-gate static struct iodev_snapshot *
1007c478bd9Sstevel@tonic-gate find_parent(struct snapshot *ss, struct iodev_snapshot *iodev)
1017c478bd9Sstevel@tonic-gate {
1027c478bd9Sstevel@tonic-gate 	enum iodev_type parent_type = parent_iodev_type(iodev->is_type);
1037c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos;
1047c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos2;
1057c478bd9Sstevel@tonic-gate 
1067c478bd9Sstevel@tonic-gate 	if (parent_type == 0 || parent_type == IODEV_UNKNOWN)
1077c478bd9Sstevel@tonic-gate 		return (NULL);
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate 	if (iodev->is_parent_id.id == IODEV_NO_ID &&
1107c478bd9Sstevel@tonic-gate 	    iodev->is_parent_id.tid[0] == '\0')
1117c478bd9Sstevel@tonic-gate 		return (NULL);
1127c478bd9Sstevel@tonic-gate 
1137c478bd9Sstevel@tonic-gate 	if (parent_type == IODEV_CONTROLLER) {
1147c478bd9Sstevel@tonic-gate 		for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
1157c478bd9Sstevel@tonic-gate 			if (pos->is_type != IODEV_CONTROLLER)
1167c478bd9Sstevel@tonic-gate 				continue;
1177c478bd9Sstevel@tonic-gate 			if (pos->is_id.id != iodev->is_parent_id.id)
1187c478bd9Sstevel@tonic-gate 				continue;
1197c478bd9Sstevel@tonic-gate 			return (pos);
1207c478bd9Sstevel@tonic-gate 		}
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate 		if (!(ss->s_types & SNAP_CONTROLLERS))
1237c478bd9Sstevel@tonic-gate 			return (NULL);
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 		pos = make_controller(iodev->is_parent_id.id);
1267c478bd9Sstevel@tonic-gate 		insert_iodev(ss, pos);
1277c478bd9Sstevel@tonic-gate 		return (pos);
1287c478bd9Sstevel@tonic-gate 	}
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	/* IODEV_DISK parent */
1317c478bd9Sstevel@tonic-gate 	for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
1327c478bd9Sstevel@tonic-gate 		if (id_match(&iodev->is_parent_id, &pos->is_id) &&
1337c478bd9Sstevel@tonic-gate 		    pos->is_type == IODEV_DISK)
1347c478bd9Sstevel@tonic-gate 			return (pos);
1357c478bd9Sstevel@tonic-gate 		if (pos->is_type != IODEV_CONTROLLER)
1367c478bd9Sstevel@tonic-gate 			continue;
1377c478bd9Sstevel@tonic-gate 		for (pos2 = pos->is_children; pos2; pos2 = pos2->is_next) {
1387c478bd9Sstevel@tonic-gate 			if (pos2->is_type != IODEV_DISK)
1397c478bd9Sstevel@tonic-gate 				continue;
1407c478bd9Sstevel@tonic-gate 			if (id_match(&iodev->is_parent_id, &pos2->is_id))
1417c478bd9Sstevel@tonic-gate 				return (pos2);
1427c478bd9Sstevel@tonic-gate 		}
1437c478bd9Sstevel@tonic-gate 	}
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate 	return (NULL);
1467c478bd9Sstevel@tonic-gate }
1477c478bd9Sstevel@tonic-gate 
148*4be70790Swroche /*
149*4be70790Swroche  * Introduce an index into the list to speed up insert_into looking for the
150*4be70790Swroche  * right position in the list. This index is an AVL tree of all the
151*4be70790Swroche  * iodev_snapshot in the list.
152*4be70790Swroche  */
153*4be70790Swroche 
154*4be70790Swroche #define	offsetof(s, m)	(size_t)(&(((s *)0)->m))	/* for avl_create */
155*4be70790Swroche 
156*4be70790Swroche static int
157*4be70790Swroche avl_iodev_cmp(const void* is1, const void* is2)
158*4be70790Swroche {
159*4be70790Swroche 	int c = iodev_cmp((struct iodev_snapshot *)is1,
160*4be70790Swroche 	    (struct iodev_snapshot *)is2);
161*4be70790Swroche 
162*4be70790Swroche 	if (c > 0)
163*4be70790Swroche 		return (1);
164*4be70790Swroche 
165*4be70790Swroche 	if (c < 0)
166*4be70790Swroche 		return (-1);
167*4be70790Swroche 
168*4be70790Swroche 	return (0);
169*4be70790Swroche }
170*4be70790Swroche 
171*4be70790Swroche static void
172*4be70790Swroche ix_new_list(struct iodev_snapshot *elem)
173*4be70790Swroche {
174*4be70790Swroche 	avl_tree_t *l = malloc(sizeof (avl_tree_t));
175*4be70790Swroche 
176*4be70790Swroche 	elem->avl_list = l;
177*4be70790Swroche 	if (l == NULL)
178*4be70790Swroche 		return;
179*4be70790Swroche 
180*4be70790Swroche 	avl_create(l, avl_iodev_cmp, sizeof (struct iodev_snapshot),
181*4be70790Swroche 	    offsetof(struct iodev_snapshot, avl_link));
182*4be70790Swroche 
183*4be70790Swroche 	avl_add(l, elem);
184*4be70790Swroche }
185*4be70790Swroche 
186*4be70790Swroche static void
187*4be70790Swroche ix_list_del(struct iodev_snapshot *elem)
188*4be70790Swroche {
189*4be70790Swroche 	avl_tree_t *l = elem->avl_list;
190*4be70790Swroche 
191*4be70790Swroche 	if (l == NULL)
192*4be70790Swroche 		return;
193*4be70790Swroche 
194*4be70790Swroche 	elem->avl_list = NULL;
195*4be70790Swroche 
196*4be70790Swroche 	avl_remove(l, elem);
197*4be70790Swroche 	if (avl_numnodes(l) == 0) {
198*4be70790Swroche 		avl_destroy(l);
199*4be70790Swroche 		free(l);
200*4be70790Swroche 	}
201*4be70790Swroche }
202*4be70790Swroche 
203*4be70790Swroche static void
204*4be70790Swroche ix_insert_here(struct iodev_snapshot *pos, struct iodev_snapshot *elem, int ba)
205*4be70790Swroche {
206*4be70790Swroche 	avl_tree_t *l = pos->avl_list;
207*4be70790Swroche 	elem->avl_list = l;
208*4be70790Swroche 
209*4be70790Swroche 	if (l == NULL)
210*4be70790Swroche 		return;
211*4be70790Swroche 
212*4be70790Swroche 	avl_insert_here(l, elem, pos, ba);
213*4be70790Swroche }
214*4be70790Swroche 
2157c478bd9Sstevel@tonic-gate static void
2167c478bd9Sstevel@tonic-gate list_del(struct iodev_snapshot **list, struct iodev_snapshot *pos)
2177c478bd9Sstevel@tonic-gate {
218*4be70790Swroche 	ix_list_del(pos);
219*4be70790Swroche 
2207c478bd9Sstevel@tonic-gate 	if (*list == pos)
2217c478bd9Sstevel@tonic-gate 		*list = pos->is_next;
2227c478bd9Sstevel@tonic-gate 	if (pos->is_next)
2237c478bd9Sstevel@tonic-gate 		pos->is_next->is_prev = pos->is_prev;
2247c478bd9Sstevel@tonic-gate 	if (pos->is_prev)
2257c478bd9Sstevel@tonic-gate 		pos->is_prev->is_next = pos->is_next;
2267c478bd9Sstevel@tonic-gate 	pos->is_prev = pos->is_next = NULL;
2277c478bd9Sstevel@tonic-gate }
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate static void
2307c478bd9Sstevel@tonic-gate insert_before(struct iodev_snapshot **list, struct iodev_snapshot *pos,
2317c478bd9Sstevel@tonic-gate     struct iodev_snapshot *new)
2327c478bd9Sstevel@tonic-gate {
2337c478bd9Sstevel@tonic-gate 	if (pos == NULL) {
2347c478bd9Sstevel@tonic-gate 		new->is_prev = new->is_next = NULL;
2357c478bd9Sstevel@tonic-gate 		*list = new;
236*4be70790Swroche 		ix_new_list(new);
2377c478bd9Sstevel@tonic-gate 		return;
2387c478bd9Sstevel@tonic-gate 	}
2397c478bd9Sstevel@tonic-gate 
2407c478bd9Sstevel@tonic-gate 	new->is_next = pos;
2417c478bd9Sstevel@tonic-gate 	new->is_prev = pos->is_prev;
2427c478bd9Sstevel@tonic-gate 	if (pos->is_prev)
2437c478bd9Sstevel@tonic-gate 		pos->is_prev->is_next = new;
2447c478bd9Sstevel@tonic-gate 	else
2457c478bd9Sstevel@tonic-gate 		*list = new;
2467c478bd9Sstevel@tonic-gate 	pos->is_prev = new;
247*4be70790Swroche 
248*4be70790Swroche 	ix_insert_here(pos, new, AVL_BEFORE);
2497c478bd9Sstevel@tonic-gate }
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate static void
2527c478bd9Sstevel@tonic-gate insert_after(struct iodev_snapshot **list, struct iodev_snapshot *pos,
2537c478bd9Sstevel@tonic-gate     struct iodev_snapshot *new)
2547c478bd9Sstevel@tonic-gate {
2557c478bd9Sstevel@tonic-gate 	if (pos == NULL) {
2567c478bd9Sstevel@tonic-gate 		new->is_prev = new->is_next = NULL;
2577c478bd9Sstevel@tonic-gate 		*list = new;
258*4be70790Swroche 		ix_new_list(new);
2597c478bd9Sstevel@tonic-gate 		return;
2607c478bd9Sstevel@tonic-gate 	}
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	new->is_next = pos->is_next;
2637c478bd9Sstevel@tonic-gate 	new->is_prev = pos;
2647c478bd9Sstevel@tonic-gate 	if (pos->is_next)
2657c478bd9Sstevel@tonic-gate 		pos->is_next->is_prev = new;
2667c478bd9Sstevel@tonic-gate 	pos->is_next = new;
267*4be70790Swroche 
268*4be70790Swroche 	ix_insert_here(pos, new, AVL_AFTER);
2697c478bd9Sstevel@tonic-gate }
2707c478bd9Sstevel@tonic-gate 
2717c478bd9Sstevel@tonic-gate static void
2727c478bd9Sstevel@tonic-gate insert_into(struct iodev_snapshot **list, struct iodev_snapshot *iodev)
2737c478bd9Sstevel@tonic-gate {
2747c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *tmp = *list;
275*4be70790Swroche 	avl_tree_t *l;
276*4be70790Swroche 	void *p;
277*4be70790Swroche 	avl_index_t where;
278*4be70790Swroche 
2797c478bd9Sstevel@tonic-gate 	if (*list == NULL) {
2807c478bd9Sstevel@tonic-gate 		*list = iodev;
281*4be70790Swroche 		ix_new_list(iodev);
2827c478bd9Sstevel@tonic-gate 		return;
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 
285*4be70790Swroche 	/*
286*4be70790Swroche 	 * Optimize the search: instead of walking the entire list
287*4be70790Swroche 	 * (which can contain thousands of nodes), search in the AVL
288*4be70790Swroche 	 * tree the nearest node and reposition the startup point to
289*4be70790Swroche 	 * this node rather than always starting from the beginning
290*4be70790Swroche 	 * of the list.
291*4be70790Swroche 	 */
292*4be70790Swroche 	l = tmp->avl_list;
293*4be70790Swroche 	if (l != NULL) {
294*4be70790Swroche 		p = avl_find(l, iodev, &where);
295*4be70790Swroche 		if (p == NULL) {
296*4be70790Swroche 			p = avl_nearest(l, where, AVL_BEFORE);
297*4be70790Swroche 		}
298*4be70790Swroche 		if (p != NULL) {
299*4be70790Swroche 			tmp = (struct iodev_snapshot *)p;
300*4be70790Swroche 		}
301*4be70790Swroche 	}
302*4be70790Swroche 
3037c478bd9Sstevel@tonic-gate 	for (;;) {
3047c478bd9Sstevel@tonic-gate 		if (iodev_cmp(tmp, iodev) > 0) {
3057c478bd9Sstevel@tonic-gate 			insert_before(list, tmp, iodev);
3067c478bd9Sstevel@tonic-gate 			return;
3077c478bd9Sstevel@tonic-gate 		}
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 		if (tmp->is_next == NULL)
3107c478bd9Sstevel@tonic-gate 			break;
3117c478bd9Sstevel@tonic-gate 
3127c478bd9Sstevel@tonic-gate 		tmp = tmp->is_next;
3137c478bd9Sstevel@tonic-gate 	}
3147c478bd9Sstevel@tonic-gate 
3157c478bd9Sstevel@tonic-gate 	insert_after(list, tmp, iodev);
3167c478bd9Sstevel@tonic-gate }
3177c478bd9Sstevel@tonic-gate 
3187c478bd9Sstevel@tonic-gate static int
3197c478bd9Sstevel@tonic-gate disk_or_partition(enum iodev_type type)
3207c478bd9Sstevel@tonic-gate {
3217c478bd9Sstevel@tonic-gate 	return (type == IODEV_DISK || type == IODEV_PARTITION);
3227c478bd9Sstevel@tonic-gate }
3237c478bd9Sstevel@tonic-gate 
32437fbbce5Scth static int
32537fbbce5Scth disk_or_partition_or_iopath(enum iodev_type type)
32637fbbce5Scth {
32737fbbce5Scth 	return (type == IODEV_DISK || type == IODEV_PARTITION ||
32837fbbce5Scth 	    type == IODEV_IOPATH_LTI);
32937fbbce5Scth }
33037fbbce5Scth 
3317c478bd9Sstevel@tonic-gate static void
3327c478bd9Sstevel@tonic-gate insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev)
3337c478bd9Sstevel@tonic-gate {
3347c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *parent = find_parent(ss, iodev);
3357c478bd9Sstevel@tonic-gate 	struct iodev_snapshot **list;
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	if (parent != NULL) {
3387c478bd9Sstevel@tonic-gate 		list = &parent->is_children;
3397c478bd9Sstevel@tonic-gate 		parent->is_nr_children++;
3407c478bd9Sstevel@tonic-gate 	} else {
3417c478bd9Sstevel@tonic-gate 		list = &ss->s_iodevs;
3427c478bd9Sstevel@tonic-gate 		ss->s_nr_iodevs++;
3437c478bd9Sstevel@tonic-gate 	}
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	insert_into(list, iodev);
3467c478bd9Sstevel@tonic-gate }
3477c478bd9Sstevel@tonic-gate 
34837fbbce5Scth /* return 1 if dev passes filter */
3497c478bd9Sstevel@tonic-gate static int
3507c478bd9Sstevel@tonic-gate iodev_match(struct iodev_snapshot *dev, struct iodev_filter *df)
3517c478bd9Sstevel@tonic-gate {
3527c478bd9Sstevel@tonic-gate 	int	is_floppy = (strncmp(dev->is_name, "fd", 2) == 0);
35337fbbce5Scth 	char	*isn, *ispn, *ifn;
35437fbbce5Scth 	char	*path;
35537fbbce5Scth 	int	ifnl;
35637fbbce5Scth 	size_t	i;
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	/* no filter, pass */
3597c478bd9Sstevel@tonic-gate 	if (df == NULL)
36037fbbce5Scth 		return (1);		/* pass */
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	/* no filtered names, pass if not floppy and skipped */
3637c478bd9Sstevel@tonic-gate 	if (df->if_nr_names == NULL)
3647c478bd9Sstevel@tonic-gate 		return (!(df->if_skip_floppy && is_floppy));
3657c478bd9Sstevel@tonic-gate 
36637fbbce5Scth 	isn = dev->is_name;
36737fbbce5Scth 	ispn = dev->is_pretty;
3687c478bd9Sstevel@tonic-gate 	for (i = 0; i < df->if_nr_names; i++) {
36937fbbce5Scth 		ifn = df->if_names[i];
37037fbbce5Scth 		ifnl = strlen(ifn);
37137fbbce5Scth 		path = strchr(ifn, '.');
37237fbbce5Scth 
37337fbbce5Scth 		if ((strcmp(isn, ifn) == 0) ||
37437fbbce5Scth 		    (ispn && (strcmp(ispn, ifn) == 0)))
37537fbbce5Scth 			return (1);	/* pass */
37637fbbce5Scth 
37737fbbce5Scth 		/* if filter is a path allow partial match */
37837fbbce5Scth 		if (path &&
37937fbbce5Scth 		    ((strncmp(isn, ifn, ifnl) == 0) ||
38037fbbce5Scth 		    (ispn && (strncmp(ispn, ifn, ifnl) == 0))))
38137fbbce5Scth 			return (1);	/* pass */
3827c478bd9Sstevel@tonic-gate 	}
3837c478bd9Sstevel@tonic-gate 
38437fbbce5Scth 	return (0);			/* fail */
38537fbbce5Scth }
38637fbbce5Scth 
38737fbbce5Scth /* return 1 if path is an mpxio path associated with dev */
38837fbbce5Scth static int
38937fbbce5Scth iodev_path_match(struct iodev_snapshot *dev, struct iodev_snapshot *path)
39037fbbce5Scth {
39137fbbce5Scth 	char	*dn, *pn;
39237fbbce5Scth 	int	dnl;
39337fbbce5Scth 
39437fbbce5Scth 	dn = dev->is_name;
39537fbbce5Scth 	pn = path->is_name;
39637fbbce5Scth 	dnl = strlen(dn);
39737fbbce5Scth 
39837fbbce5Scth 	if ((strncmp(pn, dn, dnl) == 0) && (pn[dnl] == '.'))
39937fbbce5Scth 		return (1);			/* yes */
40037fbbce5Scth 
40137fbbce5Scth 	return (0);				/* no */
4027c478bd9Sstevel@tonic-gate }
4037c478bd9Sstevel@tonic-gate 
4047c478bd9Sstevel@tonic-gate /* select which I/O devices to collect stats for */
4057c478bd9Sstevel@tonic-gate static void
4067c478bd9Sstevel@tonic-gate choose_iodevs(struct snapshot *ss, struct iodev_snapshot *iodevs,
4077c478bd9Sstevel@tonic-gate     struct iodev_filter *df)
4087c478bd9Sstevel@tonic-gate {
40937fbbce5Scth 	struct iodev_snapshot	*pos, *ppos, *tmp, *ptmp;
41037fbbce5Scth 	int			nr_iodevs;
41137fbbce5Scth 	int			nr_iodevs_orig;
41237fbbce5Scth 
41337fbbce5Scth 	nr_iodevs = df ? df->if_max_iodevs : UNLIMITED_IODEVS;
41437fbbce5Scth 	nr_iodevs_orig = nr_iodevs;
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	if (nr_iodevs == UNLIMITED_IODEVS)
4177c478bd9Sstevel@tonic-gate 		nr_iodevs = INT_MAX;
4187c478bd9Sstevel@tonic-gate 
41937fbbce5Scth 	/* add the full matches */
42037fbbce5Scth 	pos = iodevs;
4217c478bd9Sstevel@tonic-gate 	while (pos && nr_iodevs) {
42237fbbce5Scth 		tmp = pos;
4237c478bd9Sstevel@tonic-gate 		pos = pos->is_next;
4247c478bd9Sstevel@tonic-gate 
4257c478bd9Sstevel@tonic-gate 		if (!iodev_match(tmp, df))
42637fbbce5Scth 			continue;	/* failed full match */
4277c478bd9Sstevel@tonic-gate 
4287c478bd9Sstevel@tonic-gate 		list_del(&iodevs, tmp);
4297c478bd9Sstevel@tonic-gate 		insert_iodev(ss, tmp);
4307c478bd9Sstevel@tonic-gate 
43137fbbce5Scth 		/*
43237fbbce5Scth 		 * Add all mpxio paths associated with match above. Added
43337fbbce5Scth 		 * paths don't count against nr_iodevs.
43437fbbce5Scth 		 */
43537fbbce5Scth 		if (strchr(tmp->is_name, '.') == NULL) {
43637fbbce5Scth 		ppos = iodevs;
43737fbbce5Scth 		while (ppos) {
43837fbbce5Scth 			ptmp = ppos;
43937fbbce5Scth 			ppos = ppos->is_next;
44037fbbce5Scth 
44137fbbce5Scth 			if (!iodev_path_match(tmp, ptmp))
44237fbbce5Scth 				continue;	/* not an mpxio path */
44337fbbce5Scth 
44437fbbce5Scth 			list_del(&iodevs, ptmp);
44537fbbce5Scth 			insert_iodev(ss, ptmp);
44637fbbce5Scth 			if (pos == ptmp)
44737fbbce5Scth 				pos = ppos;
44837fbbce5Scth 		}
4497c478bd9Sstevel@tonic-gate 		}
4507c478bd9Sstevel@tonic-gate 
45137fbbce5Scth 		nr_iodevs--;
45237fbbce5Scth 	}
4537c478bd9Sstevel@tonic-gate 
45437fbbce5Scth 	/*
45537fbbce5Scth 	 * If we had a filter, and *nothing* passed the filter then we
45637fbbce5Scth 	 * don't want to fill the  remaining slots - it is just confusing
45737fbbce5Scth 	 * if we don that, it makes it look like the filter code is broken.
45837fbbce5Scth 	 */
45937fbbce5Scth 	if ((df->if_nr_names == NULL) || (nr_iodevs != nr_iodevs_orig)) {
4607c478bd9Sstevel@tonic-gate 		/* now insert any iodevs into the remaining slots */
46137fbbce5Scth 		pos = iodevs;
4627c478bd9Sstevel@tonic-gate 		while (pos && nr_iodevs) {
46337fbbce5Scth 			tmp = pos;
4647c478bd9Sstevel@tonic-gate 			pos = pos->is_next;
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 			if (df && df->if_skip_floppy &&
4677c478bd9Sstevel@tonic-gate 			    strncmp(tmp->is_name, "fd", 2) == 0)
4687c478bd9Sstevel@tonic-gate 				continue;
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 			list_del(&iodevs, tmp);
4717c478bd9Sstevel@tonic-gate 			insert_iodev(ss, tmp);
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate 			--nr_iodevs;
4747c478bd9Sstevel@tonic-gate 		}
47537fbbce5Scth 	}
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	/* clear the unwanted ones */
4787c478bd9Sstevel@tonic-gate 	pos = iodevs;
4797c478bd9Sstevel@tonic-gate 	while (pos) {
4807c478bd9Sstevel@tonic-gate 		struct iodev_snapshot *tmp = pos;
4817c478bd9Sstevel@tonic-gate 		pos = pos->is_next;
4827c478bd9Sstevel@tonic-gate 		free_iodev(tmp);
4837c478bd9Sstevel@tonic-gate 	}
4847c478bd9Sstevel@tonic-gate }
4857c478bd9Sstevel@tonic-gate 
4867c478bd9Sstevel@tonic-gate static int
4877c478bd9Sstevel@tonic-gate collate_controller(struct iodev_snapshot *controller,
4887c478bd9Sstevel@tonic-gate     struct iodev_snapshot *disk)
4897c478bd9Sstevel@tonic-gate {
4907c478bd9Sstevel@tonic-gate 	controller->is_stats.nread += disk->is_stats.nread;
4917c478bd9Sstevel@tonic-gate 	controller->is_stats.nwritten += disk->is_stats.nwritten;
4927c478bd9Sstevel@tonic-gate 	controller->is_stats.reads += disk->is_stats.reads;
4937c478bd9Sstevel@tonic-gate 	controller->is_stats.writes += disk->is_stats.writes;
4947c478bd9Sstevel@tonic-gate 	controller->is_stats.wtime += disk->is_stats.wtime;
4957c478bd9Sstevel@tonic-gate 	controller->is_stats.wlentime += disk->is_stats.wlentime;
4967c478bd9Sstevel@tonic-gate 	controller->is_stats.rtime += disk->is_stats.rtime;
4977c478bd9Sstevel@tonic-gate 	controller->is_stats.rlentime += disk->is_stats.rlentime;
4987c478bd9Sstevel@tonic-gate 	controller->is_crtime += disk->is_crtime;
4997c478bd9Sstevel@tonic-gate 	controller->is_snaptime += disk->is_snaptime;
5007c478bd9Sstevel@tonic-gate 	if (kstat_add(&disk->is_errors, &controller->is_errors))
5017c478bd9Sstevel@tonic-gate 		return (errno);
5027c478bd9Sstevel@tonic-gate 	return (0);
5037c478bd9Sstevel@tonic-gate }
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate static int
5067c478bd9Sstevel@tonic-gate acquire_iodev_stats(struct iodev_snapshot *list, kstat_ctl_t *kc)
5077c478bd9Sstevel@tonic-gate {
5087c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos;
5097c478bd9Sstevel@tonic-gate 	int err = 0;
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate 	for (pos = list; pos; pos = pos->is_next) {
5127c478bd9Sstevel@tonic-gate 		/* controllers don't have stats (yet) */
5137c478bd9Sstevel@tonic-gate 		if (pos->is_ksp != NULL) {
5147c478bd9Sstevel@tonic-gate 			if (kstat_read(kc, pos->is_ksp, &pos->is_stats) == -1)
5157c478bd9Sstevel@tonic-gate 				return (errno);
5167c478bd9Sstevel@tonic-gate 			/* make sure crtime/snaptime is updated */
5177c478bd9Sstevel@tonic-gate 			pos->is_crtime = pos->is_ksp->ks_crtime;
5187c478bd9Sstevel@tonic-gate 			pos->is_snaptime = pos->is_ksp->ks_snaptime;
5197c478bd9Sstevel@tonic-gate 		}
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 		if ((err = acquire_iodev_stats(pos->is_children, kc)))
5227c478bd9Sstevel@tonic-gate 			return (err);
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 		if (pos->is_type == IODEV_CONTROLLER) {
5257c478bd9Sstevel@tonic-gate 			struct iodev_snapshot *pos2 = pos->is_children;
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate 			for (; pos2; pos2 = pos2->is_next) {
5287c478bd9Sstevel@tonic-gate 				if ((err = collate_controller(pos, pos2)))
5297c478bd9Sstevel@tonic-gate 					return (err);
5307c478bd9Sstevel@tonic-gate 			}
5317c478bd9Sstevel@tonic-gate 		}
5327c478bd9Sstevel@tonic-gate 	}
5337c478bd9Sstevel@tonic-gate 
5347c478bd9Sstevel@tonic-gate 	return (0);
5357c478bd9Sstevel@tonic-gate }
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate static int
5387c478bd9Sstevel@tonic-gate acquire_iodev_errors(struct snapshot *ss, kstat_ctl_t *kc)
5397c478bd9Sstevel@tonic-gate {
5407c478bd9Sstevel@tonic-gate 	kstat_t *ksp;
5417c478bd9Sstevel@tonic-gate 
5427c478bd9Sstevel@tonic-gate 	if (!(ss->s_types && SNAP_IODEV_ERRORS))
5437c478bd9Sstevel@tonic-gate 		return (0);
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
5467c478bd9Sstevel@tonic-gate 		char kstat_name[KSTAT_STRLEN];
5477c478bd9Sstevel@tonic-gate 		char *dname = kstat_name;
5487c478bd9Sstevel@tonic-gate 		char *ename = ksp->ks_name;
5497c478bd9Sstevel@tonic-gate 		struct iodev_snapshot *iodev;
5507c478bd9Sstevel@tonic-gate 
5517c478bd9Sstevel@tonic-gate 		if (ksp->ks_type != KSTAT_TYPE_NAMED)
5527c478bd9Sstevel@tonic-gate 			continue;
5537c478bd9Sstevel@tonic-gate 		if (strncmp(ksp->ks_class, "device_error", 12) != 0 &&
5547c478bd9Sstevel@tonic-gate 		    strncmp(ksp->ks_class, "iopath_error", 12) != 0)
5557c478bd9Sstevel@tonic-gate 			continue;
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 		/*
5587c478bd9Sstevel@tonic-gate 		 * Some drivers may not follow the naming convention
5597c478bd9Sstevel@tonic-gate 		 * for error kstats (i.e., drivername,err) so
5607c478bd9Sstevel@tonic-gate 		 * be sure we don't walk off the end.
5617c478bd9Sstevel@tonic-gate 		 */
5627c478bd9Sstevel@tonic-gate 		while (*ename && *ename != ',') {
5637c478bd9Sstevel@tonic-gate 			*dname = *ename;
5647c478bd9Sstevel@tonic-gate 			dname++;
5657c478bd9Sstevel@tonic-gate 			ename++;
5667c478bd9Sstevel@tonic-gate 		}
5677c478bd9Sstevel@tonic-gate 		*dname = '\0';
5687c478bd9Sstevel@tonic-gate 
5697c478bd9Sstevel@tonic-gate 		iodev = find_iodev_by_name(ss->s_iodevs, kstat_name);
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 		if (iodev == NULL)
5727c478bd9Sstevel@tonic-gate 			continue;
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 		if (kstat_read(kc, ksp, NULL) == -1)
5757c478bd9Sstevel@tonic-gate 			return (errno);
5767c478bd9Sstevel@tonic-gate 		if (kstat_copy(ksp, &iodev->is_errors) == -1)
5777c478bd9Sstevel@tonic-gate 			return (errno);
5787c478bd9Sstevel@tonic-gate 	}
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	return (0);
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate static void
5847c478bd9Sstevel@tonic-gate get_ids(struct iodev_snapshot *iodev, const char *pretty)
5857c478bd9Sstevel@tonic-gate {
5867c478bd9Sstevel@tonic-gate 	int ctr, disk, slice, ret;
5877c478bd9Sstevel@tonic-gate 	char *target;
5887c478bd9Sstevel@tonic-gate 	const char *p1;
5897c478bd9Sstevel@tonic-gate 	const char *p2;
5907c478bd9Sstevel@tonic-gate 
5917c478bd9Sstevel@tonic-gate 	if (pretty == NULL)
5927c478bd9Sstevel@tonic-gate 		return;
5937c478bd9Sstevel@tonic-gate 
5947c478bd9Sstevel@tonic-gate 	if (sscanf(pretty, "c%d", &ctr) != 1)
5957c478bd9Sstevel@tonic-gate 		return;
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	p1 = pretty;
5987c478bd9Sstevel@tonic-gate 	while (*p1 && *p1 != 't')
5997c478bd9Sstevel@tonic-gate 		++p1;
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	if (!*p1)
6027c478bd9Sstevel@tonic-gate 		return;
6037c478bd9Sstevel@tonic-gate 	++p1;
6047c478bd9Sstevel@tonic-gate 
6057c478bd9Sstevel@tonic-gate 	p2 = p1;
6067c478bd9Sstevel@tonic-gate 	while (*p2 && *p2 != 'd')
6077c478bd9Sstevel@tonic-gate 		++p2;
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	if (!*p2 || p2 == p1)
6107c478bd9Sstevel@tonic-gate 		return;
6117c478bd9Sstevel@tonic-gate 
6127c478bd9Sstevel@tonic-gate 	target = safe_alloc(1 + p2 - p1);
6137c478bd9Sstevel@tonic-gate 	(void) strlcpy(target, p1, 1 + p2 - p1);
6147c478bd9Sstevel@tonic-gate 
6157c478bd9Sstevel@tonic-gate 	ret = sscanf(p2, "d%d%*[sp]%d", &disk, &slice);
6167c478bd9Sstevel@tonic-gate 
6177c478bd9Sstevel@tonic-gate 	if (ret == 2 && iodev->is_type == IODEV_PARTITION) {
6187c478bd9Sstevel@tonic-gate 		iodev->is_id.id = slice;
6197c478bd9Sstevel@tonic-gate 		iodev->is_parent_id.id = disk;
6207c478bd9Sstevel@tonic-gate 		(void) strlcpy(iodev->is_parent_id.tid, target, KSTAT_STRLEN);
6217c478bd9Sstevel@tonic-gate 	} else if (ret == 1) {
6227c478bd9Sstevel@tonic-gate 		if (iodev->is_type == IODEV_DISK) {
6237c478bd9Sstevel@tonic-gate 			iodev->is_id.id = disk;
6247c478bd9Sstevel@tonic-gate 			(void) strlcpy(iodev->is_id.tid, target, KSTAT_STRLEN);
6257c478bd9Sstevel@tonic-gate 			iodev->is_parent_id.id = ctr;
62637fbbce5Scth 		} else if (iodev->is_type == IODEV_IOPATH_LTI) {
6277c478bd9Sstevel@tonic-gate 			iodev->is_parent_id.id = disk;
6287c478bd9Sstevel@tonic-gate 			(void) strlcpy(iodev->is_parent_id.tid,
6297c478bd9Sstevel@tonic-gate 			    target, KSTAT_STRLEN);
6307c478bd9Sstevel@tonic-gate 		}
6317c478bd9Sstevel@tonic-gate 	}
6327c478bd9Sstevel@tonic-gate 
6337c478bd9Sstevel@tonic-gate 	free(target);
6347c478bd9Sstevel@tonic-gate }
6357c478bd9Sstevel@tonic-gate 
6367c478bd9Sstevel@tonic-gate static void
6377c478bd9Sstevel@tonic-gate get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev,
6387c478bd9Sstevel@tonic-gate 	kstat_ctl_t *kc)
6397c478bd9Sstevel@tonic-gate {
6407c478bd9Sstevel@tonic-gate 	disk_list_t	*dl;
6417c478bd9Sstevel@tonic-gate 	char		*pretty = NULL;
6427c478bd9Sstevel@tonic-gate 
6437c478bd9Sstevel@tonic-gate 	if (iodev->is_type == IODEV_NFS) {
6447c478bd9Sstevel@tonic-gate 		if (!(types & SNAP_IODEV_PRETTY))
6457c478bd9Sstevel@tonic-gate 			return;
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 		iodev->is_pretty = lookup_nfs_name(iodev->is_name, kc);
6487c478bd9Sstevel@tonic-gate 		return;
6497c478bd9Sstevel@tonic-gate 	}
6507c478bd9Sstevel@tonic-gate 
65137fbbce5Scth 	/* lookup/translate the kstat name */
652a08731ecScth 	dl = lookup_ks_name(iodev->is_name, (types & SNAP_IODEV_DEVID) ? 1 : 0);
6537c478bd9Sstevel@tonic-gate 	if (dl == NULL)
6547c478bd9Sstevel@tonic-gate 		return;
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	if (dl->dsk)
6577c478bd9Sstevel@tonic-gate 		pretty = safe_strdup(dl->dsk);
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	if (types & SNAP_IODEV_PRETTY) {
6607c478bd9Sstevel@tonic-gate 		if (dl->dname)
6617c478bd9Sstevel@tonic-gate 			iodev->is_dname = safe_strdup(dl->dname);
6627c478bd9Sstevel@tonic-gate 	}
6637c478bd9Sstevel@tonic-gate 
6647c478bd9Sstevel@tonic-gate 	if (dl->devidstr)
6657c478bd9Sstevel@tonic-gate 		iodev->is_devid = safe_strdup(dl->devidstr);
6667c478bd9Sstevel@tonic-gate 
6677c478bd9Sstevel@tonic-gate 	get_ids(iodev, pretty);
6687c478bd9Sstevel@tonic-gate 
66937fbbce5Scth 	/*
67037fbbce5Scth 	 * we fill in pretty name wether it is asked for or not because
67137fbbce5Scth 	 * it could be used in a filter by match_iodevs.
67237fbbce5Scth 	 */
6737c478bd9Sstevel@tonic-gate 	iodev->is_pretty = pretty;
6747c478bd9Sstevel@tonic-gate }
6757c478bd9Sstevel@tonic-gate 
6767c478bd9Sstevel@tonic-gate static enum iodev_type
6777c478bd9Sstevel@tonic-gate get_iodev_type(kstat_t *ksp)
6787c478bd9Sstevel@tonic-gate {
6797c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "disk") == 0)
6807c478bd9Sstevel@tonic-gate 		return (IODEV_DISK);
6817c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "partition") == 0)
6827c478bd9Sstevel@tonic-gate 		return (IODEV_PARTITION);
6837c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "nfs") == 0)
6847c478bd9Sstevel@tonic-gate 		return (IODEV_NFS);
6857c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "iopath") == 0)
68637fbbce5Scth 		return (IODEV_IOPATH_LTI);
6877c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "tape") == 0)
6887c478bd9Sstevel@tonic-gate 		return (IODEV_TAPE);
6897c478bd9Sstevel@tonic-gate 	return (IODEV_UNKNOWN);
6907c478bd9Sstevel@tonic-gate }
6917c478bd9Sstevel@tonic-gate 
69237fbbce5Scth /* get the lun/target/initiator from the name, return 1 on success */
69337fbbce5Scth static int
69437fbbce5Scth get_lti(char *s,
69537fbbce5Scth 	char *lname, int *l, char *tname, int *t, char *iname, int *i)
69637fbbce5Scth {
69737fbbce5Scth 	int  num = 0;
69837fbbce5Scth 
69937fbbce5Scth 	num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z]%d", lname, l,
70037fbbce5Scth 	    tname, t, iname, i);
70137fbbce5Scth 	return ((num == 6) ? 1 : 0);
70237fbbce5Scth }
70337fbbce5Scth 
70437fbbce5Scth 
70537fbbce5Scth /* get the lun, target, and initiator name and instance */
70637fbbce5Scth static void
70737fbbce5Scth get_path_info(struct iodev_snapshot *io, char *mod, int *type, int *inst,
70837fbbce5Scth     char *name, size_t size)
70937fbbce5Scth {
71037fbbce5Scth 
71137fbbce5Scth 	/*
71237fbbce5Scth 	 * If it is iopath or ssd then pad the name with i/t/l so we can sort
71337fbbce5Scth 	 * by alpha order and set type for IOPATH to DISK since we want to
71437fbbce5Scth 	 * have it grouped with its ssd parent. The lun can be 5 digits,
71537fbbce5Scth 	 * the target can be 4 digits, and the initiator can be 3 digits and
71637fbbce5Scth 	 * the padding is done appropriately for string comparisons.
71737fbbce5Scth 	 */
71837fbbce5Scth 	if (disk_or_partition_or_iopath(io->is_type)) {
71937fbbce5Scth 		int i1, t1, l1;
72037fbbce5Scth 		char tname[KSTAT_STRLEN], iname[KSTAT_STRLEN];
72137fbbce5Scth 		char *ptr, lname[KSTAT_STRLEN];
72237fbbce5Scth 
72337fbbce5Scth 		i1 = t1 = l1 = 0;
72437fbbce5Scth 		(void) get_lti(io->is_name, lname, &l1, tname, &t1, iname, &i1);
72537fbbce5Scth 		*type = io->is_type;
72637fbbce5Scth 		if (io->is_type == IODEV_DISK) {
72737fbbce5Scth 			(void) snprintf(name, size, "%s%05d", lname, l1);
72837fbbce5Scth 		} else if (io->is_type == IODEV_PARTITION) {
72937fbbce5Scth 			ptr = strchr(io->is_name, ',');
73037fbbce5Scth 			(void) snprintf(name, size, "%s%05d%s", lname, l1, ptr);
73137fbbce5Scth 		} else {
73237fbbce5Scth 			(void) snprintf(name, size, "%s%05d.%s%04d.%s%03d",
73337fbbce5Scth 			    lname, l1, tname, t1, iname, i1);
73437fbbce5Scth 			/* set to disk so we sort with disks */
73537fbbce5Scth 			*type = IODEV_DISK;
73637fbbce5Scth 		}
73737fbbce5Scth 		(void) strcpy(mod, lname);
73837fbbce5Scth 		*inst = l1;
73937fbbce5Scth 	} else {
74037fbbce5Scth 		(void) strcpy(mod, io->is_module);
74137fbbce5Scth 		(void) strcpy(name, io->is_name);
74237fbbce5Scth 		*type = io->is_type;
74337fbbce5Scth 		*inst = io->is_instance;
74437fbbce5Scth 	}
74537fbbce5Scth }
74637fbbce5Scth 
7477c478bd9Sstevel@tonic-gate int
7487c478bd9Sstevel@tonic-gate iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2)
7497c478bd9Sstevel@tonic-gate {
75037fbbce5Scth 	int	type1, type2;
75137fbbce5Scth 	int	inst1, inst2;
75237fbbce5Scth 	char	name1[KSTAT_STRLEN], name2[KSTAT_STRLEN];
75337fbbce5Scth 	char	mod1[KSTAT_STRLEN], mod2[KSTAT_STRLEN];
75437fbbce5Scth 
75537fbbce5Scth 	get_path_info(io1, mod1, &type1, &inst1, name1, sizeof (name1));
75637fbbce5Scth 	get_path_info(io2, mod2, &type2, &inst2, name2, sizeof (name2));
75737fbbce5Scth 	if ((!disk_or_partition(type1)) ||
75837fbbce5Scth 	    (!disk_or_partition(type2))) {
7597c478bd9Sstevel@tonic-gate 		/* neutral sort order between disk and part */
76037fbbce5Scth 		if (type1 < type2) {
7617c478bd9Sstevel@tonic-gate 			return (-1);
76237fbbce5Scth 		}
76337fbbce5Scth 		if (type1 > type2) {
7647c478bd9Sstevel@tonic-gate 			return (1);
7657c478bd9Sstevel@tonic-gate 		}
76637fbbce5Scth 	}
7677c478bd9Sstevel@tonic-gate 
7687c478bd9Sstevel@tonic-gate 	/* controller doesn't have ksp */
7697c478bd9Sstevel@tonic-gate 	if (io1->is_ksp && io2->is_ksp) {
77037fbbce5Scth 		if (strcmp(mod1, mod2) != 0) {
77137fbbce5Scth 			return (strcmp(mod1, mod2));
77237fbbce5Scth 		}
77337fbbce5Scth 		if (inst1 < inst2) {
7747c478bd9Sstevel@tonic-gate 			return (-1);
77537fbbce5Scth 		}
77637fbbce5Scth 		if (inst1 > inst2) {
7777c478bd9Sstevel@tonic-gate 			return (1);
77837fbbce5Scth 		}
7797c478bd9Sstevel@tonic-gate 	} else {
78037fbbce5Scth 		if (io1->is_id.id < io2->is_id.id) {
7817c478bd9Sstevel@tonic-gate 			return (-1);
78237fbbce5Scth 		}
78337fbbce5Scth 		if (io1->is_id.id > io2->is_id.id) {
78437fbbce5Scth 			return (1);
78537fbbce5Scth 		}
78637fbbce5Scth 	}
78737fbbce5Scth 
78837fbbce5Scth 	return (strcmp(name1, name2));
78937fbbce5Scth }
79037fbbce5Scth 
79137fbbce5Scth /* update the target reads and writes */
79237fbbce5Scth static void
79337fbbce5Scth update_target(struct iodev_snapshot *tgt, struct iodev_snapshot *path)
79437fbbce5Scth {
79537fbbce5Scth 	tgt->is_stats.reads += path->is_stats.reads;
79637fbbce5Scth 	tgt->is_stats.writes += path->is_stats.writes;
79737fbbce5Scth 	tgt->is_stats.nread += path->is_stats.nread;
79837fbbce5Scth 	tgt->is_stats.nwritten += path->is_stats.nwritten;
79937fbbce5Scth 	tgt->is_stats.wcnt += path->is_stats.wcnt;
80037fbbce5Scth 	tgt->is_stats.rcnt += path->is_stats.rcnt;
80137fbbce5Scth 
80237fbbce5Scth 	/*
80337fbbce5Scth 	 * Stash the t_delta in the crtime for use in show_disk
80437fbbce5Scth 	 * NOTE: this can't be done in show_disk because the
80537fbbce5Scth 	 * itl entry is removed for the old format
80637fbbce5Scth 	 */
80737fbbce5Scth 	tgt->is_crtime += hrtime_delta(path->is_crtime, path->is_snaptime);
80837fbbce5Scth 	tgt->is_snaptime += path->is_snaptime;
80937fbbce5Scth 	tgt->is_nr_children += 1;
81037fbbce5Scth }
81137fbbce5Scth 
81237fbbce5Scth /*
81337fbbce5Scth  * Create a new synthetic device entry of the specified type. The supported
81437fbbce5Scth  * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI.
81537fbbce5Scth  */
81637fbbce5Scth static struct iodev_snapshot *
81737fbbce5Scth make_extended_device(int type, struct iodev_snapshot *old)
81837fbbce5Scth {
81937fbbce5Scth 	struct iodev_snapshot	*tptr = NULL;
82037fbbce5Scth 	char			*ptr;
82137fbbce5Scth 	int			lun, tgt, initiator;
82237fbbce5Scth 	char			lun_name[KSTAT_STRLEN];
82337fbbce5Scth 	char			tgt_name[KSTAT_STRLEN];
82437fbbce5Scth 	char			initiator_name[KSTAT_STRLEN];
82537fbbce5Scth 
82637fbbce5Scth 	if (old == NULL)
82737fbbce5Scth 		return (NULL);
82837fbbce5Scth 	if (get_lti(old->is_name,
82937fbbce5Scth 	    lun_name, &lun, tgt_name, &tgt, initiator_name, &initiator) != 1) {
83037fbbce5Scth 		return (NULL);
83137fbbce5Scth 	}
83237fbbce5Scth 	tptr = safe_alloc(sizeof (*old));
83337fbbce5Scth 	bzero(tptr, sizeof (*old));
83437fbbce5Scth 	if (old->is_pretty != NULL) {
83537fbbce5Scth 		tptr->is_pretty = safe_alloc(strlen(old->is_pretty) + 1);
83637fbbce5Scth 		(void) strcpy(tptr->is_pretty, old->is_pretty);
83737fbbce5Scth 	}
83837fbbce5Scth 	bcopy(&old->is_parent_id, &tptr->is_parent_id,
83937fbbce5Scth 	    sizeof (old->is_parent_id));
84037fbbce5Scth 
84137fbbce5Scth 	tptr->is_type = type;
84237fbbce5Scth 
84337fbbce5Scth 	if (type == IODEV_IOPATH_LT) {
84437fbbce5Scth 		/* make new synthetic entry that is the LT */
84537fbbce5Scth 		/* set the id to the target id */
84637fbbce5Scth 		tptr->is_id.id = tgt;
84737fbbce5Scth 		(void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
84837fbbce5Scth 		    "%s%d", tgt_name, tgt);
84937fbbce5Scth 		(void) snprintf(tptr->is_name, sizeof (tptr->is_name),
85037fbbce5Scth 		    "%s%d.%s%d", lun_name, lun, tgt_name, tgt);
85137fbbce5Scth 
85237fbbce5Scth 		if (old->is_pretty) {
85337fbbce5Scth 			ptr = strrchr(tptr->is_pretty, '.');
85437fbbce5Scth 			if (ptr)
85537fbbce5Scth 				*ptr = '\0';
85637fbbce5Scth 		}
85737fbbce5Scth 	} else if (type == IODEV_IOPATH_LI) {
85837fbbce5Scth 		/* make new synthetic entry that is the LI */
85937fbbce5Scth 		/* set the id to the initiator number */
86037fbbce5Scth 		tptr->is_id.id = initiator;
86137fbbce5Scth 		(void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
86237fbbce5Scth 		    "%s%d", initiator_name, initiator);
86337fbbce5Scth 		(void) snprintf(tptr->is_name, sizeof (tptr->is_name),
86437fbbce5Scth 		    "%s%d.%s%d", lun_name, lun, initiator_name, initiator);
86537fbbce5Scth 
86637fbbce5Scth 		if (old->is_pretty) {
86737fbbce5Scth 			ptr = strchr(tptr->is_pretty, '.');
86837fbbce5Scth 			if (ptr)
86937fbbce5Scth 				(void) snprintf(ptr + 1,
87037fbbce5Scth 				    strlen(tptr->is_pretty) + 1,
87137fbbce5Scth 				    "%s%d", initiator_name, initiator);
87237fbbce5Scth 		}
87337fbbce5Scth 	}
87437fbbce5Scth 	return (tptr);
87537fbbce5Scth }
87637fbbce5Scth 
87737fbbce5Scth /*
87837fbbce5Scth  * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat
87937fbbce5Scth  * is found - traverse the children looking for the same initiator and sum
88037fbbce5Scth  * them up. Add an LI entry and delete all of the LTI entries with the same
88137fbbce5Scth  * initiator.
88237fbbce5Scth  */
88337fbbce5Scth static int
88437fbbce5Scth create_li_delete_lti(struct snapshot *ss, struct iodev_snapshot *list)
88537fbbce5Scth {
88637fbbce5Scth 	struct iodev_snapshot	*pos, *entry, *parent;
88737fbbce5Scth 	int			lun, tgt, initiator;
88837fbbce5Scth 	char			lun_name[KSTAT_STRLEN];
88937fbbce5Scth 	char			tgt_name[KSTAT_STRLEN];
89037fbbce5Scth 	char			initiator_name[KSTAT_STRLEN];
89137fbbce5Scth 	int			err;
89237fbbce5Scth 
89337fbbce5Scth 	for (entry = list; entry; entry = entry->is_next) {
89437fbbce5Scth 		if ((err = create_li_delete_lti(ss, entry->is_children)) != 0)
89537fbbce5Scth 			return (err);
89637fbbce5Scth 
89737fbbce5Scth 		if (entry->is_type == IODEV_IOPATH_LTI) {
89837fbbce5Scth 			parent = find_parent(ss, entry);
89937fbbce5Scth 			if (get_lti(entry->is_name, lun_name, &lun,
90037fbbce5Scth 			    tgt_name, &tgt, initiator_name, &initiator) != 1) {
9017c478bd9Sstevel@tonic-gate 				return (1);
9027c478bd9Sstevel@tonic-gate 			}
9037c478bd9Sstevel@tonic-gate 
90437fbbce5Scth 			pos = (parent == NULL) ? NULL : parent->is_children;
90537fbbce5Scth 			for (; pos; pos = pos->is_next) {
90637fbbce5Scth 				if (pos->is_id.id != -1 &&
90737fbbce5Scth 				    pos->is_id.id == initiator &&
90837fbbce5Scth 				    pos->is_type == IODEV_IOPATH_LI) {
90937fbbce5Scth 					/* found the same initiator */
91037fbbce5Scth 					update_target(pos, entry);
91137fbbce5Scth 					list_del(&parent->is_children, entry);
91237fbbce5Scth 					free_iodev(entry);
91337fbbce5Scth 					parent->is_nr_children--;
91437fbbce5Scth 					entry = pos;
91537fbbce5Scth 					break;
91637fbbce5Scth 				}
91737fbbce5Scth 			}
91837fbbce5Scth 
91937fbbce5Scth 			if (!pos) {
92037fbbce5Scth 				/* make the first LI entry */
92137fbbce5Scth 				pos = make_extended_device(
92237fbbce5Scth 				    IODEV_IOPATH_LI, entry);
92337fbbce5Scth 				update_target(pos, entry);
92437fbbce5Scth 
92537fbbce5Scth 				if (parent) {
92637fbbce5Scth 					insert_before(&parent->is_children,
92737fbbce5Scth 					    entry, pos);
92837fbbce5Scth 					list_del(&parent->is_children, entry);
92937fbbce5Scth 					free_iodev(entry);
93037fbbce5Scth 				} else {
93137fbbce5Scth 					insert_before(&ss->s_iodevs, entry,
93237fbbce5Scth 					    pos);
93337fbbce5Scth 					list_del(&ss->s_iodevs, entry);
93437fbbce5Scth 					free_iodev(entry);
93537fbbce5Scth 				}
93637fbbce5Scth 				entry = pos;
93737fbbce5Scth 			}
93837fbbce5Scth 		}
93937fbbce5Scth 	}
94037fbbce5Scth 	return (0);
94137fbbce5Scth }
94237fbbce5Scth 
94337fbbce5Scth /*
94437fbbce5Scth  * We have the LTI kstat, now add an entry for the LT that sums up all of
94537fbbce5Scth  * the LTI's with the same target(t).
94637fbbce5Scth  */
94737fbbce5Scth static int
94837fbbce5Scth create_lt(struct snapshot *ss, struct iodev_snapshot *list)
94937fbbce5Scth {
95037fbbce5Scth 	struct iodev_snapshot	*entry, *parent, *pos;
95137fbbce5Scth 	int			lun, tgt, initiator;
95237fbbce5Scth 	char			lun_name[KSTAT_STRLEN];
95337fbbce5Scth 	char			tgt_name[KSTAT_STRLEN];
95437fbbce5Scth 	char			initiator_name[KSTAT_STRLEN];
95537fbbce5Scth 	int			err;
95637fbbce5Scth 
95737fbbce5Scth 	for (entry = list; entry; entry = entry->is_next) {
95837fbbce5Scth 		if ((err = create_lt(ss, entry->is_children)) != 0)
95937fbbce5Scth 			return (err);
96037fbbce5Scth 
96137fbbce5Scth 		if (entry->is_type == IODEV_IOPATH_LTI) {
96237fbbce5Scth 			parent = find_parent(ss, entry);
96337fbbce5Scth 			if (get_lti(entry->is_name, lun_name, &lun,
96437fbbce5Scth 			    tgt_name, &tgt, initiator_name, &initiator) != 1) {
96537fbbce5Scth 				return (1);
96637fbbce5Scth 			}
96737fbbce5Scth 
96837fbbce5Scth 			pos = (parent == NULL) ? NULL : parent->is_children;
96937fbbce5Scth 			for (; pos; pos = pos->is_next) {
97037fbbce5Scth 				if (pos->is_id.id != -1 &&
97137fbbce5Scth 				    pos->is_id.id == tgt &&
97237fbbce5Scth 				    pos->is_type == IODEV_IOPATH_LT) {
97337fbbce5Scth 					/* found the same target */
97437fbbce5Scth 					update_target(pos, entry);
97537fbbce5Scth 					break;
97637fbbce5Scth 				}
97737fbbce5Scth 			}
97837fbbce5Scth 
97937fbbce5Scth 			if (!pos) {
98037fbbce5Scth 				pos = make_extended_device(
98137fbbce5Scth 				    IODEV_IOPATH_LT, entry);
98237fbbce5Scth 				update_target(pos, entry);
98337fbbce5Scth 
98437fbbce5Scth 				if (parent) {
98537fbbce5Scth 					insert_before(&parent->is_children,
98637fbbce5Scth 					    entry, pos);
98737fbbce5Scth 					parent->is_nr_children++;
98837fbbce5Scth 				} else {
98937fbbce5Scth 					insert_before(&ss->s_iodevs,
99037fbbce5Scth 					    entry, pos);
99137fbbce5Scth 				}
99237fbbce5Scth 			}
99337fbbce5Scth 		}
99437fbbce5Scth 	}
99537fbbce5Scth 	return (0);
99637fbbce5Scth }
99737fbbce5Scth 
99837fbbce5Scth /* Find the longest is_name field to aid formatting of output */
99937fbbce5Scth static int
100037fbbce5Scth iodevs_is_name_maxlen(struct iodev_snapshot *list)
100137fbbce5Scth {
100237fbbce5Scth 	struct iodev_snapshot	*entry;
100337fbbce5Scth 	int			max = 0, cmax, len;
100437fbbce5Scth 
100537fbbce5Scth 	for (entry = list; entry; entry = entry->is_next) {
100637fbbce5Scth 		cmax = iodevs_is_name_maxlen(entry->is_children);
100737fbbce5Scth 		max = (cmax > max) ? cmax : max;
100837fbbce5Scth 		len = strlen(entry->is_name);
100937fbbce5Scth 		max = (len > max) ? len : max;
101037fbbce5Scth 	}
101137fbbce5Scth 	return (max);
10127c478bd9Sstevel@tonic-gate }
10137c478bd9Sstevel@tonic-gate 
10147c478bd9Sstevel@tonic-gate int
10157c478bd9Sstevel@tonic-gate acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df)
10167c478bd9Sstevel@tonic-gate {
10177c478bd9Sstevel@tonic-gate 	kstat_t	*ksp;
10187c478bd9Sstevel@tonic-gate 	struct	iodev_snapshot *pos;
10197c478bd9Sstevel@tonic-gate 	struct	iodev_snapshot *list = NULL;
102037fbbce5Scth 	int	err = 0;
10217c478bd9Sstevel@tonic-gate 
10227c478bd9Sstevel@tonic-gate 	ss->s_nr_iodevs = 0;
102337fbbce5Scth 	ss->s_iodevs_is_name_maxlen = 0;
10247c478bd9Sstevel@tonic-gate 
1025a08731ecScth 	/*
1026a08731ecScth 	 * Call cleanup_iodevs_snapshot() so that a cache miss in
1027a08731ecScth 	 * lookup_ks_name() will result in a fresh snapshot.
1028a08731ecScth 	 */
1029a08731ecScth 	cleanup_iodevs_snapshot();
1030a08731ecScth 
10317c478bd9Sstevel@tonic-gate 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
10327c478bd9Sstevel@tonic-gate 		enum iodev_type type;
10337c478bd9Sstevel@tonic-gate 
10347c478bd9Sstevel@tonic-gate 		if (ksp->ks_type != KSTAT_TYPE_IO)
10357c478bd9Sstevel@tonic-gate 			continue;
10367c478bd9Sstevel@tonic-gate 
10377c478bd9Sstevel@tonic-gate 		/* e.g. "usb_byte_count" is not handled */
10387c478bd9Sstevel@tonic-gate 		if ((type = get_iodev_type(ksp)) == IODEV_UNKNOWN)
10397c478bd9Sstevel@tonic-gate 			continue;
10407c478bd9Sstevel@tonic-gate 
10417c478bd9Sstevel@tonic-gate 		if (df && !(type & df->if_allowed_types))
10427c478bd9Sstevel@tonic-gate 			continue;
10437c478bd9Sstevel@tonic-gate 
10447c478bd9Sstevel@tonic-gate 		if ((pos = malloc(sizeof (struct iodev_snapshot))) == NULL) {
10457c478bd9Sstevel@tonic-gate 			err = errno;
10467c478bd9Sstevel@tonic-gate 			goto out;
10477c478bd9Sstevel@tonic-gate 		}
10487c478bd9Sstevel@tonic-gate 
10497c478bd9Sstevel@tonic-gate 		(void) memset(pos, 0, sizeof (struct iodev_snapshot));
10507c478bd9Sstevel@tonic-gate 
10517c478bd9Sstevel@tonic-gate 		pos->is_type = type;
10527c478bd9Sstevel@tonic-gate 		pos->is_crtime = ksp->ks_crtime;
10537c478bd9Sstevel@tonic-gate 		pos->is_snaptime = ksp->ks_snaptime;
10547c478bd9Sstevel@tonic-gate 		pos->is_id.id = IODEV_NO_ID;
10557c478bd9Sstevel@tonic-gate 		pos->is_parent_id.id = IODEV_NO_ID;
10567c478bd9Sstevel@tonic-gate 		pos->is_ksp = ksp;
10577c478bd9Sstevel@tonic-gate 		pos->is_instance = ksp->ks_instance;
10587c478bd9Sstevel@tonic-gate 
10597c478bd9Sstevel@tonic-gate 		(void) strlcpy(pos->is_module, ksp->ks_module, KSTAT_STRLEN);
10607c478bd9Sstevel@tonic-gate 		(void) strlcpy(pos->is_name, ksp->ks_name, KSTAT_STRLEN);
10617c478bd9Sstevel@tonic-gate 		get_pretty_name(ss->s_types, pos, kc);
10627c478bd9Sstevel@tonic-gate 
10637c478bd9Sstevel@tonic-gate 		/*
10647c478bd9Sstevel@tonic-gate 		 * We must insert in sort order so e.g. vmstat -l
10657c478bd9Sstevel@tonic-gate 		 * chooses in order.
10667c478bd9Sstevel@tonic-gate 		 */
10677c478bd9Sstevel@tonic-gate 		insert_into(&list, pos);
10687c478bd9Sstevel@tonic-gate 	}
10697c478bd9Sstevel@tonic-gate 
10707c478bd9Sstevel@tonic-gate 	choose_iodevs(ss, list, df);
10717c478bd9Sstevel@tonic-gate 
10727c478bd9Sstevel@tonic-gate 	/* before acquire_stats for collate_controller()'s benefit */
10737c478bd9Sstevel@tonic-gate 	if (ss->s_types & SNAP_IODEV_ERRORS) {
10747c478bd9Sstevel@tonic-gate 		if ((err = acquire_iodev_errors(ss, kc)) != 0)
10757c478bd9Sstevel@tonic-gate 			goto out;
10767c478bd9Sstevel@tonic-gate 	}
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate 	if ((err = acquire_iodev_stats(ss->s_iodevs, kc)) != 0)
10797c478bd9Sstevel@tonic-gate 		goto out;
10807c478bd9Sstevel@tonic-gate 
108137fbbce5Scth 	if (ss->s_types & SNAP_IOPATHS_LTI) {
108237fbbce5Scth 		/*
108337fbbce5Scth 		 * -Y: kstats are LTI, need to create a synthetic LT
108437fbbce5Scth 		 * for -Y output.
108537fbbce5Scth 		 */
108637fbbce5Scth 		if ((err = create_lt(ss, ss->s_iodevs)) != 0) {
108737fbbce5Scth 			return (err);
108837fbbce5Scth 		}
108937fbbce5Scth 	}
109037fbbce5Scth 	if (ss->s_types & SNAP_IOPATHS_LI) {
109137fbbce5Scth 		/*
109237fbbce5Scth 		 * -X: kstats are LTI, need to create a synthetic LI and
109337fbbce5Scth 		 * delete the LTI for -X output
109437fbbce5Scth 		 */
109537fbbce5Scth 		if ((err = create_li_delete_lti(ss, ss->s_iodevs)) != 0) {
109637fbbce5Scth 			return (err);
109737fbbce5Scth 		}
109837fbbce5Scth 	}
109937fbbce5Scth 
110037fbbce5Scth 	/* determine width of longest is_name */
110137fbbce5Scth 	ss->s_iodevs_is_name_maxlen = iodevs_is_name_maxlen(ss->s_iodevs);
110237fbbce5Scth 
11037c478bd9Sstevel@tonic-gate 	err = 0;
11047c478bd9Sstevel@tonic-gate out:
11057c478bd9Sstevel@tonic-gate 	return (err);
11067c478bd9Sstevel@tonic-gate }
11077c478bd9Sstevel@tonic-gate 
11087c478bd9Sstevel@tonic-gate void
11097c478bd9Sstevel@tonic-gate free_iodev(struct iodev_snapshot *iodev)
11107c478bd9Sstevel@tonic-gate {
11117c478bd9Sstevel@tonic-gate 	while (iodev->is_children) {
11127c478bd9Sstevel@tonic-gate 		struct iodev_snapshot *tmp = iodev->is_children;
11137c478bd9Sstevel@tonic-gate 		iodev->is_children = iodev->is_children->is_next;
11147c478bd9Sstevel@tonic-gate 		free_iodev(tmp);
11157c478bd9Sstevel@tonic-gate 	}
11167c478bd9Sstevel@tonic-gate 
1117*4be70790Swroche 	if (iodev->avl_list) {
1118*4be70790Swroche 		avl_remove(iodev->avl_list, iodev);
1119*4be70790Swroche 		if (avl_numnodes(iodev->avl_list) == 0) {
1120*4be70790Swroche 			avl_destroy(iodev->avl_list);
1121*4be70790Swroche 			free(iodev->avl_list);
1122*4be70790Swroche 		}
1123*4be70790Swroche 	}
1124*4be70790Swroche 
11257c478bd9Sstevel@tonic-gate 	free(iodev->is_errors.ks_data);
11267c478bd9Sstevel@tonic-gate 	free(iodev->is_pretty);
11277c478bd9Sstevel@tonic-gate 	free(iodev->is_dname);
11287c478bd9Sstevel@tonic-gate 	free(iodev->is_devid);
11297c478bd9Sstevel@tonic-gate 	free(iodev);
11307c478bd9Sstevel@tonic-gate }
1131