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 /*
22fff11ba8SJonathan Cowper-Andrewes  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate #include "statcommon.h"
267c478bd9Sstevel@tonic-gate #include "dsr.h"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/dklabel.h>
297c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
307c478bd9Sstevel@tonic-gate #include <stdlib.h>
317c478bd9Sstevel@tonic-gate #include <stdarg.h>
325c5f1371SRichard Lowe #include <stddef.h>
337c478bd9Sstevel@tonic-gate #include <unistd.h>
347c478bd9Sstevel@tonic-gate #include <strings.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <limits.h>
377c478bd9Sstevel@tonic-gate 
387c478bd9Sstevel@tonic-gate static void insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev);
397c478bd9Sstevel@tonic-gate 
407c478bd9Sstevel@tonic-gate static struct iodev_snapshot *
make_controller(int cid)417c478bd9Sstevel@tonic-gate make_controller(int cid)
427c478bd9Sstevel@tonic-gate {
437c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *new;
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate 	new = safe_alloc(sizeof (struct iodev_snapshot));
467c478bd9Sstevel@tonic-gate 	(void) memset(new, 0, sizeof (struct iodev_snapshot));
477c478bd9Sstevel@tonic-gate 	new->is_type = IODEV_CONTROLLER;
487c478bd9Sstevel@tonic-gate 	new->is_id.id = cid;
497c478bd9Sstevel@tonic-gate 	new->is_parent_id.id = IODEV_NO_ID;
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate 	(void) snprintf(new->is_name, sizeof (new->is_name), "c%d", cid);
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate 	return (new);
547c478bd9Sstevel@tonic-gate }
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate static struct iodev_snapshot *
find_iodev_by_name(struct iodev_snapshot * list,const char * name)577c478bd9Sstevel@tonic-gate find_iodev_by_name(struct iodev_snapshot *list, const char *name)
587c478bd9Sstevel@tonic-gate {
597c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos;
607c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos2;
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate 	for (pos = list; pos; pos = pos->is_next) {
637c478bd9Sstevel@tonic-gate 		if (strcmp(pos->is_name, name) == 0)
647c478bd9Sstevel@tonic-gate 			return (pos);
657c478bd9Sstevel@tonic-gate 
667c478bd9Sstevel@tonic-gate 		pos2 = find_iodev_by_name(pos->is_children, name);
677c478bd9Sstevel@tonic-gate 		if (pos2 != NULL)
687c478bd9Sstevel@tonic-gate 			return (pos2);
697c478bd9Sstevel@tonic-gate 	}
707c478bd9Sstevel@tonic-gate 
717c478bd9Sstevel@tonic-gate 	return (NULL);
727c478bd9Sstevel@tonic-gate }
737c478bd9Sstevel@tonic-gate 
747c478bd9Sstevel@tonic-gate static enum iodev_type
parent_iodev_type(enum iodev_type type)757c478bd9Sstevel@tonic-gate parent_iodev_type(enum iodev_type type)
767c478bd9Sstevel@tonic-gate {
777c478bd9Sstevel@tonic-gate 	switch (type) {
787c478bd9Sstevel@tonic-gate 		case IODEV_CONTROLLER: return (0);
7937fbbce5Scth 		case IODEV_IOPATH_LT: return (0);
8037fbbce5Scth 		case IODEV_IOPATH_LI: return (0);
817c478bd9Sstevel@tonic-gate 		case IODEV_NFS: return (0);
827c478bd9Sstevel@tonic-gate 		case IODEV_TAPE: return (0);
8337fbbce5Scth 		case IODEV_IOPATH_LTI: return (IODEV_DISK);
847c478bd9Sstevel@tonic-gate 		case IODEV_DISK: return (IODEV_CONTROLLER);
857c478bd9Sstevel@tonic-gate 		case IODEV_PARTITION: return (IODEV_DISK);
867c478bd9Sstevel@tonic-gate 	}
877c478bd9Sstevel@tonic-gate 	return (IODEV_UNKNOWN);
887c478bd9Sstevel@tonic-gate }
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate static int
id_match(struct iodev_id * id1,struct iodev_id * id2)917c478bd9Sstevel@tonic-gate id_match(struct iodev_id *id1, struct iodev_id *id2)
927c478bd9Sstevel@tonic-gate {
937c478bd9Sstevel@tonic-gate 	return (id1->id == id2->id &&
944be70790Swroche 	    strcmp(id1->tid, id2->tid) == 0);
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate 
977c478bd9Sstevel@tonic-gate static struct iodev_snapshot *
find_parent(struct snapshot * ss,struct iodev_snapshot * iodev)987c478bd9Sstevel@tonic-gate find_parent(struct snapshot *ss, struct iodev_snapshot *iodev)
997c478bd9Sstevel@tonic-gate {
1007c478bd9Sstevel@tonic-gate 	enum iodev_type parent_type = parent_iodev_type(iodev->is_type);
1017c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos;
1027c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos2;
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate 	if (parent_type == 0 || parent_type == IODEV_UNKNOWN)
1057c478bd9Sstevel@tonic-gate 		return (NULL);
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	if (iodev->is_parent_id.id == IODEV_NO_ID &&
1087c478bd9Sstevel@tonic-gate 	    iodev->is_parent_id.tid[0] == '\0')
1097c478bd9Sstevel@tonic-gate 		return (NULL);
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate 	if (parent_type == IODEV_CONTROLLER) {
1127c478bd9Sstevel@tonic-gate 		for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
1137c478bd9Sstevel@tonic-gate 			if (pos->is_type != IODEV_CONTROLLER)
1147c478bd9Sstevel@tonic-gate 				continue;
1157c478bd9Sstevel@tonic-gate 			if (pos->is_id.id != iodev->is_parent_id.id)
1167c478bd9Sstevel@tonic-gate 				continue;
1177c478bd9Sstevel@tonic-gate 			return (pos);
1187c478bd9Sstevel@tonic-gate 		}
1197c478bd9Sstevel@tonic-gate 
1207c478bd9Sstevel@tonic-gate 		if (!(ss->s_types & SNAP_CONTROLLERS))
1217c478bd9Sstevel@tonic-gate 			return (NULL);
1227c478bd9Sstevel@tonic-gate 
1237c478bd9Sstevel@tonic-gate 		pos = make_controller(iodev->is_parent_id.id);
1247c478bd9Sstevel@tonic-gate 		insert_iodev(ss, pos);
1257c478bd9Sstevel@tonic-gate 		return (pos);
1267c478bd9Sstevel@tonic-gate 	}
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 	/* IODEV_DISK parent */
1297c478bd9Sstevel@tonic-gate 	for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
1307c478bd9Sstevel@tonic-gate 		if (id_match(&iodev->is_parent_id, &pos->is_id) &&
1317c478bd9Sstevel@tonic-gate 		    pos->is_type == IODEV_DISK)
1327c478bd9Sstevel@tonic-gate 			return (pos);
1337c478bd9Sstevel@tonic-gate 		if (pos->is_type != IODEV_CONTROLLER)
1347c478bd9Sstevel@tonic-gate 			continue;
1357c478bd9Sstevel@tonic-gate 		for (pos2 = pos->is_children; pos2; pos2 = pos2->is_next) {
1367c478bd9Sstevel@tonic-gate 			if (pos2->is_type != IODEV_DISK)
1377c478bd9Sstevel@tonic-gate 				continue;
1387c478bd9Sstevel@tonic-gate 			if (id_match(&iodev->is_parent_id, &pos2->is_id))
1397c478bd9Sstevel@tonic-gate 				return (pos2);
1407c478bd9Sstevel@tonic-gate 		}
1417c478bd9Sstevel@tonic-gate 	}
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 	return (NULL);
1447c478bd9Sstevel@tonic-gate }
1457c478bd9Sstevel@tonic-gate 
1464be70790Swroche /*
1474be70790Swroche  * Introduce an index into the list to speed up insert_into looking for the
1484be70790Swroche  * right position in the list. This index is an AVL tree of all the
1494be70790Swroche  * iodev_snapshot in the list.
1504be70790Swroche  */
1514be70790Swroche static int
avl_iodev_cmp(const void * is1,const void * is2)1524be70790Swroche avl_iodev_cmp(const void* is1, const void* is2)
1534be70790Swroche {
1544be70790Swroche 	int c = iodev_cmp((struct iodev_snapshot *)is1,
1554be70790Swroche 	    (struct iodev_snapshot *)is2);
1564be70790Swroche 
1574be70790Swroche 	if (c > 0)
1584be70790Swroche 		return (1);
1594be70790Swroche 
1604be70790Swroche 	if (c < 0)
1614be70790Swroche 		return (-1);
1624be70790Swroche 
1634be70790Swroche 	return (0);
1644be70790Swroche }
1654be70790Swroche 
1664be70790Swroche static void
ix_new_list(struct iodev_snapshot * elem)1674be70790Swroche ix_new_list(struct iodev_snapshot *elem)
1684be70790Swroche {
1694be70790Swroche 	avl_tree_t *l = malloc(sizeof (avl_tree_t));
1704be70790Swroche 
1714be70790Swroche 	elem->avl_list = l;
1724be70790Swroche 	if (l == NULL)
1734be70790Swroche 		return;
1744be70790Swroche 
1754be70790Swroche 	avl_create(l, avl_iodev_cmp, sizeof (struct iodev_snapshot),
1764be70790Swroche 	    offsetof(struct iodev_snapshot, avl_link));
1774be70790Swroche 
1784be70790Swroche 	avl_add(l, elem);
1794be70790Swroche }
1804be70790Swroche 
1814be70790Swroche static void
ix_list_del(struct iodev_snapshot * elem)1824be70790Swroche ix_list_del(struct iodev_snapshot *elem)
1834be70790Swroche {
1844be70790Swroche 	avl_tree_t *l = elem->avl_list;
1854be70790Swroche 
1864be70790Swroche 	if (l == NULL)
1874be70790Swroche 		return;
1884be70790Swroche 
1894be70790Swroche 	elem->avl_list = NULL;
1904be70790Swroche 
1914be70790Swroche 	avl_remove(l, elem);
1924be70790Swroche 	if (avl_numnodes(l) == 0) {
1934be70790Swroche 		avl_destroy(l);
1944be70790Swroche 		free(l);
1954be70790Swroche 	}
1964be70790Swroche }
1974be70790Swroche 
1984be70790Swroche static void
ix_insert_here(struct iodev_snapshot * pos,struct iodev_snapshot * elem,int ba)1994be70790Swroche ix_insert_here(struct iodev_snapshot *pos, struct iodev_snapshot *elem, int ba)
2004be70790Swroche {
2014be70790Swroche 	avl_tree_t *l = pos->avl_list;
2024be70790Swroche 	elem->avl_list = l;
2034be70790Swroche 
2044be70790Swroche 	if (l == NULL)
2054be70790Swroche 		return;
2064be70790Swroche 
2074be70790Swroche 	avl_insert_here(l, elem, pos, ba);
2084be70790Swroche }
2094be70790Swroche 
2107c478bd9Sstevel@tonic-gate static void
list_del(struct iodev_snapshot ** list,struct iodev_snapshot * pos)2117c478bd9Sstevel@tonic-gate list_del(struct iodev_snapshot **list, struct iodev_snapshot *pos)
2127c478bd9Sstevel@tonic-gate {
2134be70790Swroche 	ix_list_del(pos);
2144be70790Swroche 
2157c478bd9Sstevel@tonic-gate 	if (*list == pos)
2167c478bd9Sstevel@tonic-gate 		*list = pos->is_next;
2177c478bd9Sstevel@tonic-gate 	if (pos->is_next)
2187c478bd9Sstevel@tonic-gate 		pos->is_next->is_prev = pos->is_prev;
2197c478bd9Sstevel@tonic-gate 	if (pos->is_prev)
2207c478bd9Sstevel@tonic-gate 		pos->is_prev->is_next = pos->is_next;
2217c478bd9Sstevel@tonic-gate 	pos->is_prev = pos->is_next = NULL;
2227c478bd9Sstevel@tonic-gate }
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate static void
insert_before(struct iodev_snapshot ** list,struct iodev_snapshot * pos,struct iodev_snapshot * new)2257c478bd9Sstevel@tonic-gate insert_before(struct iodev_snapshot **list, struct iodev_snapshot *pos,
2267c478bd9Sstevel@tonic-gate     struct iodev_snapshot *new)
2277c478bd9Sstevel@tonic-gate {
2287c478bd9Sstevel@tonic-gate 	if (pos == NULL) {
2297c478bd9Sstevel@tonic-gate 		new->is_prev = new->is_next = NULL;
2307c478bd9Sstevel@tonic-gate 		*list = new;
2314be70790Swroche 		ix_new_list(new);
2327c478bd9Sstevel@tonic-gate 		return;
2337c478bd9Sstevel@tonic-gate 	}
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 	new->is_next = pos;
2367c478bd9Sstevel@tonic-gate 	new->is_prev = pos->is_prev;
2377c478bd9Sstevel@tonic-gate 	if (pos->is_prev)
2387c478bd9Sstevel@tonic-gate 		pos->is_prev->is_next = new;
2397c478bd9Sstevel@tonic-gate 	else
2407c478bd9Sstevel@tonic-gate 		*list = new;
2417c478bd9Sstevel@tonic-gate 	pos->is_prev = new;
2424be70790Swroche 
2434be70790Swroche 	ix_insert_here(pos, new, AVL_BEFORE);
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate static void
insert_after(struct iodev_snapshot ** list,struct iodev_snapshot * pos,struct iodev_snapshot * new)2477c478bd9Sstevel@tonic-gate insert_after(struct iodev_snapshot **list, struct iodev_snapshot *pos,
2487c478bd9Sstevel@tonic-gate     struct iodev_snapshot *new)
2497c478bd9Sstevel@tonic-gate {
2507c478bd9Sstevel@tonic-gate 	if (pos == NULL) {
2517c478bd9Sstevel@tonic-gate 		new->is_prev = new->is_next = NULL;
2527c478bd9Sstevel@tonic-gate 		*list = new;
2534be70790Swroche 		ix_new_list(new);
2547c478bd9Sstevel@tonic-gate 		return;
2557c478bd9Sstevel@tonic-gate 	}
2567c478bd9Sstevel@tonic-gate 
2577c478bd9Sstevel@tonic-gate 	new->is_next = pos->is_next;
2587c478bd9Sstevel@tonic-gate 	new->is_prev = pos;
2597c478bd9Sstevel@tonic-gate 	if (pos->is_next)
2607c478bd9Sstevel@tonic-gate 		pos->is_next->is_prev = new;
2617c478bd9Sstevel@tonic-gate 	pos->is_next = new;
2624be70790Swroche 
2634be70790Swroche 	ix_insert_here(pos, new, AVL_AFTER);
2647c478bd9Sstevel@tonic-gate }
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate static void
insert_into(struct iodev_snapshot ** list,struct iodev_snapshot * iodev)2677c478bd9Sstevel@tonic-gate insert_into(struct iodev_snapshot **list, struct iodev_snapshot *iodev)
2687c478bd9Sstevel@tonic-gate {
2697c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *tmp = *list;
2704be70790Swroche 	avl_tree_t *l;
2714be70790Swroche 	void *p;
2724be70790Swroche 	avl_index_t where;
2734be70790Swroche 
2747c478bd9Sstevel@tonic-gate 	if (*list == NULL) {
2757c478bd9Sstevel@tonic-gate 		*list = iodev;
2764be70790Swroche 		ix_new_list(iodev);
2777c478bd9Sstevel@tonic-gate 		return;
2787c478bd9Sstevel@tonic-gate 	}
2797c478bd9Sstevel@tonic-gate 
2804be70790Swroche 	/*
2814be70790Swroche 	 * Optimize the search: instead of walking the entire list
2824be70790Swroche 	 * (which can contain thousands of nodes), search in the AVL
2834be70790Swroche 	 * tree the nearest node and reposition the startup point to
2844be70790Swroche 	 * this node rather than always starting from the beginning
2854be70790Swroche 	 * of the list.
2864be70790Swroche 	 */
2874be70790Swroche 	l = tmp->avl_list;
2884be70790Swroche 	if (l != NULL) {
2894be70790Swroche 		p = avl_find(l, iodev, &where);
2904be70790Swroche 		if (p == NULL) {
2914be70790Swroche 			p = avl_nearest(l, where, AVL_BEFORE);
2924be70790Swroche 		}
2934be70790Swroche 		if (p != NULL) {
2944be70790Swroche 			tmp = (struct iodev_snapshot *)p;
2954be70790Swroche 		}
2964be70790Swroche 	}
2974be70790Swroche 
2987c478bd9Sstevel@tonic-gate 	for (;;) {
2997c478bd9Sstevel@tonic-gate 		if (iodev_cmp(tmp, iodev) > 0) {
3007c478bd9Sstevel@tonic-gate 			insert_before(list, tmp, iodev);
3017c478bd9Sstevel@tonic-gate 			return;
3027c478bd9Sstevel@tonic-gate 		}
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 		if (tmp->is_next == NULL)
3057c478bd9Sstevel@tonic-gate 			break;
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 		tmp = tmp->is_next;
3087c478bd9Sstevel@tonic-gate 	}
3097c478bd9Sstevel@tonic-gate 
3107c478bd9Sstevel@tonic-gate 	insert_after(list, tmp, iodev);
3117c478bd9Sstevel@tonic-gate }
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate static int
disk_or_partition(enum iodev_type type)3147c478bd9Sstevel@tonic-gate disk_or_partition(enum iodev_type type)
3157c478bd9Sstevel@tonic-gate {
3167c478bd9Sstevel@tonic-gate 	return (type == IODEV_DISK || type == IODEV_PARTITION);
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate 
31937fbbce5Scth static int
disk_or_partition_or_iopath(enum iodev_type type)32037fbbce5Scth disk_or_partition_or_iopath(enum iodev_type type)
32137fbbce5Scth {
32237fbbce5Scth 	return (type == IODEV_DISK || type == IODEV_PARTITION ||
32337fbbce5Scth 	    type == IODEV_IOPATH_LTI);
32437fbbce5Scth }
32537fbbce5Scth 
3267c478bd9Sstevel@tonic-gate static void
insert_iodev(struct snapshot * ss,struct iodev_snapshot * iodev)3277c478bd9Sstevel@tonic-gate insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev)
3287c478bd9Sstevel@tonic-gate {
3297c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *parent = find_parent(ss, iodev);
3307c478bd9Sstevel@tonic-gate 	struct iodev_snapshot **list;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	if (parent != NULL) {
3337c478bd9Sstevel@tonic-gate 		list = &parent->is_children;
3347c478bd9Sstevel@tonic-gate 		parent->is_nr_children++;
3357c478bd9Sstevel@tonic-gate 	} else {
3367c478bd9Sstevel@tonic-gate 		list = &ss->s_iodevs;
3377c478bd9Sstevel@tonic-gate 		ss->s_nr_iodevs++;
3387c478bd9Sstevel@tonic-gate 	}
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 	insert_into(list, iodev);
3417c478bd9Sstevel@tonic-gate }
3427c478bd9Sstevel@tonic-gate 
34337fbbce5Scth /* return 1 if dev passes filter */
3447c478bd9Sstevel@tonic-gate static int
iodev_match(struct iodev_snapshot * dev,struct iodev_filter * df)3457c478bd9Sstevel@tonic-gate iodev_match(struct iodev_snapshot *dev, struct iodev_filter *df)
3467c478bd9Sstevel@tonic-gate {
34737fbbce5Scth 	int	is_floppy = (strncmp(dev->is_name, "fd", 2) == 0);
34837fbbce5Scth 	char	*isn, *ispn, *ifn;
34937fbbce5Scth 	char	*path;
35037fbbce5Scth 	int	ifnl;
35137fbbce5Scth 	size_t	i;
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 	/* no filter, pass */
3547c478bd9Sstevel@tonic-gate 	if (df == NULL)
35537fbbce5Scth 		return (1);		/* pass */
3567c478bd9Sstevel@tonic-gate 
3577c478bd9Sstevel@tonic-gate 	/* no filtered names, pass if not floppy and skipped */
358*f9409f99SToomas Soome 	if (df->if_nr_names == 0)
3597c478bd9Sstevel@tonic-gate 		return (!(df->if_skip_floppy && is_floppy));
3607c478bd9Sstevel@tonic-gate 
36137fbbce5Scth 	isn = dev->is_name;
36237fbbce5Scth 	ispn = dev->is_pretty;
3637c478bd9Sstevel@tonic-gate 	for (i = 0; i < df->if_nr_names; i++) {
36437fbbce5Scth 		ifn = df->if_names[i];
36537fbbce5Scth 		ifnl = strlen(ifn);
36637fbbce5Scth 		path = strchr(ifn, '.');
36737fbbce5Scth 
36837fbbce5Scth 		if ((strcmp(isn, ifn) == 0) ||
36937fbbce5Scth 		    (ispn && (strcmp(ispn, ifn) == 0)))
37037fbbce5Scth 			return (1);	/* pass */
37137fbbce5Scth 
37237fbbce5Scth 		/* if filter is a path allow partial match */
37337fbbce5Scth 		if (path &&
37437fbbce5Scth 		    ((strncmp(isn, ifn, ifnl) == 0) ||
37537fbbce5Scth 		    (ispn && (strncmp(ispn, ifn, ifnl) == 0))))
37637fbbce5Scth 			return (1);	/* pass */
3777c478bd9Sstevel@tonic-gate 	}
3787c478bd9Sstevel@tonic-gate 
37937fbbce5Scth 	return (0);			/* fail */
38037fbbce5Scth }
38137fbbce5Scth 
38237fbbce5Scth /* return 1 if path is an mpxio path associated with dev */
38337fbbce5Scth static int
iodev_path_match(struct iodev_snapshot * dev,struct iodev_snapshot * path)38437fbbce5Scth iodev_path_match(struct iodev_snapshot *dev, struct iodev_snapshot *path)
38537fbbce5Scth {
38637fbbce5Scth 	char	*dn, *pn;
38737fbbce5Scth 	int	dnl;
38837fbbce5Scth 
38937fbbce5Scth 	dn = dev->is_name;
39037fbbce5Scth 	pn = path->is_name;
39137fbbce5Scth 	dnl = strlen(dn);
39237fbbce5Scth 
39337fbbce5Scth 	if ((strncmp(pn, dn, dnl) == 0) && (pn[dnl] == '.'))
39437fbbce5Scth 		return (1);			/* yes */
39537fbbce5Scth 
39637fbbce5Scth 	return (0);				/* no */
3977c478bd9Sstevel@tonic-gate }
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate /* select which I/O devices to collect stats for */
4007c478bd9Sstevel@tonic-gate static void
choose_iodevs(struct snapshot * ss,struct iodev_snapshot * iodevs,struct iodev_filter * df)4017c478bd9Sstevel@tonic-gate choose_iodevs(struct snapshot *ss, struct iodev_snapshot *iodevs,
4027c478bd9Sstevel@tonic-gate     struct iodev_filter *df)
4037c478bd9Sstevel@tonic-gate {
40437fbbce5Scth 	struct iodev_snapshot	*pos, *ppos, *tmp, *ptmp;
40537fbbce5Scth 	int			nr_iodevs;
40637fbbce5Scth 	int			nr_iodevs_orig;
40737fbbce5Scth 
40837fbbce5Scth 	nr_iodevs = df ? df->if_max_iodevs : UNLIMITED_IODEVS;
40937fbbce5Scth 	nr_iodevs_orig = nr_iodevs;
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	if (nr_iodevs == UNLIMITED_IODEVS)
4127c478bd9Sstevel@tonic-gate 		nr_iodevs = INT_MAX;
4137c478bd9Sstevel@tonic-gate 
41437fbbce5Scth 	/* add the full matches */
41537fbbce5Scth 	pos = iodevs;
4167c478bd9Sstevel@tonic-gate 	while (pos && nr_iodevs) {
41737fbbce5Scth 		tmp = pos;
4187c478bd9Sstevel@tonic-gate 		pos = pos->is_next;
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 		if (!iodev_match(tmp, df))
42137fbbce5Scth 			continue;	/* failed full match */
4227c478bd9Sstevel@tonic-gate 
4237c478bd9Sstevel@tonic-gate 		list_del(&iodevs, tmp);
4247c478bd9Sstevel@tonic-gate 		insert_iodev(ss, tmp);
4257c478bd9Sstevel@tonic-gate 
42637fbbce5Scth 		/*
42737fbbce5Scth 		 * Add all mpxio paths associated with match above. Added
42837fbbce5Scth 		 * paths don't count against nr_iodevs.
42937fbbce5Scth 		 */
43037fbbce5Scth 		if (strchr(tmp->is_name, '.') == NULL) {
43137fbbce5Scth 		ppos = iodevs;
43237fbbce5Scth 		while (ppos) {
43337fbbce5Scth 			ptmp = ppos;
43437fbbce5Scth 			ppos = ppos->is_next;
43537fbbce5Scth 
43637fbbce5Scth 			if (!iodev_path_match(tmp, ptmp))
43737fbbce5Scth 				continue;	/* not an mpxio path */
43837fbbce5Scth 
43937fbbce5Scth 			list_del(&iodevs, ptmp);
44037fbbce5Scth 			insert_iodev(ss, ptmp);
44137fbbce5Scth 			if (pos == ptmp)
44237fbbce5Scth 				pos = ppos;
44337fbbce5Scth 		}
44437fbbce5Scth 		}
4457c478bd9Sstevel@tonic-gate 
44637fbbce5Scth 		nr_iodevs--;
44737fbbce5Scth 	}
4487c478bd9Sstevel@tonic-gate 
44937fbbce5Scth 	/*
45037fbbce5Scth 	 * If we had a filter, and *nothing* passed the filter then we
45137fbbce5Scth 	 * don't want to fill the  remaining slots - it is just confusing
45237fbbce5Scth 	 * if we don that, it makes it look like the filter code is broken.
45337fbbce5Scth 	 */
454*f9409f99SToomas Soome 	if ((df->if_nr_names == 0) || (nr_iodevs != nr_iodevs_orig)) {
45537fbbce5Scth 		/* now insert any iodevs into the remaining slots */
45637fbbce5Scth 		pos = iodevs;
45737fbbce5Scth 		while (pos && nr_iodevs) {
45837fbbce5Scth 			tmp = pos;
45937fbbce5Scth 			pos = pos->is_next;
46037fbbce5Scth 
4619a25c41fSJohn Levon 			if (df->if_skip_floppy &&
4624be70790Swroche 			    strncmp(tmp->is_name, "fd", 2) == 0)
46337fbbce5Scth 				continue;
4647c478bd9Sstevel@tonic-gate 
46537fbbce5Scth 			list_del(&iodevs, tmp);
46637fbbce5Scth 			insert_iodev(ss, tmp);
4677c478bd9Sstevel@tonic-gate 
46837fbbce5Scth 			--nr_iodevs;
46937fbbce5Scth 		}
4707c478bd9Sstevel@tonic-gate 	}
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	/* clear the unwanted ones */
4737c478bd9Sstevel@tonic-gate 	pos = iodevs;
4747c478bd9Sstevel@tonic-gate 	while (pos) {
4757c478bd9Sstevel@tonic-gate 		struct iodev_snapshot *tmp = pos;
4767c478bd9Sstevel@tonic-gate 		pos = pos->is_next;
4777c478bd9Sstevel@tonic-gate 		free_iodev(tmp);
4787c478bd9Sstevel@tonic-gate 	}
4797c478bd9Sstevel@tonic-gate }
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate static int
collate_controller(struct iodev_snapshot * controller,struct iodev_snapshot * disk)4827c478bd9Sstevel@tonic-gate collate_controller(struct iodev_snapshot *controller,
4837c478bd9Sstevel@tonic-gate     struct iodev_snapshot *disk)
4847c478bd9Sstevel@tonic-gate {
4857c478bd9Sstevel@tonic-gate 	controller->is_stats.nread += disk->is_stats.nread;
4867c478bd9Sstevel@tonic-gate 	controller->is_stats.nwritten += disk->is_stats.nwritten;
4877c478bd9Sstevel@tonic-gate 	controller->is_stats.reads += disk->is_stats.reads;
4887c478bd9Sstevel@tonic-gate 	controller->is_stats.writes += disk->is_stats.writes;
4897c478bd9Sstevel@tonic-gate 	controller->is_stats.wtime += disk->is_stats.wtime;
4907c478bd9Sstevel@tonic-gate 	controller->is_stats.wlentime += disk->is_stats.wlentime;
4917c478bd9Sstevel@tonic-gate 	controller->is_stats.rtime += disk->is_stats.rtime;
4927c478bd9Sstevel@tonic-gate 	controller->is_stats.rlentime += disk->is_stats.rlentime;
4937c478bd9Sstevel@tonic-gate 	controller->is_crtime += disk->is_crtime;
4947c478bd9Sstevel@tonic-gate 	controller->is_snaptime += disk->is_snaptime;
4957c478bd9Sstevel@tonic-gate 	if (kstat_add(&disk->is_errors, &controller->is_errors))
4967c478bd9Sstevel@tonic-gate 		return (errno);
4977c478bd9Sstevel@tonic-gate 	return (0);
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate 
5007c478bd9Sstevel@tonic-gate static int
acquire_iodev_stats(struct iodev_snapshot * list,kstat_ctl_t * kc)5017c478bd9Sstevel@tonic-gate acquire_iodev_stats(struct iodev_snapshot *list, kstat_ctl_t *kc)
5027c478bd9Sstevel@tonic-gate {
5037c478bd9Sstevel@tonic-gate 	struct iodev_snapshot *pos;
5047c478bd9Sstevel@tonic-gate 	int err = 0;
5057c478bd9Sstevel@tonic-gate 
5067c478bd9Sstevel@tonic-gate 	for (pos = list; pos; pos = pos->is_next) {
5077c478bd9Sstevel@tonic-gate 		/* controllers don't have stats (yet) */
5087c478bd9Sstevel@tonic-gate 		if (pos->is_ksp != NULL) {
5097c478bd9Sstevel@tonic-gate 			if (kstat_read(kc, pos->is_ksp, &pos->is_stats) == -1)
5107c478bd9Sstevel@tonic-gate 				return (errno);
5117c478bd9Sstevel@tonic-gate 			/* make sure crtime/snaptime is updated */
5127c478bd9Sstevel@tonic-gate 			pos->is_crtime = pos->is_ksp->ks_crtime;
5137c478bd9Sstevel@tonic-gate 			pos->is_snaptime = pos->is_ksp->ks_snaptime;
5147c478bd9Sstevel@tonic-gate 		}
5157c478bd9Sstevel@tonic-gate 
5167c478bd9Sstevel@tonic-gate 		if ((err = acquire_iodev_stats(pos->is_children, kc)))
5177c478bd9Sstevel@tonic-gate 			return (err);
5187c478bd9Sstevel@tonic-gate 
5197c478bd9Sstevel@tonic-gate 		if (pos->is_type == IODEV_CONTROLLER) {
5207c478bd9Sstevel@tonic-gate 			struct iodev_snapshot *pos2 = pos->is_children;
5217c478bd9Sstevel@tonic-gate 
5227c478bd9Sstevel@tonic-gate 			for (; pos2; pos2 = pos2->is_next) {
5237c478bd9Sstevel@tonic-gate 				if ((err = collate_controller(pos, pos2)))
5247c478bd9Sstevel@tonic-gate 					return (err);
5257c478bd9Sstevel@tonic-gate 			}
5267c478bd9Sstevel@tonic-gate 		}
5277c478bd9Sstevel@tonic-gate 	}
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	return (0);
5307c478bd9Sstevel@tonic-gate }
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate static int
acquire_iodev_errors(struct snapshot * ss,kstat_ctl_t * kc)5337c478bd9Sstevel@tonic-gate acquire_iodev_errors(struct snapshot *ss, kstat_ctl_t *kc)
5347c478bd9Sstevel@tonic-gate {
5357c478bd9Sstevel@tonic-gate 	kstat_t *ksp;
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
5387c478bd9Sstevel@tonic-gate 		char kstat_name[KSTAT_STRLEN];
5397c478bd9Sstevel@tonic-gate 		char *dname = kstat_name;
5407c478bd9Sstevel@tonic-gate 		char *ename = ksp->ks_name;
5417c478bd9Sstevel@tonic-gate 		struct iodev_snapshot *iodev;
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 		if (ksp->ks_type != KSTAT_TYPE_NAMED)
5447c478bd9Sstevel@tonic-gate 			continue;
5457c478bd9Sstevel@tonic-gate 		if (strncmp(ksp->ks_class, "device_error", 12) != 0 &&
5467c478bd9Sstevel@tonic-gate 		    strncmp(ksp->ks_class, "iopath_error", 12) != 0)
5477c478bd9Sstevel@tonic-gate 			continue;
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 		/*
5507c478bd9Sstevel@tonic-gate 		 * Some drivers may not follow the naming convention
5517c478bd9Sstevel@tonic-gate 		 * for error kstats (i.e., drivername,err) so
5527c478bd9Sstevel@tonic-gate 		 * be sure we don't walk off the end.
5537c478bd9Sstevel@tonic-gate 		 */
5547c478bd9Sstevel@tonic-gate 		while (*ename && *ename != ',') {
5557c478bd9Sstevel@tonic-gate 			*dname = *ename;
5567c478bd9Sstevel@tonic-gate 			dname++;
5577c478bd9Sstevel@tonic-gate 			ename++;
5587c478bd9Sstevel@tonic-gate 		}
5597c478bd9Sstevel@tonic-gate 		*dname = '\0';
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 		iodev = find_iodev_by_name(ss->s_iodevs, kstat_name);
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate 		if (iodev == NULL)
5647c478bd9Sstevel@tonic-gate 			continue;
5657c478bd9Sstevel@tonic-gate 
5667c478bd9Sstevel@tonic-gate 		if (kstat_read(kc, ksp, NULL) == -1)
5677c478bd9Sstevel@tonic-gate 			return (errno);
5687c478bd9Sstevel@tonic-gate 		if (kstat_copy(ksp, &iodev->is_errors) == -1)
5697c478bd9Sstevel@tonic-gate 			return (errno);
5707c478bd9Sstevel@tonic-gate 	}
5717c478bd9Sstevel@tonic-gate 
5727c478bd9Sstevel@tonic-gate 	return (0);
5737c478bd9Sstevel@tonic-gate }
5747c478bd9Sstevel@tonic-gate 
5757c478bd9Sstevel@tonic-gate static void
get_ids(struct iodev_snapshot * iodev,const char * pretty)5767c478bd9Sstevel@tonic-gate get_ids(struct iodev_snapshot *iodev, const char *pretty)
5777c478bd9Sstevel@tonic-gate {
5787c478bd9Sstevel@tonic-gate 	int ctr, disk, slice, ret;
5797c478bd9Sstevel@tonic-gate 	char *target;
5807c478bd9Sstevel@tonic-gate 	const char *p1;
5817c478bd9Sstevel@tonic-gate 	const char *p2;
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate 	if (pretty == NULL)
5847c478bd9Sstevel@tonic-gate 		return;
5857c478bd9Sstevel@tonic-gate 
5867c478bd9Sstevel@tonic-gate 	if (sscanf(pretty, "c%d", &ctr) != 1)
5877c478bd9Sstevel@tonic-gate 		return;
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	p1 = pretty;
5907c478bd9Sstevel@tonic-gate 	while (*p1 && *p1 != 't')
5917c478bd9Sstevel@tonic-gate 		++p1;
5927c478bd9Sstevel@tonic-gate 
5937c478bd9Sstevel@tonic-gate 	if (!*p1)
5947c478bd9Sstevel@tonic-gate 		return;
5957c478bd9Sstevel@tonic-gate 	++p1;
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	p2 = p1;
5987c478bd9Sstevel@tonic-gate 	while (*p2 && *p2 != 'd')
5997c478bd9Sstevel@tonic-gate 		++p2;
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate 	if (!*p2 || p2 == p1)
6027c478bd9Sstevel@tonic-gate 		return;
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate 	target = safe_alloc(1 + p2 - p1);
6057c478bd9Sstevel@tonic-gate 	(void) strlcpy(target, p1, 1 + p2 - p1);
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate 	ret = sscanf(p2, "d%d%*[sp]%d", &disk, &slice);
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate 	if (ret == 2 && iodev->is_type == IODEV_PARTITION) {
6107c478bd9Sstevel@tonic-gate 		iodev->is_id.id = slice;
6117c478bd9Sstevel@tonic-gate 		iodev->is_parent_id.id = disk;
6127c478bd9Sstevel@tonic-gate 		(void) strlcpy(iodev->is_parent_id.tid, target, KSTAT_STRLEN);
6137c478bd9Sstevel@tonic-gate 	} else if (ret == 1) {
6147c478bd9Sstevel@tonic-gate 		if (iodev->is_type == IODEV_DISK) {
6157c478bd9Sstevel@tonic-gate 			iodev->is_id.id = disk;
6167c478bd9Sstevel@tonic-gate 			(void) strlcpy(iodev->is_id.tid, target, KSTAT_STRLEN);
6177c478bd9Sstevel@tonic-gate 			iodev->is_parent_id.id = ctr;
61837fbbce5Scth 		} else if (iodev->is_type == IODEV_IOPATH_LTI) {
6197c478bd9Sstevel@tonic-gate 			iodev->is_parent_id.id = disk;
6207c478bd9Sstevel@tonic-gate 			(void) strlcpy(iodev->is_parent_id.tid,
6214be70790Swroche 			    target, KSTAT_STRLEN);
6227c478bd9Sstevel@tonic-gate 		}
6237c478bd9Sstevel@tonic-gate 	}
6247c478bd9Sstevel@tonic-gate 
6257c478bd9Sstevel@tonic-gate 	free(target);
6267c478bd9Sstevel@tonic-gate }
6277c478bd9Sstevel@tonic-gate 
6287c478bd9Sstevel@tonic-gate static void
get_pretty_name(enum snapshot_types types,struct iodev_snapshot * iodev,kstat_ctl_t * kc)6297c478bd9Sstevel@tonic-gate get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev,
630*f9409f99SToomas Soome     kstat_ctl_t *kc)
6317c478bd9Sstevel@tonic-gate {
63237fbbce5Scth 	disk_list_t	*dl;
63337fbbce5Scth 	char		*pretty = NULL;
6347c478bd9Sstevel@tonic-gate 
6357c478bd9Sstevel@tonic-gate 	if (iodev->is_type == IODEV_NFS) {
6367c478bd9Sstevel@tonic-gate 		if (!(types & SNAP_IODEV_PRETTY))
6377c478bd9Sstevel@tonic-gate 			return;
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 		iodev->is_pretty = lookup_nfs_name(iodev->is_name, kc);
6407c478bd9Sstevel@tonic-gate 		return;
6417c478bd9Sstevel@tonic-gate 	}
6427c478bd9Sstevel@tonic-gate 
64337fbbce5Scth 	/* lookup/translate the kstat name */
644a08731ecScth 	dl = lookup_ks_name(iodev->is_name, (types & SNAP_IODEV_DEVID) ? 1 : 0);
6457c478bd9Sstevel@tonic-gate 	if (dl == NULL)
6467c478bd9Sstevel@tonic-gate 		return;
6477c478bd9Sstevel@tonic-gate 
6487c478bd9Sstevel@tonic-gate 	if (dl->dsk)
6497c478bd9Sstevel@tonic-gate 		pretty = safe_strdup(dl->dsk);
6507c478bd9Sstevel@tonic-gate 
6517c478bd9Sstevel@tonic-gate 	if (types & SNAP_IODEV_PRETTY) {
6527c478bd9Sstevel@tonic-gate 		if (dl->dname)
6537c478bd9Sstevel@tonic-gate 			iodev->is_dname = safe_strdup(dl->dname);
6547c478bd9Sstevel@tonic-gate 	}
6557c478bd9Sstevel@tonic-gate 
6567c478bd9Sstevel@tonic-gate 	if (dl->devidstr)
6577c478bd9Sstevel@tonic-gate 		iodev->is_devid = safe_strdup(dl->devidstr);
6587c478bd9Sstevel@tonic-gate 
6597c478bd9Sstevel@tonic-gate 	get_ids(iodev, pretty);
6607c478bd9Sstevel@tonic-gate 
66137fbbce5Scth 	/*
66237fbbce5Scth 	 * we fill in pretty name wether it is asked for or not because
66337fbbce5Scth 	 * it could be used in a filter by match_iodevs.
66437fbbce5Scth 	 */
66537fbbce5Scth 	iodev->is_pretty = pretty;
6667c478bd9Sstevel@tonic-gate }
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate static enum iodev_type
get_iodev_type(kstat_t * ksp)6697c478bd9Sstevel@tonic-gate get_iodev_type(kstat_t *ksp)
6707c478bd9Sstevel@tonic-gate {
6717c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "disk") == 0)
6727c478bd9Sstevel@tonic-gate 		return (IODEV_DISK);
6737c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "partition") == 0)
6747c478bd9Sstevel@tonic-gate 		return (IODEV_PARTITION);
6757c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "nfs") == 0)
6767c478bd9Sstevel@tonic-gate 		return (IODEV_NFS);
6777c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "iopath") == 0)
67837fbbce5Scth 		return (IODEV_IOPATH_LTI);
6797c478bd9Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "tape") == 0)
6807c478bd9Sstevel@tonic-gate 		return (IODEV_TAPE);
6817c478bd9Sstevel@tonic-gate 	return (IODEV_UNKNOWN);
6827c478bd9Sstevel@tonic-gate }
6837c478bd9Sstevel@tonic-gate 
68437fbbce5Scth /* get the lun/target/initiator from the name, return 1 on success */
68537fbbce5Scth static int
get_lti(char * s,char * lname,int * l,char * tname,int * t,char * iname,int * i)686*f9409f99SToomas Soome get_lti(char *s, char *lname, int *l, char *tname, int *t, char *iname, int *i)
68737fbbce5Scth {
68837fbbce5Scth 	int  num = 0;
68937fbbce5Scth 
6900c3de118SChris Liu 	num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z_]%d", lname, l,
69137fbbce5Scth 	    tname, t, iname, i);
69237fbbce5Scth 	return ((num == 6) ? 1 : 0);
69337fbbce5Scth }
69437fbbce5Scth 
69537fbbce5Scth 
69637fbbce5Scth /* get the lun, target, and initiator name and instance */
69737fbbce5Scth static void
get_path_info(struct iodev_snapshot * io,char * mod,size_t modlen,int * type,int * inst,char * name,size_t size)698fff11ba8SJonathan Cowper-Andrewes get_path_info(struct iodev_snapshot *io, char *mod, size_t modlen, int *type,
699fff11ba8SJonathan Cowper-Andrewes     int *inst, char *name, size_t size)
70037fbbce5Scth {
70137fbbce5Scth 
70237fbbce5Scth 	/*
70337fbbce5Scth 	 * If it is iopath or ssd then pad the name with i/t/l so we can sort
70437fbbce5Scth 	 * by alpha order and set type for IOPATH to DISK since we want to
70537fbbce5Scth 	 * have it grouped with its ssd parent. The lun can be 5 digits,
70637fbbce5Scth 	 * the target can be 4 digits, and the initiator can be 3 digits and
70737fbbce5Scth 	 * the padding is done appropriately for string comparisons.
70837fbbce5Scth 	 */
70937fbbce5Scth 	if (disk_or_partition_or_iopath(io->is_type)) {
71037fbbce5Scth 		int i1, t1, l1;
71137fbbce5Scth 		char tname[KSTAT_STRLEN], iname[KSTAT_STRLEN];
71237fbbce5Scth 		char *ptr, lname[KSTAT_STRLEN];
71337fbbce5Scth 
71437fbbce5Scth 		i1 = t1 = l1 = 0;
71537fbbce5Scth 		(void) get_lti(io->is_name, lname, &l1, tname, &t1, iname, &i1);
71637fbbce5Scth 		*type = io->is_type;
71737fbbce5Scth 		if (io->is_type == IODEV_DISK) {
71837fbbce5Scth 			(void) snprintf(name, size, "%s%05d", lname, l1);
71937fbbce5Scth 		} else if (io->is_type == IODEV_PARTITION) {
72037fbbce5Scth 			ptr = strchr(io->is_name, ',');
72137fbbce5Scth 			(void) snprintf(name, size, "%s%05d%s", lname, l1, ptr);
72237fbbce5Scth 		} else {
72337fbbce5Scth 			(void) snprintf(name, size, "%s%05d.%s%04d.%s%03d",
72437fbbce5Scth 			    lname, l1, tname, t1, iname, i1);
72537fbbce5Scth 			/* set to disk so we sort with disks */
72637fbbce5Scth 			*type = IODEV_DISK;
72737fbbce5Scth 		}
728fff11ba8SJonathan Cowper-Andrewes 		(void) strlcpy(mod, lname, modlen);
72937fbbce5Scth 		*inst = l1;
73037fbbce5Scth 	} else {
731fff11ba8SJonathan Cowper-Andrewes 		(void) strlcpy(mod, io->is_module, modlen);
732fff11ba8SJonathan Cowper-Andrewes 		(void) strlcpy(name, io->is_name, size);
73337fbbce5Scth 		*type = io->is_type;
73437fbbce5Scth 		*inst = io->is_instance;
73537fbbce5Scth 	}
73637fbbce5Scth }
73737fbbce5Scth 
7387c478bd9Sstevel@tonic-gate int
iodev_cmp(struct iodev_snapshot * io1,struct iodev_snapshot * io2)7397c478bd9Sstevel@tonic-gate iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2)
7407c478bd9Sstevel@tonic-gate {
74137fbbce5Scth 	int	type1, type2;
74237fbbce5Scth 	int	inst1, inst2;
74337fbbce5Scth 	char	name1[KSTAT_STRLEN], name2[KSTAT_STRLEN];
74437fbbce5Scth 	char	mod1[KSTAT_STRLEN], mod2[KSTAT_STRLEN];
74537fbbce5Scth 
746fff11ba8SJonathan Cowper-Andrewes 	get_path_info(io1, mod1, sizeof (mod1), &type1, &inst1, name1,
747fff11ba8SJonathan Cowper-Andrewes 	    sizeof (name1));
748fff11ba8SJonathan Cowper-Andrewes 	get_path_info(io2, mod2, sizeof (mod2), &type2, &inst2, name2,
749fff11ba8SJonathan Cowper-Andrewes 	    sizeof (name2));
75037fbbce5Scth 	if ((!disk_or_partition(type1)) ||
75137fbbce5Scth 	    (!disk_or_partition(type2))) {
75237fbbce5Scth 		/* neutral sort order between disk and part */
75337fbbce5Scth 		if (type1 < type2) {
7547c478bd9Sstevel@tonic-gate 			return (-1);
75537fbbce5Scth 		}
75637fbbce5Scth 		if (type1 > type2) {
7577c478bd9Sstevel@tonic-gate 			return (1);
75837fbbce5Scth 		}
7597c478bd9Sstevel@tonic-gate 	}
7607c478bd9Sstevel@tonic-gate 
7617c478bd9Sstevel@tonic-gate 	/* controller doesn't have ksp */
7627c478bd9Sstevel@tonic-gate 	if (io1->is_ksp && io2->is_ksp) {
76337fbbce5Scth 		if (strcmp(mod1, mod2) != 0) {
76437fbbce5Scth 			return (strcmp(mod1, mod2));
76537fbbce5Scth 		}
76637fbbce5Scth 		if (inst1 < inst2) {
7677c478bd9Sstevel@tonic-gate 			return (-1);
76837fbbce5Scth 		}
76937fbbce5Scth 		if (inst1 > inst2) {
7707c478bd9Sstevel@tonic-gate 			return (1);
77137fbbce5Scth 		}
7727c478bd9Sstevel@tonic-gate 	} else {
77337fbbce5Scth 		if (io1->is_id.id < io2->is_id.id) {
7747c478bd9Sstevel@tonic-gate 			return (-1);
77537fbbce5Scth 		}
77637fbbce5Scth 		if (io1->is_id.id > io2->is_id.id) {
7777c478bd9Sstevel@tonic-gate 			return (1);
77837fbbce5Scth 		}
7797c478bd9Sstevel@tonic-gate 	}
7807c478bd9Sstevel@tonic-gate 
78137fbbce5Scth 	return (strcmp(name1, name2));
78237fbbce5Scth }
78337fbbce5Scth 
78437fbbce5Scth /* update the target reads and writes */
78537fbbce5Scth static void
update_target(struct iodev_snapshot * tgt,struct iodev_snapshot * path)78637fbbce5Scth update_target(struct iodev_snapshot *tgt, struct iodev_snapshot *path)
78737fbbce5Scth {
78837fbbce5Scth 	tgt->is_stats.reads += path->is_stats.reads;
78937fbbce5Scth 	tgt->is_stats.writes += path->is_stats.writes;
79037fbbce5Scth 	tgt->is_stats.nread += path->is_stats.nread;
79137fbbce5Scth 	tgt->is_stats.nwritten += path->is_stats.nwritten;
79237fbbce5Scth 	tgt->is_stats.wcnt += path->is_stats.wcnt;
79337fbbce5Scth 	tgt->is_stats.rcnt += path->is_stats.rcnt;
79437fbbce5Scth 
79537fbbce5Scth 	/*
79637fbbce5Scth 	 * Stash the t_delta in the crtime for use in show_disk
79737fbbce5Scth 	 * NOTE: this can't be done in show_disk because the
79837fbbce5Scth 	 * itl entry is removed for the old format
79937fbbce5Scth 	 */
80037fbbce5Scth 	tgt->is_crtime += hrtime_delta(path->is_crtime, path->is_snaptime);
80137fbbce5Scth 	tgt->is_snaptime += path->is_snaptime;
80237fbbce5Scth 	tgt->is_nr_children += 1;
80337fbbce5Scth }
80437fbbce5Scth 
80537fbbce5Scth /*
80637fbbce5Scth  * Create a new synthetic device entry of the specified type. The supported
80737fbbce5Scth  * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI.
80837fbbce5Scth  */
80937fbbce5Scth static struct iodev_snapshot *
make_extended_device(int type,struct iodev_snapshot * old)81037fbbce5Scth make_extended_device(int type, struct iodev_snapshot *old)
81137fbbce5Scth {
81237fbbce5Scth 	struct iodev_snapshot	*tptr = NULL;
81337fbbce5Scth 	char			*ptr;
81437fbbce5Scth 	int			lun, tgt, initiator;
81537fbbce5Scth 	char			lun_name[KSTAT_STRLEN];
81637fbbce5Scth 	char			tgt_name[KSTAT_STRLEN];
81737fbbce5Scth 	char			initiator_name[KSTAT_STRLEN];
81837fbbce5Scth 
81937fbbce5Scth 	if (old == NULL)
82037fbbce5Scth 		return (NULL);
82137fbbce5Scth 	if (get_lti(old->is_name,
82237fbbce5Scth 	    lun_name, &lun, tgt_name, &tgt, initiator_name, &initiator) != 1) {
82337fbbce5Scth 		return (NULL);
82437fbbce5Scth 	}
82537fbbce5Scth 	tptr = safe_alloc(sizeof (*old));
82637fbbce5Scth 	bzero(tptr, sizeof (*old));
82737fbbce5Scth 	if (old->is_pretty != NULL) {
82837fbbce5Scth 		tptr->is_pretty = safe_alloc(strlen(old->is_pretty) + 1);
82937fbbce5Scth 		(void) strcpy(tptr->is_pretty, old->is_pretty);
83037fbbce5Scth 	}
83137fbbce5Scth 	bcopy(&old->is_parent_id, &tptr->is_parent_id,
8324be70790Swroche 	    sizeof (old->is_parent_id));
83337fbbce5Scth 
83437fbbce5Scth 	tptr->is_type = type;
83537fbbce5Scth 
83637fbbce5Scth 	if (type == IODEV_IOPATH_LT) {
83737fbbce5Scth 		/* make new synthetic entry that is the LT */
83837fbbce5Scth 		/* set the id to the target id */
83937fbbce5Scth 		tptr->is_id.id = tgt;
84037fbbce5Scth 		(void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
84137fbbce5Scth 		    "%s%d", tgt_name, tgt);
84237fbbce5Scth 		(void) snprintf(tptr->is_name, sizeof (tptr->is_name),
84337fbbce5Scth 		    "%s%d.%s%d", lun_name, lun, tgt_name, tgt);
84437fbbce5Scth 
84537fbbce5Scth 		if (old->is_pretty) {
84637fbbce5Scth 			ptr = strrchr(tptr->is_pretty, '.');
84737fbbce5Scth 			if (ptr)
84837fbbce5Scth 				*ptr = '\0';
84937fbbce5Scth 		}
85037fbbce5Scth 	} else if (type == IODEV_IOPATH_LI) {
85137fbbce5Scth 		/* make new synthetic entry that is the LI */
85237fbbce5Scth 		/* set the id to the initiator number */
85337fbbce5Scth 		tptr->is_id.id = initiator;
85437fbbce5Scth 		(void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
85537fbbce5Scth 		    "%s%d", initiator_name, initiator);
85637fbbce5Scth 		(void) snprintf(tptr->is_name, sizeof (tptr->is_name),
85737fbbce5Scth 		    "%s%d.%s%d", lun_name, lun, initiator_name, initiator);
85837fbbce5Scth 
85937fbbce5Scth 		if (old->is_pretty) {
86037fbbce5Scth 			ptr = strchr(tptr->is_pretty, '.');
86137fbbce5Scth 			if (ptr)
86237fbbce5Scth 				(void) snprintf(ptr + 1,
86337fbbce5Scth 				    strlen(tptr->is_pretty) + 1,
86437fbbce5Scth 				    "%s%d", initiator_name, initiator);
86537fbbce5Scth 		}
86637fbbce5Scth 	}
86737fbbce5Scth 	return (tptr);
86837fbbce5Scth }
86937fbbce5Scth 
87037fbbce5Scth /*
87137fbbce5Scth  * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat
87237fbbce5Scth  * is found - traverse the children looking for the same initiator and sum
87337fbbce5Scth  * them up. Add an LI entry and delete all of the LTI entries with the same
87437fbbce5Scth  * initiator.
87537fbbce5Scth  */
87637fbbce5Scth static int
create_li_delete_lti(struct snapshot * ss,struct iodev_snapshot * list)87737fbbce5Scth create_li_delete_lti(struct snapshot *ss, struct iodev_snapshot *list)
87837fbbce5Scth {
87937fbbce5Scth 	struct iodev_snapshot	*pos, *entry, *parent;
88037fbbce5Scth 	int			lun, tgt, initiator;
88137fbbce5Scth 	char			lun_name[KSTAT_STRLEN];
88237fbbce5Scth 	char			tgt_name[KSTAT_STRLEN];
88337fbbce5Scth 	char			initiator_name[KSTAT_STRLEN];
88437fbbce5Scth 	int			err;
88537fbbce5Scth 
88637fbbce5Scth 	for (entry = list; entry; entry = entry->is_next) {
88737fbbce5Scth 		if ((err = create_li_delete_lti(ss, entry->is_children)) != 0)
88837fbbce5Scth 			return (err);
88937fbbce5Scth 
89037fbbce5Scth 		if (entry->is_type == IODEV_IOPATH_LTI) {
89137fbbce5Scth 			parent = find_parent(ss, entry);
89237fbbce5Scth 			if (get_lti(entry->is_name, lun_name, &lun,
89337fbbce5Scth 			    tgt_name, &tgt, initiator_name, &initiator) != 1) {
89437fbbce5Scth 				return (1);
89537fbbce5Scth 			}
89637fbbce5Scth 
89737fbbce5Scth 			pos = (parent == NULL) ? NULL : parent->is_children;
89837fbbce5Scth 			for (; pos; pos = pos->is_next) {
89937fbbce5Scth 				if (pos->is_id.id != -1 &&
90037fbbce5Scth 				    pos->is_id.id == initiator &&
90137fbbce5Scth 				    pos->is_type == IODEV_IOPATH_LI) {
90237fbbce5Scth 					/* found the same initiator */
90337fbbce5Scth 					update_target(pos, entry);
90437fbbce5Scth 					list_del(&parent->is_children, entry);
90537fbbce5Scth 					free_iodev(entry);
90637fbbce5Scth 					parent->is_nr_children--;
90737fbbce5Scth 					entry = pos;
90837fbbce5Scth 					break;
90937fbbce5Scth 				}
91037fbbce5Scth 			}
91137fbbce5Scth 
91237fbbce5Scth 			if (!pos) {
91337fbbce5Scth 				/* make the first LI entry */
91437fbbce5Scth 				pos = make_extended_device(
91537fbbce5Scth 				    IODEV_IOPATH_LI, entry);
91637fbbce5Scth 				update_target(pos, entry);
91737fbbce5Scth 
91837fbbce5Scth 				if (parent) {
91937fbbce5Scth 					insert_before(&parent->is_children,
92037fbbce5Scth 					    entry, pos);
92137fbbce5Scth 					list_del(&parent->is_children, entry);
92237fbbce5Scth 					free_iodev(entry);
92337fbbce5Scth 				} else {
92437fbbce5Scth 					insert_before(&ss->s_iodevs, entry,
92537fbbce5Scth 					    pos);
92637fbbce5Scth 					list_del(&ss->s_iodevs, entry);
92737fbbce5