xref: /onnv-gate/usr/src/cmd/stat/common/acquire_iodevs.c (revision 12920:ee579b934f0a)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52723Scth  * Common Development and Distribution License (the "License").
62723Scth  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*12920SJonathan.Ca@Sun.COM  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate 
250Sstevel@tonic-gate #include "statcommon.h"
260Sstevel@tonic-gate #include "dsr.h"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate #include <sys/dklabel.h>
290Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
300Sstevel@tonic-gate #include <stdlib.h>
310Sstevel@tonic-gate #include <stdarg.h>
320Sstevel@tonic-gate #include <unistd.h>
330Sstevel@tonic-gate #include <strings.h>
340Sstevel@tonic-gate #include <errno.h>
350Sstevel@tonic-gate #include <limits.h>
360Sstevel@tonic-gate 
370Sstevel@tonic-gate static void insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev);
380Sstevel@tonic-gate 
390Sstevel@tonic-gate static struct iodev_snapshot *
make_controller(int cid)400Sstevel@tonic-gate make_controller(int cid)
410Sstevel@tonic-gate {
420Sstevel@tonic-gate 	struct iodev_snapshot *new;
430Sstevel@tonic-gate 
440Sstevel@tonic-gate 	new = safe_alloc(sizeof (struct iodev_snapshot));
450Sstevel@tonic-gate 	(void) memset(new, 0, sizeof (struct iodev_snapshot));
460Sstevel@tonic-gate 	new->is_type = IODEV_CONTROLLER;
470Sstevel@tonic-gate 	new->is_id.id = cid;
480Sstevel@tonic-gate 	new->is_parent_id.id = IODEV_NO_ID;
490Sstevel@tonic-gate 
500Sstevel@tonic-gate 	(void) snprintf(new->is_name, sizeof (new->is_name), "c%d", cid);
510Sstevel@tonic-gate 
520Sstevel@tonic-gate 	return (new);
530Sstevel@tonic-gate }
540Sstevel@tonic-gate 
550Sstevel@tonic-gate static struct iodev_snapshot *
find_iodev_by_name(struct iodev_snapshot * list,const char * name)560Sstevel@tonic-gate find_iodev_by_name(struct iodev_snapshot *list, const char *name)
570Sstevel@tonic-gate {
580Sstevel@tonic-gate 	struct iodev_snapshot *pos;
590Sstevel@tonic-gate 	struct iodev_snapshot *pos2;
600Sstevel@tonic-gate 
610Sstevel@tonic-gate 	for (pos = list; pos; pos = pos->is_next) {
620Sstevel@tonic-gate 		if (strcmp(pos->is_name, name) == 0)
630Sstevel@tonic-gate 			return (pos);
640Sstevel@tonic-gate 
650Sstevel@tonic-gate 		pos2 = find_iodev_by_name(pos->is_children, name);
660Sstevel@tonic-gate 		if (pos2 != NULL)
670Sstevel@tonic-gate 			return (pos2);
680Sstevel@tonic-gate 	}
690Sstevel@tonic-gate 
700Sstevel@tonic-gate 	return (NULL);
710Sstevel@tonic-gate }
720Sstevel@tonic-gate 
730Sstevel@tonic-gate static enum iodev_type
parent_iodev_type(enum iodev_type type)740Sstevel@tonic-gate parent_iodev_type(enum iodev_type type)
750Sstevel@tonic-gate {
760Sstevel@tonic-gate 	switch (type) {
770Sstevel@tonic-gate 		case IODEV_CONTROLLER: return (0);
782907Scth 		case IODEV_IOPATH_LT: return (0);
792907Scth 		case IODEV_IOPATH_LI: return (0);
800Sstevel@tonic-gate 		case IODEV_NFS: return (0);
810Sstevel@tonic-gate 		case IODEV_TAPE: return (0);
822907Scth 		case IODEV_IOPATH_LTI: return (IODEV_DISK);
830Sstevel@tonic-gate 		case IODEV_DISK: return (IODEV_CONTROLLER);
840Sstevel@tonic-gate 		case IODEV_PARTITION: return (IODEV_DISK);
850Sstevel@tonic-gate 	}
860Sstevel@tonic-gate 	return (IODEV_UNKNOWN);
870Sstevel@tonic-gate }
880Sstevel@tonic-gate 
890Sstevel@tonic-gate static int
id_match(struct iodev_id * id1,struct iodev_id * id2)900Sstevel@tonic-gate id_match(struct iodev_id *id1, struct iodev_id *id2)
910Sstevel@tonic-gate {
920Sstevel@tonic-gate 	return (id1->id == id2->id &&
935957Swroche 	    strcmp(id1->tid, id2->tid) == 0);
940Sstevel@tonic-gate }
950Sstevel@tonic-gate 
960Sstevel@tonic-gate static struct iodev_snapshot *
find_parent(struct snapshot * ss,struct iodev_snapshot * iodev)970Sstevel@tonic-gate find_parent(struct snapshot *ss, struct iodev_snapshot *iodev)
980Sstevel@tonic-gate {
990Sstevel@tonic-gate 	enum iodev_type parent_type = parent_iodev_type(iodev->is_type);
1000Sstevel@tonic-gate 	struct iodev_snapshot *pos;
1010Sstevel@tonic-gate 	struct iodev_snapshot *pos2;
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate 	if (parent_type == 0 || parent_type == IODEV_UNKNOWN)
1040Sstevel@tonic-gate 		return (NULL);
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate 	if (iodev->is_parent_id.id == IODEV_NO_ID &&
1070Sstevel@tonic-gate 	    iodev->is_parent_id.tid[0] == '\0')
1080Sstevel@tonic-gate 		return (NULL);
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	if (parent_type == IODEV_CONTROLLER) {
1110Sstevel@tonic-gate 		for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
1120Sstevel@tonic-gate 			if (pos->is_type != IODEV_CONTROLLER)
1130Sstevel@tonic-gate 				continue;
1140Sstevel@tonic-gate 			if (pos->is_id.id != iodev->is_parent_id.id)
1150Sstevel@tonic-gate 				continue;
1160Sstevel@tonic-gate 			return (pos);
1170Sstevel@tonic-gate 		}
1180Sstevel@tonic-gate 
1190Sstevel@tonic-gate 		if (!(ss->s_types & SNAP_CONTROLLERS))
1200Sstevel@tonic-gate 			return (NULL);
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 		pos = make_controller(iodev->is_parent_id.id);
1230Sstevel@tonic-gate 		insert_iodev(ss, pos);
1240Sstevel@tonic-gate 		return (pos);
1250Sstevel@tonic-gate 	}
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	/* IODEV_DISK parent */
1280Sstevel@tonic-gate 	for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
1290Sstevel@tonic-gate 		if (id_match(&iodev->is_parent_id, &pos->is_id) &&
1300Sstevel@tonic-gate 		    pos->is_type == IODEV_DISK)
1310Sstevel@tonic-gate 			return (pos);
1320Sstevel@tonic-gate 		if (pos->is_type != IODEV_CONTROLLER)
1330Sstevel@tonic-gate 			continue;
1340Sstevel@tonic-gate 		for (pos2 = pos->is_children; pos2; pos2 = pos2->is_next) {
1350Sstevel@tonic-gate 			if (pos2->is_type != IODEV_DISK)
1360Sstevel@tonic-gate 				continue;
1370Sstevel@tonic-gate 			if (id_match(&iodev->is_parent_id, &pos2->is_id))
1380Sstevel@tonic-gate 				return (pos2);
1390Sstevel@tonic-gate 		}
1400Sstevel@tonic-gate 	}
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate 	return (NULL);
1430Sstevel@tonic-gate }
1440Sstevel@tonic-gate 
1455957Swroche /*
1465957Swroche  * Introduce an index into the list to speed up insert_into looking for the
1475957Swroche  * right position in the list. This index is an AVL tree of all the
1485957Swroche  * iodev_snapshot in the list.
1495957Swroche  */
1505957Swroche 
1515957Swroche #define	offsetof(s, m)	(size_t)(&(((s *)0)->m))	/* for avl_create */
1525957Swroche 
1535957Swroche static int
avl_iodev_cmp(const void * is1,const void * is2)1545957Swroche avl_iodev_cmp(const void* is1, const void* is2)
1555957Swroche {
1565957Swroche 	int c = iodev_cmp((struct iodev_snapshot *)is1,
1575957Swroche 	    (struct iodev_snapshot *)is2);
1585957Swroche 
1595957Swroche 	if (c > 0)
1605957Swroche 		return (1);
1615957Swroche 
1625957Swroche 	if (c < 0)
1635957Swroche 		return (-1);
1645957Swroche 
1655957Swroche 	return (0);
1665957Swroche }
1675957Swroche 
1685957Swroche static void
ix_new_list(struct iodev_snapshot * elem)1695957Swroche ix_new_list(struct iodev_snapshot *elem)
1705957Swroche {
1715957Swroche 	avl_tree_t *l = malloc(sizeof (avl_tree_t));
1725957Swroche 
1735957Swroche 	elem->avl_list = l;
1745957Swroche 	if (l == NULL)
1755957Swroche 		return;
1765957Swroche 
1775957Swroche 	avl_create(l, avl_iodev_cmp, sizeof (struct iodev_snapshot),
1785957Swroche 	    offsetof(struct iodev_snapshot, avl_link));
1795957Swroche 
1805957Swroche 	avl_add(l, elem);
1815957Swroche }
1825957Swroche 
1835957Swroche static void
ix_list_del(struct iodev_snapshot * elem)1845957Swroche ix_list_del(struct iodev_snapshot *elem)
1855957Swroche {
1865957Swroche 	avl_tree_t *l = elem->avl_list;
1875957Swroche 
1885957Swroche 	if (l == NULL)
1895957Swroche 		return;
1905957Swroche 
1915957Swroche 	elem->avl_list = NULL;
1925957Swroche 
1935957Swroche 	avl_remove(l, elem);
1945957Swroche 	if (avl_numnodes(l) == 0) {
1955957Swroche 		avl_destroy(l);
1965957Swroche 		free(l);
1975957Swroche 	}
1985957Swroche }
1995957Swroche 
2005957Swroche static void
ix_insert_here(struct iodev_snapshot * pos,struct iodev_snapshot * elem,int ba)2015957Swroche ix_insert_here(struct iodev_snapshot *pos, struct iodev_snapshot *elem, int ba)
2025957Swroche {
2035957Swroche 	avl_tree_t *l = pos->avl_list;
2045957Swroche 	elem->avl_list = l;
2055957Swroche 
2065957Swroche 	if (l == NULL)
2075957Swroche 		return;
2085957Swroche 
2095957Swroche 	avl_insert_here(l, elem, pos, ba);
2105957Swroche }
2115957Swroche 
2120Sstevel@tonic-gate static void
list_del(struct iodev_snapshot ** list,struct iodev_snapshot * pos)2130Sstevel@tonic-gate list_del(struct iodev_snapshot **list, struct iodev_snapshot *pos)
2140Sstevel@tonic-gate {
2155957Swroche 	ix_list_del(pos);
2165957Swroche 
2170Sstevel@tonic-gate 	if (*list == pos)
2180Sstevel@tonic-gate 		*list = pos->is_next;
2190Sstevel@tonic-gate 	if (pos->is_next)
2200Sstevel@tonic-gate 		pos->is_next->is_prev = pos->is_prev;
2210Sstevel@tonic-gate 	if (pos->is_prev)
2220Sstevel@tonic-gate 		pos->is_prev->is_next = pos->is_next;
2230Sstevel@tonic-gate 	pos->is_prev = pos->is_next = NULL;
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate static void
insert_before(struct iodev_snapshot ** list,struct iodev_snapshot * pos,struct iodev_snapshot * new)2270Sstevel@tonic-gate insert_before(struct iodev_snapshot **list, struct iodev_snapshot *pos,
2280Sstevel@tonic-gate     struct iodev_snapshot *new)
2290Sstevel@tonic-gate {
2300Sstevel@tonic-gate 	if (pos == NULL) {
2310Sstevel@tonic-gate 		new->is_prev = new->is_next = NULL;
2320Sstevel@tonic-gate 		*list = new;
2335957Swroche 		ix_new_list(new);
2340Sstevel@tonic-gate 		return;
2350Sstevel@tonic-gate 	}
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	new->is_next = pos;
2380Sstevel@tonic-gate 	new->is_prev = pos->is_prev;
2390Sstevel@tonic-gate 	if (pos->is_prev)
2400Sstevel@tonic-gate 		pos->is_prev->is_next = new;
2410Sstevel@tonic-gate 	else
2420Sstevel@tonic-gate 		*list = new;
2430Sstevel@tonic-gate 	pos->is_prev = new;
2445957Swroche 
2455957Swroche 	ix_insert_here(pos, new, AVL_BEFORE);
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate static void
insert_after(struct iodev_snapshot ** list,struct iodev_snapshot * pos,struct iodev_snapshot * new)2490Sstevel@tonic-gate insert_after(struct iodev_snapshot **list, struct iodev_snapshot *pos,
2500Sstevel@tonic-gate     struct iodev_snapshot *new)
2510Sstevel@tonic-gate {
2520Sstevel@tonic-gate 	if (pos == NULL) {
2530Sstevel@tonic-gate 		new->is_prev = new->is_next = NULL;
2540Sstevel@tonic-gate 		*list = new;
2555957Swroche 		ix_new_list(new);
2560Sstevel@tonic-gate 		return;
2570Sstevel@tonic-gate 	}
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	new->is_next = pos->is_next;
2600Sstevel@tonic-gate 	new->is_prev = pos;
2610Sstevel@tonic-gate 	if (pos->is_next)
2620Sstevel@tonic-gate 		pos->is_next->is_prev = new;
2630Sstevel@tonic-gate 	pos->is_next = new;
2645957Swroche 
2655957Swroche 	ix_insert_here(pos, new, AVL_AFTER);
2660Sstevel@tonic-gate }
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate static void
insert_into(struct iodev_snapshot ** list,struct iodev_snapshot * iodev)2690Sstevel@tonic-gate insert_into(struct iodev_snapshot **list, struct iodev_snapshot *iodev)
2700Sstevel@tonic-gate {
2710Sstevel@tonic-gate 	struct iodev_snapshot *tmp = *list;
2725957Swroche 	avl_tree_t *l;
2735957Swroche 	void *p;
2745957Swroche 	avl_index_t where;
2755957Swroche 
2760Sstevel@tonic-gate 	if (*list == NULL) {
2770Sstevel@tonic-gate 		*list = iodev;
2785957Swroche 		ix_new_list(iodev);
2790Sstevel@tonic-gate 		return;
2800Sstevel@tonic-gate 	}
2810Sstevel@tonic-gate 
2825957Swroche 	/*
2835957Swroche 	 * Optimize the search: instead of walking the entire list
2845957Swroche 	 * (which can contain thousands of nodes), search in the AVL
2855957Swroche 	 * tree the nearest node and reposition the startup point to
2865957Swroche 	 * this node rather than always starting from the beginning
2875957Swroche 	 * of the list.
2885957Swroche 	 */
2895957Swroche 	l = tmp->avl_list;
2905957Swroche 	if (l != NULL) {
2915957Swroche 		p = avl_find(l, iodev, &where);
2925957Swroche 		if (p == NULL) {
2935957Swroche 			p = avl_nearest(l, where, AVL_BEFORE);
2945957Swroche 		}
2955957Swroche 		if (p != NULL) {
2965957Swroche 			tmp = (struct iodev_snapshot *)p;
2975957Swroche 		}
2985957Swroche 	}
2995957Swroche 
3000Sstevel@tonic-gate 	for (;;) {
3010Sstevel@tonic-gate 		if (iodev_cmp(tmp, iodev) > 0) {
3020Sstevel@tonic-gate 			insert_before(list, tmp, iodev);
3030Sstevel@tonic-gate 			return;
3040Sstevel@tonic-gate 		}
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 		if (tmp->is_next == NULL)
3070Sstevel@tonic-gate 			break;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 		tmp = tmp->is_next;
3100Sstevel@tonic-gate 	}
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	insert_after(list, tmp, iodev);
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate static int
disk_or_partition(enum iodev_type type)3160Sstevel@tonic-gate disk_or_partition(enum iodev_type type)
3170Sstevel@tonic-gate {
3180Sstevel@tonic-gate 	return (type == IODEV_DISK || type == IODEV_PARTITION);
3190Sstevel@tonic-gate }
3200Sstevel@tonic-gate 
3212907Scth static int
disk_or_partition_or_iopath(enum iodev_type type)3222907Scth disk_or_partition_or_iopath(enum iodev_type type)
3232907Scth {
3242907Scth 	return (type == IODEV_DISK || type == IODEV_PARTITION ||
3252907Scth 	    type == IODEV_IOPATH_LTI);
3262907Scth }
3272907Scth 
3280Sstevel@tonic-gate static void
insert_iodev(struct snapshot * ss,struct iodev_snapshot * iodev)3290Sstevel@tonic-gate insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev)
3300Sstevel@tonic-gate {
3310Sstevel@tonic-gate 	struct iodev_snapshot *parent = find_parent(ss, iodev);
3320Sstevel@tonic-gate 	struct iodev_snapshot **list;
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	if (parent != NULL) {
3350Sstevel@tonic-gate 		list = &parent->is_children;
3360Sstevel@tonic-gate 		parent->is_nr_children++;
3370Sstevel@tonic-gate 	} else {
3380Sstevel@tonic-gate 		list = &ss->s_iodevs;
3390Sstevel@tonic-gate 		ss->s_nr_iodevs++;
3400Sstevel@tonic-gate 	}
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	insert_into(list, iodev);
3430Sstevel@tonic-gate }
3440Sstevel@tonic-gate 
3452907Scth /* return 1 if dev passes filter */
3460Sstevel@tonic-gate static int
iodev_match(struct iodev_snapshot * dev,struct iodev_filter * df)3470Sstevel@tonic-gate iodev_match(struct iodev_snapshot *dev, struct iodev_filter *df)
3480Sstevel@tonic-gate {
3492907Scth 	int	is_floppy = (strncmp(dev->is_name, "fd", 2) == 0);
3502907Scth 	char	*isn, *ispn, *ifn;
3512907Scth 	char	*path;
3522907Scth 	int	ifnl;
3532907Scth 	size_t	i;
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate 	/* no filter, pass */
3560Sstevel@tonic-gate 	if (df == NULL)
3572907Scth 		return (1);		/* pass */
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 	/* no filtered names, pass if not floppy and skipped */
3600Sstevel@tonic-gate 	if (df->if_nr_names == NULL)
3610Sstevel@tonic-gate 		return (!(df->if_skip_floppy && is_floppy));
3620Sstevel@tonic-gate 
3632907Scth 	isn = dev->is_name;
3642907Scth 	ispn = dev->is_pretty;
3650Sstevel@tonic-gate 	for (i = 0; i < df->if_nr_names; i++) {
3662907Scth 		ifn = df->if_names[i];
3672907Scth 		ifnl = strlen(ifn);
3682907Scth 		path = strchr(ifn, '.');
3692907Scth 
3702907Scth 		if ((strcmp(isn, ifn) == 0) ||
3712907Scth 		    (ispn && (strcmp(ispn, ifn) == 0)))
3722907Scth 			return (1);	/* pass */
3732907Scth 
3742907Scth 		/* if filter is a path allow partial match */
3752907Scth 		if (path &&
3762907Scth 		    ((strncmp(isn, ifn, ifnl) == 0) ||
3772907Scth 		    (ispn && (strncmp(ispn, ifn, ifnl) == 0))))
3782907Scth 			return (1);	/* pass */
3790Sstevel@tonic-gate 	}
3800Sstevel@tonic-gate 
3812907Scth 	return (0);			/* fail */
3822907Scth }
3832907Scth 
3842907Scth /* return 1 if path is an mpxio path associated with dev */
3852907Scth static int
iodev_path_match(struct iodev_snapshot * dev,struct iodev_snapshot * path)3862907Scth iodev_path_match(struct iodev_snapshot *dev, struct iodev_snapshot *path)
3872907Scth {
3882907Scth 	char	*dn, *pn;
3892907Scth 	int	dnl;
3902907Scth 
3912907Scth 	dn = dev->is_name;
3922907Scth 	pn = path->is_name;
3932907Scth 	dnl = strlen(dn);
3942907Scth 
3952907Scth 	if ((strncmp(pn, dn, dnl) == 0) && (pn[dnl] == '.'))
3962907Scth 		return (1);			/* yes */
3972907Scth 
3982907Scth 	return (0);				/* no */
3990Sstevel@tonic-gate }
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate /* select which I/O devices to collect stats for */
4020Sstevel@tonic-gate static void
choose_iodevs(struct snapshot * ss,struct iodev_snapshot * iodevs,struct iodev_filter * df)4030Sstevel@tonic-gate choose_iodevs(struct snapshot *ss, struct iodev_snapshot *iodevs,
4040Sstevel@tonic-gate     struct iodev_filter *df)
4050Sstevel@tonic-gate {
4062907Scth 	struct iodev_snapshot	*pos, *ppos, *tmp, *ptmp;
4072907Scth 	int			nr_iodevs;
4082907Scth 	int			nr_iodevs_orig;
4092907Scth 
4102907Scth 	nr_iodevs = df ? df->if_max_iodevs : UNLIMITED_IODEVS;
4112907Scth 	nr_iodevs_orig = nr_iodevs;
4120Sstevel@tonic-gate 
4130Sstevel@tonic-gate 	if (nr_iodevs == UNLIMITED_IODEVS)
4140Sstevel@tonic-gate 		nr_iodevs = INT_MAX;
4150Sstevel@tonic-gate 
4162907Scth 	/* add the full matches */
4172907Scth 	pos = iodevs;
4180Sstevel@tonic-gate 	while (pos && nr_iodevs) {
4192907Scth 		tmp = pos;
4200Sstevel@tonic-gate 		pos = pos->is_next;
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 		if (!iodev_match(tmp, df))
4232907Scth 			continue;	/* failed full match */
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate 		list_del(&iodevs, tmp);
4260Sstevel@tonic-gate 		insert_iodev(ss, tmp);
4270Sstevel@tonic-gate 
4282907Scth 		/*
4292907Scth 		 * Add all mpxio paths associated with match above. Added
4302907Scth 		 * paths don't count against nr_iodevs.
4312907Scth 		 */
4322907Scth 		if (strchr(tmp->is_name, '.') == NULL) {
4332907Scth 		ppos = iodevs;
4342907Scth 		while (ppos) {
4352907Scth 			ptmp = ppos;
4362907Scth 			ppos = ppos->is_next;
4372907Scth 
4382907Scth 			if (!iodev_path_match(tmp, ptmp))
4392907Scth 				continue;	/* not an mpxio path */
4402907Scth 
4412907Scth 			list_del(&iodevs, ptmp);
4422907Scth 			insert_iodev(ss, ptmp);
4432907Scth 			if (pos == ptmp)
4442907Scth 				pos = ppos;
4452907Scth 		}
4462907Scth 		}
4472907Scth 
4482907Scth 		nr_iodevs--;
4490Sstevel@tonic-gate 	}
4500Sstevel@tonic-gate 
4512907Scth 	/*
4522907Scth 	 * If we had a filter, and *nothing* passed the filter then we
4532907Scth 	 * don't want to fill the  remaining slots - it is just confusing
4542907Scth 	 * if we don that, it makes it look like the filter code is broken.
4552907Scth 	 */
4562907Scth 	if ((df->if_nr_names == NULL) || (nr_iodevs != nr_iodevs_orig)) {
4572907Scth 		/* now insert any iodevs into the remaining slots */
4582907Scth 		pos = iodevs;
4592907Scth 		while (pos && nr_iodevs) {
4602907Scth 			tmp = pos;
4612907Scth 			pos = pos->is_next;
4620Sstevel@tonic-gate 
4632907Scth 			if (df && df->if_skip_floppy &&
4645957Swroche 			    strncmp(tmp->is_name, "fd", 2) == 0)
4652907Scth 				continue;
4660Sstevel@tonic-gate 
4672907Scth 			list_del(&iodevs, tmp);
4682907Scth 			insert_iodev(ss, tmp);
4690Sstevel@tonic-gate 
4702907Scth 			--nr_iodevs;
4712907Scth 		}
4720Sstevel@tonic-gate 	}
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 	/* clear the unwanted ones */
4750Sstevel@tonic-gate 	pos = iodevs;
4760Sstevel@tonic-gate 	while (pos) {
4770Sstevel@tonic-gate 		struct iodev_snapshot *tmp = pos;
4780Sstevel@tonic-gate 		pos = pos->is_next;
4790Sstevel@tonic-gate 		free_iodev(tmp);
4800Sstevel@tonic-gate 	}
4810Sstevel@tonic-gate }
4820Sstevel@tonic-gate 
4830Sstevel@tonic-gate static int
collate_controller(struct iodev_snapshot * controller,struct iodev_snapshot * disk)4840Sstevel@tonic-gate collate_controller(struct iodev_snapshot *controller,
4850Sstevel@tonic-gate     struct iodev_snapshot *disk)
4860Sstevel@tonic-gate {
4870Sstevel@tonic-gate 	controller->is_stats.nread += disk->is_stats.nread;
4880Sstevel@tonic-gate 	controller->is_stats.nwritten += disk->is_stats.nwritten;
4890Sstevel@tonic-gate 	controller->is_stats.reads += disk->is_stats.reads;
4900Sstevel@tonic-gate 	controller->is_stats.writes += disk->is_stats.writes;
4910Sstevel@tonic-gate 	controller->is_stats.wtime += disk->is_stats.wtime;
4920Sstevel@tonic-gate 	controller->is_stats.wlentime += disk->is_stats.wlentime;
4930Sstevel@tonic-gate 	controller->is_stats.rtime += disk->is_stats.rtime;
4940Sstevel@tonic-gate 	controller->is_stats.rlentime += disk->is_stats.rlentime;
4950Sstevel@tonic-gate 	controller->is_crtime += disk->is_crtime;
4960Sstevel@tonic-gate 	controller->is_snaptime += disk->is_snaptime;
4970Sstevel@tonic-gate 	if (kstat_add(&disk->is_errors, &controller->is_errors))
4980Sstevel@tonic-gate 		return (errno);
4990Sstevel@tonic-gate 	return (0);
5000Sstevel@tonic-gate }
5010Sstevel@tonic-gate 
5020Sstevel@tonic-gate static int
acquire_iodev_stats(struct iodev_snapshot * list,kstat_ctl_t * kc)5030Sstevel@tonic-gate acquire_iodev_stats(struct iodev_snapshot *list, kstat_ctl_t *kc)
5040Sstevel@tonic-gate {
5050Sstevel@tonic-gate 	struct iodev_snapshot *pos;
5060Sstevel@tonic-gate 	int err = 0;
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate 	for (pos = list; pos; pos = pos->is_next) {
5090Sstevel@tonic-gate 		/* controllers don't have stats (yet) */
5100Sstevel@tonic-gate 		if (pos->is_ksp != NULL) {
5110Sstevel@tonic-gate 			if (kstat_read(kc, pos->is_ksp, &pos->is_stats) == -1)
5120Sstevel@tonic-gate 				return (errno);
5130Sstevel@tonic-gate 			/* make sure crtime/snaptime is updated */
5140Sstevel@tonic-gate 			pos->is_crtime = pos->is_ksp->ks_crtime;
5150Sstevel@tonic-gate 			pos->is_snaptime = pos->is_ksp->ks_snaptime;
5160Sstevel@tonic-gate 		}
5170Sstevel@tonic-gate 
5180Sstevel@tonic-gate 		if ((err = acquire_iodev_stats(pos->is_children, kc)))
5190Sstevel@tonic-gate 			return (err);
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 		if (pos->is_type == IODEV_CONTROLLER) {
5220Sstevel@tonic-gate 			struct iodev_snapshot *pos2 = pos->is_children;
5230Sstevel@tonic-gate 
5240Sstevel@tonic-gate 			for (; pos2; pos2 = pos2->is_next) {
5250Sstevel@tonic-gate 				if ((err = collate_controller(pos, pos2)))
5260Sstevel@tonic-gate 					return (err);
5270Sstevel@tonic-gate 			}
5280Sstevel@tonic-gate 		}
5290Sstevel@tonic-gate 	}
5300Sstevel@tonic-gate 
5310Sstevel@tonic-gate 	return (0);
5320Sstevel@tonic-gate }
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate static int
acquire_iodev_errors(struct snapshot * ss,kstat_ctl_t * kc)5350Sstevel@tonic-gate acquire_iodev_errors(struct snapshot *ss, kstat_ctl_t *kc)
5360Sstevel@tonic-gate {
5370Sstevel@tonic-gate 	kstat_t *ksp;
5380Sstevel@tonic-gate 
5390Sstevel@tonic-gate 	if (!(ss->s_types && SNAP_IODEV_ERRORS))
5400Sstevel@tonic-gate 		return (0);
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
5430Sstevel@tonic-gate 		char kstat_name[KSTAT_STRLEN];
5440Sstevel@tonic-gate 		char *dname = kstat_name;
5450Sstevel@tonic-gate 		char *ename = ksp->ks_name;
5460Sstevel@tonic-gate 		struct iodev_snapshot *iodev;
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 		if (ksp->ks_type != KSTAT_TYPE_NAMED)
5490Sstevel@tonic-gate 			continue;
5500Sstevel@tonic-gate 		if (strncmp(ksp->ks_class, "device_error", 12) != 0 &&
5510Sstevel@tonic-gate 		    strncmp(ksp->ks_class, "iopath_error", 12) != 0)
5520Sstevel@tonic-gate 			continue;
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 		/*
5550Sstevel@tonic-gate 		 * Some drivers may not follow the naming convention
5560Sstevel@tonic-gate 		 * for error kstats (i.e., drivername,err) so
5570Sstevel@tonic-gate 		 * be sure we don't walk off the end.
5580Sstevel@tonic-gate 		 */
5590Sstevel@tonic-gate 		while (*ename && *ename != ',') {
5600Sstevel@tonic-gate 			*dname = *ename;
5610Sstevel@tonic-gate 			dname++;
5620Sstevel@tonic-gate 			ename++;
5630Sstevel@tonic-gate 		}
5640Sstevel@tonic-gate 		*dname = '\0';
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 		iodev = find_iodev_by_name(ss->s_iodevs, kstat_name);
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate 		if (iodev == NULL)
5690Sstevel@tonic-gate 			continue;
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 		if (kstat_read(kc, ksp, NULL) == -1)
5720Sstevel@tonic-gate 			return (errno);
5730Sstevel@tonic-gate 		if (kstat_copy(ksp, &iodev->is_errors) == -1)
5740Sstevel@tonic-gate 			return (errno);
5750Sstevel@tonic-gate 	}
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 	return (0);
5780Sstevel@tonic-gate }
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate static void
get_ids(struct iodev_snapshot * iodev,const char * pretty)5810Sstevel@tonic-gate get_ids(struct iodev_snapshot *iodev, const char *pretty)
5820Sstevel@tonic-gate {
5830Sstevel@tonic-gate 	int ctr, disk, slice, ret;
5840Sstevel@tonic-gate 	char *target;
5850Sstevel@tonic-gate 	const char *p1;
5860Sstevel@tonic-gate 	const char *p2;
5870Sstevel@tonic-gate 
5880Sstevel@tonic-gate 	if (pretty == NULL)
5890Sstevel@tonic-gate 		return;
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 	if (sscanf(pretty, "c%d", &ctr) != 1)
5920Sstevel@tonic-gate 		return;
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 	p1 = pretty;
5950Sstevel@tonic-gate 	while (*p1 && *p1 != 't')
5960Sstevel@tonic-gate 		++p1;
5970Sstevel@tonic-gate 
5980Sstevel@tonic-gate 	if (!*p1)
5990Sstevel@tonic-gate 		return;
6000Sstevel@tonic-gate 	++p1;
6010Sstevel@tonic-gate 
6020Sstevel@tonic-gate 	p2 = p1;
6030Sstevel@tonic-gate 	while (*p2 && *p2 != 'd')
6040Sstevel@tonic-gate 		++p2;
6050Sstevel@tonic-gate 
6060Sstevel@tonic-gate 	if (!*p2 || p2 == p1)
6070Sstevel@tonic-gate 		return;
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	target = safe_alloc(1 + p2 - p1);
6100Sstevel@tonic-gate 	(void) strlcpy(target, p1, 1 + p2 - p1);
6110Sstevel@tonic-gate 
6120Sstevel@tonic-gate 	ret = sscanf(p2, "d%d%*[sp]%d", &disk, &slice);
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	if (ret == 2 && iodev->is_type == IODEV_PARTITION) {
6150Sstevel@tonic-gate 		iodev->is_id.id = slice;
6160Sstevel@tonic-gate 		iodev->is_parent_id.id = disk;
6170Sstevel@tonic-gate 		(void) strlcpy(iodev->is_parent_id.tid, target, KSTAT_STRLEN);
6180Sstevel@tonic-gate 	} else if (ret == 1) {
6190Sstevel@tonic-gate 		if (iodev->is_type == IODEV_DISK) {
6200Sstevel@tonic-gate 			iodev->is_id.id = disk;
6210Sstevel@tonic-gate 			(void) strlcpy(iodev->is_id.tid, target, KSTAT_STRLEN);
6220Sstevel@tonic-gate 			iodev->is_parent_id.id = ctr;
6232907Scth 		} else if (iodev->is_type == IODEV_IOPATH_LTI) {
6240Sstevel@tonic-gate 			iodev->is_parent_id.id = disk;
6250Sstevel@tonic-gate 			(void) strlcpy(iodev->is_parent_id.tid,
6265957Swroche 			    target, KSTAT_STRLEN);
6270Sstevel@tonic-gate 		}
6280Sstevel@tonic-gate 	}
6290Sstevel@tonic-gate 
6300Sstevel@tonic-gate 	free(target);
6310Sstevel@tonic-gate }
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate static void
get_pretty_name(enum snapshot_types types,struct iodev_snapshot * iodev,kstat_ctl_t * kc)6340Sstevel@tonic-gate get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev,
6350Sstevel@tonic-gate 	kstat_ctl_t *kc)
6360Sstevel@tonic-gate {
6372907Scth 	disk_list_t	*dl;
6382907Scth 	char		*pretty = NULL;
6390Sstevel@tonic-gate 
6400Sstevel@tonic-gate 	if (iodev->is_type == IODEV_NFS) {
6410Sstevel@tonic-gate 		if (!(types & SNAP_IODEV_PRETTY))
6420Sstevel@tonic-gate 			return;
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 		iodev->is_pretty = lookup_nfs_name(iodev->is_name, kc);
6450Sstevel@tonic-gate 		return;
6460Sstevel@tonic-gate 	}
6470Sstevel@tonic-gate 
6482907Scth 	/* lookup/translate the kstat name */
6492723Scth 	dl = lookup_ks_name(iodev->is_name, (types & SNAP_IODEV_DEVID) ? 1 : 0);
6500Sstevel@tonic-gate 	if (dl == NULL)
6510Sstevel@tonic-gate 		return;
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	if (dl->dsk)
6540Sstevel@tonic-gate 		pretty = safe_strdup(dl->dsk);
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate 	if (types & SNAP_IODEV_PRETTY) {
6570Sstevel@tonic-gate 		if (dl->dname)
6580Sstevel@tonic-gate 			iodev->is_dname = safe_strdup(dl->dname);
6590Sstevel@tonic-gate 	}
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 	if (dl->devidstr)
6620Sstevel@tonic-gate 		iodev->is_devid = safe_strdup(dl->devidstr);
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	get_ids(iodev, pretty);
6650Sstevel@tonic-gate 
6662907Scth 	/*
6672907Scth 	 * we fill in pretty name wether it is asked for or not because
6682907Scth 	 * it could be used in a filter by match_iodevs.
6692907Scth 	 */
6702907Scth 	iodev->is_pretty = pretty;
6710Sstevel@tonic-gate }
6720Sstevel@tonic-gate 
6730Sstevel@tonic-gate static enum iodev_type
get_iodev_type(kstat_t * ksp)6740Sstevel@tonic-gate get_iodev_type(kstat_t *ksp)
6750Sstevel@tonic-gate {
6760Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "disk") == 0)
6770Sstevel@tonic-gate 		return (IODEV_DISK);
6780Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "partition") == 0)
6790Sstevel@tonic-gate 		return (IODEV_PARTITION);
6800Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "nfs") == 0)
6810Sstevel@tonic-gate 		return (IODEV_NFS);
6820Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "iopath") == 0)
6832907Scth 		return (IODEV_IOPATH_LTI);
6840Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "tape") == 0)
6850Sstevel@tonic-gate 		return (IODEV_TAPE);
6860Sstevel@tonic-gate 	return (IODEV_UNKNOWN);
6870Sstevel@tonic-gate }
6880Sstevel@tonic-gate 
6892907Scth /* get the lun/target/initiator from the name, return 1 on success */
6902907Scth static int
get_lti(char * s,char * lname,int * l,char * tname,int * t,char * iname,int * i)6912907Scth get_lti(char *s,
6922907Scth 	char *lname, int *l, char *tname, int *t, char *iname, int *i)
6932907Scth {
6942907Scth 	int  num = 0;
6952907Scth 
6969782SChris.Liu@Sun.COM 	num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z_]%d", lname, l,
6972907Scth 	    tname, t, iname, i);
6982907Scth 	return ((num == 6) ? 1 : 0);
6992907Scth }
7002907Scth 
7012907Scth 
7022907Scth /* get the lun, target, and initiator name and instance */
7032907Scth static void
get_path_info(struct iodev_snapshot * io,char * mod,size_t modlen,int * type,int * inst,char * name,size_t size)704*12920SJonathan.Ca@Sun.COM get_path_info(struct iodev_snapshot *io, char *mod, size_t modlen, int *type,
705*12920SJonathan.Ca@Sun.COM     int *inst, char *name, size_t size)
7062907Scth {
7072907Scth 
7082907Scth 	/*
7092907Scth 	 * If it is iopath or ssd then pad the name with i/t/l so we can sort
7102907Scth 	 * by alpha order and set type for IOPATH to DISK since we want to
7112907Scth 	 * have it grouped with its ssd parent. The lun can be 5 digits,
7122907Scth 	 * the target can be 4 digits, and the initiator can be 3 digits and
7132907Scth 	 * the padding is done appropriately for string comparisons.
7142907Scth 	 */
7152907Scth 	if (disk_or_partition_or_iopath(io->is_type)) {
7162907Scth 		int i1, t1, l1;
7172907Scth 		char tname[KSTAT_STRLEN], iname[KSTAT_STRLEN];
7182907Scth 		char *ptr, lname[KSTAT_STRLEN];
7192907Scth 
7202907Scth 		i1 = t1 = l1 = 0;
7212907Scth 		(void) get_lti(io->is_name, lname, &l1, tname, &t1, iname, &i1);
7222907Scth 		*type = io->is_type;
7232907Scth 		if (io->is_type == IODEV_DISK) {
7242907Scth 			(void) snprintf(name, size, "%s%05d", lname, l1);
7252907Scth 		} else if (io->is_type == IODEV_PARTITION) {
7262907Scth 			ptr = strchr(io->is_name, ',');
7272907Scth 			(void) snprintf(name, size, "%s%05d%s", lname, l1, ptr);
7282907Scth 		} else {
7292907Scth 			(void) snprintf(name, size, "%s%05d.%s%04d.%s%03d",
7302907Scth 			    lname, l1, tname, t1, iname, i1);
7312907Scth 			/* set to disk so we sort with disks */
7322907Scth 			*type = IODEV_DISK;
7332907Scth 		}
734*12920SJonathan.Ca@Sun.COM 		(void) strlcpy(mod, lname, modlen);
7352907Scth 		*inst = l1;
7362907Scth 	} else {
737*12920SJonathan.Ca@Sun.COM 		(void) strlcpy(mod, io->is_module, modlen);
738*12920SJonathan.Ca@Sun.COM 		(void) strlcpy(name, io->is_name, size);
7392907Scth 		*type = io->is_type;
7402907Scth 		*inst = io->is_instance;
7412907Scth 	}
7422907Scth }
7432907Scth 
7440Sstevel@tonic-gate int
iodev_cmp(struct iodev_snapshot * io1,struct iodev_snapshot * io2)7450Sstevel@tonic-gate iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2)
7460Sstevel@tonic-gate {
7472907Scth 	int	type1, type2;
7482907Scth 	int	inst1, inst2;
7492907Scth 	char	name1[KSTAT_STRLEN], name2[KSTAT_STRLEN];
7502907Scth 	char	mod1[KSTAT_STRLEN], mod2[KSTAT_STRLEN];
7512907Scth 
752*12920SJonathan.Ca@Sun.COM 	get_path_info(io1, mod1, sizeof (mod1), &type1, &inst1, name1,
753*12920SJonathan.Ca@Sun.COM 	    sizeof (name1));
754*12920SJonathan.Ca@Sun.COM 	get_path_info(io2, mod2, sizeof (mod2), &type2, &inst2, name2,
755*12920SJonathan.Ca@Sun.COM 	    sizeof (name2));
7562907Scth 	if ((!disk_or_partition(type1)) ||
7572907Scth 	    (!disk_or_partition(type2))) {
7582907Scth 		/* neutral sort order between disk and part */
7592907Scth 		if (type1 < type2) {
7600Sstevel@tonic-gate 			return (-1);
7612907Scth 		}
7622907Scth 		if (type1 > type2) {
7630Sstevel@tonic-gate 			return (1);
7642907Scth 		}
7650Sstevel@tonic-gate 	}
7660Sstevel@tonic-gate 
7670Sstevel@tonic-gate 	/* controller doesn't have ksp */
7680Sstevel@tonic-gate 	if (io1->is_ksp && io2->is_ksp) {
7692907Scth 		if (strcmp(mod1, mod2) != 0) {
7702907Scth 			return (strcmp(mod1, mod2));
7712907Scth 		}
7722907Scth 		if (inst1 < inst2) {
7730Sstevel@tonic-gate 			return (-1);
7742907Scth 		}
7752907Scth 		if (inst1 > inst2) {
7760Sstevel@tonic-gate 			return (1);
7772907Scth 		}
7780Sstevel@tonic-gate 	} else {
7792907Scth 		if (io1->is_id.id < io2->is_id.id) {
7800Sstevel@tonic-gate 			return (-1);
7812907Scth 		}
7822907Scth 		if (io1->is_id.id > io2->is_id.id) {
7830Sstevel@tonic-gate 			return (1);
7842907Scth 		}
7850Sstevel@tonic-gate 	}
7860Sstevel@tonic-gate 
7872907Scth 	return (strcmp(name1, name2));
7882907Scth }
7892907Scth 
7902907Scth /* update the target reads and writes */
7912907Scth static void
update_target(struct iodev_snapshot * tgt,struct iodev_snapshot * path)7922907Scth update_target(struct iodev_snapshot *tgt, struct iodev_snapshot *path)
7932907Scth {
7942907Scth 	tgt->is_stats.reads += path->is_stats.reads;
7952907Scth 	tgt->is_stats.writes += path->is_stats.writes;
7962907Scth 	tgt->is_stats.nread += path->is_stats.nread;
7972907Scth 	tgt->is_stats.nwritten += path->is_stats.nwritten;
7982907Scth 	tgt->is_stats.wcnt += path->is_stats.wcnt;
7992907Scth 	tgt->is_stats.rcnt += path->is_stats.rcnt;
8002907Scth 
8012907Scth 	/*
8022907Scth 	 * Stash the t_delta in the crtime for use in show_disk
8032907Scth 	 * NOTE: this can't be done in show_disk because the
8042907Scth 	 * itl entry is removed for the old format
8052907Scth 	 */
8062907Scth 	tgt->is_crtime += hrtime_delta(path->is_crtime, path->is_snaptime);
8072907Scth 	tgt->is_snaptime += path->is_snaptime;
8082907Scth 	tgt->is_nr_children += 1;
8092907Scth }
8102907Scth 
8112907Scth /*
8122907Scth  * Create a new synthetic device entry of the specified type. The supported
8132907Scth  * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI.
8142907Scth  */
8152907Scth static struct iodev_snapshot *
make_extended_device(int type,struct iodev_snapshot * old)8162907Scth make_extended_device(int type, struct iodev_snapshot *old)
8172907Scth {
8182907Scth 	struct iodev_snapshot	*tptr = NULL;
8192907Scth 	char			*ptr;
8202907Scth 	int			lun, tgt, initiator;
8212907Scth 	char			lun_name[KSTAT_STRLEN];
8222907Scth 	char			tgt_name[KSTAT_STRLEN];
8232907Scth 	char			initiator_name[KSTAT_STRLEN];
8242907Scth 
8252907Scth 	if (old == NULL)
8262907Scth 		return (NULL);
8272907Scth 	if (get_lti(old->is_name,
8282907Scth 	    lun_name, &lun, tgt_name, &tgt, initiator_name, &initiator) != 1) {
8292907Scth 		return (NULL);
8302907Scth 	}
8312907Scth 	tptr = safe_alloc(sizeof (*old));
8322907Scth 	bzero(tptr, sizeof (*old));
8332907Scth 	if (old->is_pretty != NULL) {
8342907Scth 		tptr->is_pretty = safe_alloc(strlen(old->is_pretty) + 1);
8352907Scth 		(void) strcpy(tptr->is_pretty, old->is_pretty);
8362907Scth 	}
8372907Scth 	bcopy(&old->is_parent_id, &tptr->is_parent_id,
8385957Swroche 	    sizeof (old->is_parent_id));
8392907Scth 
8402907Scth 	tptr->is_type = type;
8412907Scth 
8422907Scth 	if (type == IODEV_IOPATH_LT) {
8432907Scth 		/* make new synthetic entry that is the LT */
8442907Scth 		/* set the id to the target id */
8452907Scth 		tptr->is_id.id = tgt;
8462907Scth 		(void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
8472907Scth 		    "%s%d", tgt_name, tgt);
8482907Scth 		(void) snprintf(tptr->is_name, sizeof (tptr->is_name),
8492907Scth 		    "%s%d.%s%d", lun_name, lun, tgt_name, tgt);
8502907Scth 
8512907Scth 		if (old->is_pretty) {
8522907Scth 			ptr = strrchr(tptr->is_pretty, '.');
8532907Scth 			if (ptr)
8542907Scth 				*ptr = '\0';
8552907Scth 		}
8562907Scth 	} else if (type == IODEV_IOPATH_LI) {
8572907Scth 		/* make new synthetic entry that is the LI */
8582907Scth 		/* set the id to the initiator number */
8592907Scth 		tptr->is_id.id = initiator;
8602907Scth 		(void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
8612907Scth 		    "%s%d", initiator_name, initiator);
8622907Scth 		(void) snprintf(tptr->is_name, sizeof (tptr->is_name),
8632907Scth 		    "%s%d.%s%d", lun_name, lun, initiator_name, initiator);
8642907Scth 
8652907Scth 		if (old->is_pretty) {
8662907Scth 			ptr = strchr(tptr->is_pretty, '.');
8672907Scth 			if (ptr)
8682907Scth 				(void) snprintf(ptr + 1,
8692907Scth 				    strlen(tptr->is_pretty) + 1,
8702907Scth 				    "%s%d", initiator_name, initiator);
8712907Scth 		}
8722907Scth 	}
8732907Scth 	return (tptr);
8742907Scth }
8752907Scth 
8762907Scth /*
8772907Scth  * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat
8782907Scth  * is found - traverse the children looking for the same initiator and sum
8792907Scth  * them up. Add an LI entry and delete all of the LTI entries with the same
8802907Scth  * initiator.
8812907Scth  */
8822907Scth static int
create_li_delete_lti(struct snapshot * ss,struct iodev_snapshot * list)8832907Scth create_li_delete_lti(struct snapshot *ss, struct iodev_snapshot *list)
8842907Scth {
8852907Scth 	struct iodev_snapshot	*pos, *entry, *parent;
8862907Scth 	int			lun, tgt, initiator;
8872907Scth 	char			lun_name[KSTAT_STRLEN];
8882907Scth 	char			tgt_name[KSTAT_STRLEN];
8892907Scth 	char			initiator_name[KSTAT_STRLEN];
8902907Scth 	int			err;
8912907Scth 
8922907Scth 	for (entry = list; entry; entry = entry->is_next) {
8932907Scth 		if ((err = create_li_delete_lti(ss, entry->is_children)) != 0)
8942907Scth 			return (err);
8952907Scth 
8962907Scth 		if (entry->is_type == IODEV_IOPATH_LTI) {
8972907Scth 			parent = find_parent(ss, entry);
8982907Scth 			if (get_lti(entry->is_name, lun_name, &lun,
8992907Scth 			    tgt_name, &tgt, initiator_name, &initiator) != 1) {
9002907Scth 				return (1);
9012907Scth 			}
9022907Scth 
9032907Scth 			pos = (parent == NULL) ? NULL : parent->is_children;
9042907Scth 			for (; pos; pos = pos->is_next) {
9052907Scth 				if (pos->is_id.id != -1 &&
9062907Scth 				    pos->is_id.id == initiator &&
9072907Scth 				    pos->is_type == IODEV_IOPATH_LI) {
9082907Scth 					/* found the same initiator */
9092907Scth 					update_target(pos, entry);
9102907Scth 					list_del(&parent->is_children, entry);
9112907Scth 					free_iodev(entry);
9122907Scth 					parent->is_nr_children--;
9132907Scth 					entry = pos;
9142907Scth 					break;
9152907Scth 				}
9162907Scth 			}
9172907Scth 
9182907Scth 			if (!pos) {
9192907Scth 				/* make the first LI entry */
9202907Scth 				pos = make_extended_device(
9212907Scth 				    IODEV_IOPATH_LI, entry);
9222907Scth 				update_target(pos, entry);
9232907Scth 
9242907Scth 				if (parent) {
9252907Scth 					insert_before(&parent->is_children,
9262907Scth 					    entry, pos);
9272907Scth 					list_del(&parent->is_children, entry);
9282907Scth 					free_iodev(entry);
9292907Scth 				} else {
9302907Scth 					insert_before(&ss->s_iodevs, entry,
9312907Scth 					    pos);
9322907Scth 					list_del(&ss->s_iodevs, entry);
9332907Scth 					free_iodev(entry);
9342907Scth 				}
9352907Scth 				entry = pos;
9362907Scth 			}
9372907Scth 		}
9382907Scth 	}
9392907Scth 	return (0);
9402907Scth }
9412907Scth 
9422907Scth /*
9432907Scth  * We have the LTI kstat, now add an entry for the LT that sums up all of
9442907Scth  * the LTI's with the same target(t).
9452907Scth  */
9462907Scth static int
create_lt(struct snapshot * ss,struct iodev_snapshot * list)9472907Scth create_lt(struct snapshot *ss, struct iodev_snapshot *list)
9482907Scth {
9492907Scth 	struct iodev_snapshot	*entry, *parent, *pos;
9502907Scth 	int			lun, tgt, initiator;
9512907Scth 	char			lun_name[KSTAT_STRLEN];
9522907Scth 	char			tgt_name[KSTAT_STRLEN];
9532907Scth 	char			initiator_name[KSTAT_STRLEN];
9542907Scth 	int			err;
9552907Scth 
9562907Scth 	for (entry = list; entry; entry = entry->is_next) {
9572907Scth 		if ((err = create_lt(ss, entry->is_children)) != 0)
9582907Scth 			return (err);
9592907Scth 
9602907Scth 		if (entry->is_type == IODEV_IOPATH_LTI) {
9612907Scth 			parent = find_parent(ss, entry);
9622907Scth 			if (get_lti(entry->is_name, lun_name, &lun,
9632907Scth 			    tgt_name, &tgt, initiator_name, &initiator) != 1) {
9642907Scth 				return (1);
9652907Scth 			}
9662907Scth 
9672907Scth 			pos = (parent == NULL) ? NULL : parent->is_children;
9682907Scth 			for (; pos; pos = pos->is_next) {
9692907Scth 				if (pos->is_id.id != -1 &&
9702907Scth 				    pos->is_id.id == tgt &&
9712907Scth 				    pos->is_type == IODEV_IOPATH_LT) {
9722907Scth 					/* found the same target */
9732907Scth 					update_target(pos, entry);
9742907Scth 					break;
9752907Scth 				}
9762907Scth 			}
9772907Scth 
9782907Scth 			if (!pos) {
9792907Scth 				pos = make_extended_device(
9802907Scth 				    IODEV_IOPATH_LT, entry);
9812907Scth 				update_target(pos, entry);
9822907Scth 
9832907Scth 				if (parent) {
9842907Scth 					insert_before(&parent->is_children,
9852907Scth 					    entry, pos);
9862907Scth 					parent->is_nr_children++;
9872907Scth 				} else {
9882907Scth 					insert_before(&ss->s_iodevs,
9892907Scth 					    entry, pos);
9902907Scth 				}
9912907Scth 			}
9922907Scth 		}
9932907Scth 	}
9942907Scth 	return (0);
9952907Scth }
9962907Scth 
9972907Scth /* Find the longest is_name field to aid formatting of output */
9982907Scth static int
iodevs_is_name_maxlen(struct iodev_snapshot * list)9992907Scth iodevs_is_name_maxlen(struct iodev_snapshot *list)
10002907Scth {
10012907Scth 	struct iodev_snapshot	*entry;
10022907Scth 	int			max = 0, cmax, len;
10032907Scth 
10042907Scth 	for (entry = list; entry; entry = entry->is_next) {
10052907Scth 		cmax = iodevs_is_name_maxlen(entry->is_children);
10062907Scth 		max = (cmax > max) ? cmax : max;
10072907Scth 		len = strlen(entry->is_name);
10082907Scth 		max = (len > max) ? len : max;
10092907Scth 	}
10102907Scth 	return (max);
10110Sstevel@tonic-gate }
10120Sstevel@tonic-gate 
10130Sstevel@tonic-gate int
acquire_iodevs(struct snapshot * ss,kstat_ctl_t * kc,struct iodev_filter * df)10140Sstevel@tonic-gate acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df)
10150Sstevel@tonic-gate {
10162907Scth 	kstat_t	*ksp;
10172907Scth 	struct	iodev_snapshot *pos;
10182907Scth 	struct	iodev_snapshot *list = NULL;
10192907Scth 	int	err = 0;
10200Sstevel@tonic-gate 
10210Sstevel@tonic-gate 	ss->s_nr_iodevs = 0;
10222907Scth 	ss->s_iodevs_is_name_maxlen = 0;
10230Sstevel@tonic-gate 
10242723Scth 	/*
10252723Scth 	 * Call cleanup_iodevs_snapshot() so that a cache miss in
10262723Scth 	 * lookup_ks_name() will result in a fresh snapshot.
10272723Scth 	 */
10282723Scth 	cleanup_iodevs_snapshot();
10292723Scth 
10300Sstevel@tonic-gate 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
10310Sstevel@tonic-gate 		enum iodev_type type;
10320Sstevel@tonic-gate 
10330Sstevel@tonic-gate 		if (ksp->ks_type != KSTAT_TYPE_IO)
10340Sstevel@tonic-gate 			continue;
10350Sstevel@tonic-gate 
10360Sstevel@tonic-gate 		/* e.g. "usb_byte_count" is not handled */
10370Sstevel@tonic-gate 		if ((type = get_iodev_type(ksp)) == IODEV_UNKNOWN)
10380Sstevel@tonic-gate 			continue;
10390Sstevel@tonic-gate 
10400Sstevel@tonic-gate 		if (df && !(type & df->if_allowed_types))
10410Sstevel@tonic-gate 			continue;
10420Sstevel@tonic-gate 
10430Sstevel@tonic-gate 		if ((pos = malloc(sizeof (struct iodev_snapshot))) == NULL) {
10440Sstevel@tonic-gate 			err = errno;
10450Sstevel@tonic-gate 			goto out;
10460Sstevel@tonic-gate 		}
10470Sstevel@tonic-gate 
10480Sstevel@tonic-gate 		(void) memset(pos, 0, sizeof (struct iodev_snapshot));
10490Sstevel@tonic-gate 
10500Sstevel@tonic-gate 		pos->is_type = type;
10510Sstevel@tonic-gate 		pos->is_crtime = ksp->ks_crtime;
10520Sstevel@tonic-gate 		pos->is_snaptime = ksp->ks_snaptime;
10530Sstevel@tonic-gate 		pos->is_id.id = IODEV_NO_ID;
10540Sstevel@tonic-gate 		pos->is_parent_id.id = IODEV_NO_ID;
10550Sstevel@tonic-gate 		pos->is_ksp = ksp;
10560Sstevel@tonic-gate 		pos->is_instance = ksp->ks_instance;
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate 		(void) strlcpy(pos->is_module, ksp->ks_module, KSTAT_STRLEN);
10590Sstevel@tonic-gate 		(void) strlcpy(pos->is_name, ksp->ks_name, KSTAT_STRLEN);
10600Sstevel@tonic-gate 		get_pretty_name(ss->s_types, pos, kc);
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 		/*
10630Sstevel@tonic-gate 		 * We must insert in sort order so e.g. vmstat -l
10640Sstevel@tonic-gate 		 * chooses in order.
10650Sstevel@tonic-gate 		 */
10660Sstevel@tonic-gate 		insert_into(&list, pos);
10670Sstevel@tonic-gate 	}
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	choose_iodevs(ss, list, df);
10700Sstevel@tonic-gate 
10710Sstevel@tonic-gate 	/* before acquire_stats for collate_controller()'s benefit */
10720Sstevel@tonic-gate 	if (ss->s_types & SNAP_IODEV_ERRORS) {
10730Sstevel@tonic-gate 		if ((err = acquire_iodev_errors(ss, kc)) != 0)
10740Sstevel@tonic-gate 			goto out;
10750Sstevel@tonic-gate 	}
10760Sstevel@tonic-gate 
10770Sstevel@tonic-gate 	if ((err = acquire_iodev_stats(ss->s_iodevs, kc)) != 0)
10780Sstevel@tonic-gate 		goto out;
10790Sstevel@tonic-gate 
10802907Scth 	if (ss->s_types & SNAP_IOPATHS_LTI) {
10812907Scth 		/*
10822907Scth 		 * -Y: kstats are LTI, need to create a synthetic LT
10832907Scth 		 * for -Y output.
10842907Scth 		 */
10852907Scth 		if ((err = create_lt(ss, ss->s_iodevs)) != 0) {
10862907Scth 			return (err);
10872907Scth 		}
10882907Scth 	}
10892907Scth 	if (ss->s_types & SNAP_IOPATHS_LI) {
10902907Scth 		/*
10912907Scth 		 * -X: kstats are LTI, need to create a synthetic LI and
10922907Scth 		 * delete the LTI for -X output
10932907Scth 		 */
10942907Scth 		if ((err = create_li_delete_lti(ss, ss->s_iodevs)) != 0) {
10952907Scth 			return (err);
10962907Scth 		}
10972907Scth 	}
10982907Scth 
10992907Scth 	/* determine width of longest is_name */
11002907Scth 	ss->s_iodevs_is_name_maxlen = iodevs_is_name_maxlen(ss->s_iodevs);
11012907Scth 
11020Sstevel@tonic-gate 	err = 0;
11030Sstevel@tonic-gate out:
11040Sstevel@tonic-gate 	return (err);
11050Sstevel@tonic-gate }
11060Sstevel@tonic-gate 
11070Sstevel@tonic-gate void
free_iodev(struct iodev_snapshot * iodev)11080Sstevel@tonic-gate free_iodev(struct iodev_snapshot *iodev)
11090Sstevel@tonic-gate {
11100Sstevel@tonic-gate 	while (iodev->is_children) {
11110Sstevel@tonic-gate 		struct iodev_snapshot *tmp = iodev->is_children;
11120Sstevel@tonic-gate 		iodev->is_children = iodev->is_children->is_next;
11130Sstevel@tonic-gate 		free_iodev(tmp);
11140Sstevel@tonic-gate 	}
11150Sstevel@tonic-gate 
11165957Swroche 	if (iodev->avl_list) {
11175957Swroche 		avl_remove(iodev->avl_list, iodev);
11185957Swroche 		if (avl_numnodes(iodev->avl_list) == 0) {
11195957Swroche 			avl_destroy(iodev->avl_list);
11205957Swroche 			free(iodev->avl_list);
11215957Swroche 		}
11225957Swroche 	}
11235957Swroche 
11240Sstevel@tonic-gate 	free(iodev->is_errors.ks_data);
11250Sstevel@tonic-gate 	free(iodev->is_pretty);
11260Sstevel@tonic-gate 	free(iodev->is_dname);
11270Sstevel@tonic-gate 	free(iodev->is_devid);
11280Sstevel@tonic-gate 	free(iodev);
11290Sstevel@tonic-gate }
1130