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