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