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.
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.
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]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include "statcommon.h"
28 #include <sys/dklabel.h>
29 #include <sys/dktp/fdisk.h>
38 static void insert_iodev(struct snapshot
*ss
, struct iodev_snapshot
*iodev
);
40 static struct iodev_snapshot
*
41 make_controller(int cid
)
43 struct iodev_snapshot
*new;
45 new = safe_alloc(sizeof (struct iodev_snapshot
));
46 (void) memset(new, 0, sizeof (struct iodev_snapshot
));
47 new->is_type
= IODEV_CONTROLLER
;
49 new->is_parent_id
.id
= IODEV_NO_ID
;
51 (void) snprintf(new->is_name
, sizeof (new->is_name
), "c%d", cid
);
56 static struct iodev_snapshot
*
57 find_iodev_by_name(struct iodev_snapshot
*list
, const char *name
)
59 struct iodev_snapshot
*pos
;
60 struct iodev_snapshot
*pos2
;
62 for (pos
= list
; pos
; pos
= pos
->is_next
) {
63 if (strcmp(pos
->is_name
, name
) == 0)
66 pos2
= find_iodev_by_name(pos
->is_children
, name
);
74 static enum iodev_type
75 parent_iodev_type(enum iodev_type 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
);
87 return (IODEV_UNKNOWN
);
91 id_match(struct iodev_id
*id1
, struct iodev_id
*id2
)
93 return (id1
->id
== id2
->id
&&
94 strcmp(id1
->tid
, id2
->tid
) == 0);
97 static struct iodev_snapshot
*
98 find_parent(struct snapshot
*ss
, struct iodev_snapshot
*iodev
)
100 enum iodev_type parent_type
= parent_iodev_type(iodev
->is_type
);
101 struct iodev_snapshot
*pos
;
102 struct iodev_snapshot
*pos2
;
104 if (parent_type
== 0 || parent_type
== IODEV_UNKNOWN
)
107 if (iodev
->is_parent_id
.id
== IODEV_NO_ID
&&
108 iodev
->is_parent_id
.tid
[0] == '\0')
111 if (parent_type
== IODEV_CONTROLLER
) {
112 for (pos
= ss
->s_iodevs
; pos
; pos
= pos
->is_next
) {
113 if (pos
->is_type
!= IODEV_CONTROLLER
)
115 if (pos
->is_id
.id
!= iodev
->is_parent_id
.id
)
120 if (!(ss
->s_types
& SNAP_CONTROLLERS
))
123 pos
= make_controller(iodev
->is_parent_id
.id
);
124 insert_iodev(ss
, pos
);
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
)
133 if (pos
->is_type
!= IODEV_CONTROLLER
)
135 for (pos2
= pos
->is_children
; pos2
; pos2
= pos2
->is_next
) {
136 if (pos2
->is_type
!= IODEV_DISK
)
138 if (id_match(&iodev
->is_parent_id
, &pos2
->is_id
))
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.
152 avl_iodev_cmp(const void* is1
, const void* is2
)
154 int c
= iodev_cmp((struct iodev_snapshot
*)is1
,
155 (struct iodev_snapshot
*)is2
);
167 ix_new_list(struct iodev_snapshot
*elem
)
169 avl_tree_t
*l
= malloc(sizeof (avl_tree_t
));
175 avl_create(l
, avl_iodev_cmp
, sizeof (struct iodev_snapshot
),
176 offsetof(struct iodev_snapshot
, avl_link
));
182 ix_list_del(struct iodev_snapshot
*elem
)
184 avl_tree_t
*l
= elem
->avl_list
;
189 elem
->avl_list
= NULL
;
192 if (avl_numnodes(l
) == 0) {
199 ix_insert_here(struct iodev_snapshot
*pos
, struct iodev_snapshot
*elem
, int ba
)
201 avl_tree_t
*l
= pos
->avl_list
;
207 avl_insert_here(l
, elem
, pos
, ba
);
211 list_del(struct iodev_snapshot
**list
, struct iodev_snapshot
*pos
)
216 *list
= pos
->is_next
;
218 pos
->is_next
->is_prev
= pos
->is_prev
;
220 pos
->is_prev
->is_next
= pos
->is_next
;
221 pos
->is_prev
= pos
->is_next
= NULL
;
225 insert_before(struct iodev_snapshot
**list
, struct iodev_snapshot
*pos
,
226 struct iodev_snapshot
*new)
229 new->is_prev
= new->is_next
= NULL
;
236 new->is_prev
= pos
->is_prev
;
238 pos
->is_prev
->is_next
= new;
243 ix_insert_here(pos
, new, AVL_BEFORE
);
247 insert_after(struct iodev_snapshot
**list
, struct iodev_snapshot
*pos
,
248 struct iodev_snapshot
*new)
251 new->is_prev
= new->is_next
= NULL
;
257 new->is_next
= pos
->is_next
;
260 pos
->is_next
->is_prev
= new;
263 ix_insert_here(pos
, new, AVL_AFTER
);
267 insert_into(struct iodev_snapshot
**list
, struct iodev_snapshot
*iodev
)
269 struct iodev_snapshot
*tmp
= *list
;
281 * Optimize the search: instead of walking the entire list
282 * (which can contain thousands of nodes), search in the AVL
283 * tree the nearest node and reposition the startup point to
284 * this node rather than always starting from the beginning
289 p
= avl_find(l
, iodev
, &where
);
291 p
= avl_nearest(l
, where
, AVL_BEFORE
);
294 tmp
= (struct iodev_snapshot
*)p
;
299 if (iodev_cmp(tmp
, iodev
) > 0) {
300 insert_before(list
, tmp
, iodev
);
304 if (tmp
->is_next
== NULL
)
310 insert_after(list
, tmp
, iodev
);
314 disk_or_partition(enum iodev_type type
)
316 return (type
== IODEV_DISK
|| type
== IODEV_PARTITION
);
320 disk_or_partition_or_iopath(enum iodev_type type
)
322 return (type
== IODEV_DISK
|| type
== IODEV_PARTITION
||
323 type
== IODEV_IOPATH_LTI
);
327 insert_iodev(struct snapshot
*ss
, struct iodev_snapshot
*iodev
)
329 struct iodev_snapshot
*parent
= find_parent(ss
, iodev
);
330 struct iodev_snapshot
**list
;
332 if (parent
!= NULL
) {
333 list
= &parent
->is_children
;
334 parent
->is_nr_children
++;
336 list
= &ss
->s_iodevs
;
340 insert_into(list
, iodev
);
343 /* return 1 if dev passes filter */
345 iodev_match(struct iodev_snapshot
*dev
, struct iodev_filter
*df
)
347 int is_floppy
= (strncmp(dev
->is_name
, "fd", 2) == 0);
348 char *isn
, *ispn
, *ifn
;
353 /* no filter, pass */
355 return (1); /* pass */
357 /* no filtered names, pass if not floppy and skipped */
358 if (df
->if_nr_names
== 0)
359 return (!(df
->if_skip_floppy
&& is_floppy
));
362 ispn
= dev
->is_pretty
;
363 for (i
= 0; i
< df
->if_nr_names
; i
++) {
364 ifn
= df
->if_names
[i
];
366 path
= strchr(ifn
, '.');
368 if ((strcmp(isn
, ifn
) == 0) ||
369 (ispn
&& (strcmp(ispn
, ifn
) == 0)))
370 return (1); /* pass */
372 /* if filter is a path allow partial match */
374 ((strncmp(isn
, ifn
, ifnl
) == 0) ||
375 (ispn
&& (strncmp(ispn
, ifn
, ifnl
) == 0))))
376 return (1); /* pass */
379 return (0); /* fail */
382 /* return 1 if path is an mpxio path associated with dev */
384 iodev_path_match(struct iodev_snapshot
*dev
, struct iodev_snapshot
*path
)
393 if ((strncmp(pn
, dn
, dnl
) == 0) && (pn
[dnl
] == '.'))
394 return (1); /* yes */
399 /* select which I/O devices to collect stats for */
401 choose_iodevs(struct snapshot
*ss
, struct iodev_snapshot
*iodevs
,
402 struct iodev_filter
*df
)
404 struct iodev_snapshot
*pos
, *ppos
, *tmp
, *ptmp
;
408 nr_iodevs
= df
? df
->if_max_iodevs
: UNLIMITED_IODEVS
;
409 nr_iodevs_orig
= nr_iodevs
;
411 if (nr_iodevs
== UNLIMITED_IODEVS
)
414 /* add the full matches */
416 while (pos
&& nr_iodevs
) {
420 if (!iodev_match(tmp
, df
))
421 continue; /* failed full match */
423 list_del(&iodevs
, tmp
);
424 insert_iodev(ss
, tmp
);
427 * Add all mpxio paths associated with match above. Added
428 * paths don't count against nr_iodevs.
430 if (strchr(tmp
->is_name
, '.') == NULL
) {
434 ppos
= ppos
->is_next
;
436 if (!iodev_path_match(tmp
, ptmp
))
437 continue; /* not an mpxio path */
439 list_del(&iodevs
, ptmp
);
440 insert_iodev(ss
, ptmp
);
450 * If we had a filter, and *nothing* passed the filter then we
451 * don't want to fill the remaining slots - it is just confusing
452 * if we don that, it makes it look like the filter code is broken.
454 if ((df
->if_nr_names
== 0) || (nr_iodevs
!= nr_iodevs_orig
)) {
455 /* now insert any iodevs into the remaining slots */
457 while (pos
&& nr_iodevs
) {
461 if (df
&& df
->if_skip_floppy
&&
462 strncmp(tmp
->is_name
, "fd", 2) == 0)
465 list_del(&iodevs
, tmp
);
466 insert_iodev(ss
, tmp
);
472 /* clear the unwanted ones */
475 struct iodev_snapshot
*tmp
= pos
;
482 collate_controller(struct iodev_snapshot
*controller
,
483 struct iodev_snapshot
*disk
)
485 controller
->is_stats
.nread
+= disk
->is_stats
.nread
;
486 controller
->is_stats
.nwritten
+= disk
->is_stats
.nwritten
;
487 controller
->is_stats
.reads
+= disk
->is_stats
.reads
;
488 controller
->is_stats
.writes
+= disk
->is_stats
.writes
;
489 controller
->is_stats
.wtime
+= disk
->is_stats
.wtime
;
490 controller
->is_stats
.wlentime
+= disk
->is_stats
.wlentime
;
491 controller
->is_stats
.rtime
+= disk
->is_stats
.rtime
;
492 controller
->is_stats
.rlentime
+= disk
->is_stats
.rlentime
;
493 controller
->is_crtime
+= disk
->is_crtime
;
494 controller
->is_snaptime
+= disk
->is_snaptime
;
495 if (kstat_add(&disk
->is_errors
, &controller
->is_errors
))
501 acquire_iodev_stats(struct iodev_snapshot
*list
, kstat_ctl_t
*kc
)
503 struct iodev_snapshot
*pos
;
506 for (pos
= list
; pos
; pos
= pos
->is_next
) {
507 /* controllers don't have stats (yet) */
508 if (pos
->is_ksp
!= NULL
) {
509 if (kstat_read(kc
, pos
->is_ksp
, &pos
->is_stats
) == -1)
511 /* make sure crtime/snaptime is updated */
512 pos
->is_crtime
= pos
->is_ksp
->ks_crtime
;
513 pos
->is_snaptime
= pos
->is_ksp
->ks_snaptime
;
516 if ((err
= acquire_iodev_stats(pos
->is_children
, kc
)))
519 if (pos
->is_type
== IODEV_CONTROLLER
) {
520 struct iodev_snapshot
*pos2
= pos
->is_children
;
522 for (; pos2
; pos2
= pos2
->is_next
) {
523 if ((err
= collate_controller(pos
, pos2
)))
533 acquire_iodev_errors(struct snapshot
*ss
, kstat_ctl_t
*kc
)
537 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
538 char kstat_name
[KSTAT_STRLEN
];
539 char *dname
= kstat_name
;
540 char *ename
= ksp
->ks_name
;
541 struct iodev_snapshot
*iodev
;
543 if (ksp
->ks_type
!= KSTAT_TYPE_NAMED
)
545 if (strncmp(ksp
->ks_class
, "device_error", 12) != 0 &&
546 strncmp(ksp
->ks_class
, "iopath_error", 12) != 0)
550 * Some drivers may not follow the naming convention
551 * for error kstats (i.e., drivername,err) so
552 * be sure we don't walk off the end.
554 while (*ename
&& *ename
!= ',') {
561 iodev
= find_iodev_by_name(ss
->s_iodevs
, kstat_name
);
566 if (kstat_read(kc
, ksp
, NULL
) == -1)
568 if (kstat_copy(ksp
, &iodev
->is_errors
) == -1)
576 get_ids(struct iodev_snapshot
*iodev
, const char *pretty
)
578 int ctr
, disk
, slice
, ret
;
586 if (sscanf(pretty
, "c%d", &ctr
) != 1)
590 while (*p1
&& *p1
!= 't')
598 while (*p2
&& *p2
!= 'd')
601 if (!*p2
|| p2
== p1
)
604 target
= safe_alloc(1 + p2
- p1
);
605 (void) strlcpy(target
, p1
, 1 + p2
- p1
);
607 ret
= sscanf(p2
, "d%d%*[sp]%d", &disk
, &slice
);
609 if (ret
== 2 && iodev
->is_type
== IODEV_PARTITION
) {
610 iodev
->is_id
.id
= slice
;
611 iodev
->is_parent_id
.id
= disk
;
612 (void) strlcpy(iodev
->is_parent_id
.tid
, target
, KSTAT_STRLEN
);
613 } else if (ret
== 1) {
614 if (iodev
->is_type
== IODEV_DISK
) {
615 iodev
->is_id
.id
= disk
;
616 (void) strlcpy(iodev
->is_id
.tid
, target
, KSTAT_STRLEN
);
617 iodev
->is_parent_id
.id
= ctr
;
618 } else if (iodev
->is_type
== IODEV_IOPATH_LTI
) {
619 iodev
->is_parent_id
.id
= disk
;
620 (void) strlcpy(iodev
->is_parent_id
.tid
,
621 target
, KSTAT_STRLEN
);
629 get_pretty_name(enum snapshot_types types
, struct iodev_snapshot
*iodev
,
635 if (iodev
->is_type
== IODEV_NFS
) {
636 if (!(types
& SNAP_IODEV_PRETTY
))
639 iodev
->is_pretty
= lookup_nfs_name(iodev
->is_name
, kc
);
643 /* lookup/translate the kstat name */
644 dl
= lookup_ks_name(iodev
->is_name
, (types
& SNAP_IODEV_DEVID
) ? 1 : 0);
649 pretty
= safe_strdup(dl
->dsk
);
651 if (types
& SNAP_IODEV_PRETTY
) {
653 iodev
->is_dname
= safe_strdup(dl
->dname
);
657 iodev
->is_devid
= safe_strdup(dl
->devidstr
);
659 get_ids(iodev
, pretty
);
662 * we fill in pretty name wether it is asked for or not because
663 * it could be used in a filter by match_iodevs.
665 iodev
->is_pretty
= pretty
;
668 static enum iodev_type
669 get_iodev_type(kstat_t
*ksp
)
671 if (strcmp(ksp
->ks_class
, "disk") == 0)
673 if (strcmp(ksp
->ks_class
, "partition") == 0)
674 return (IODEV_PARTITION
);
675 if (strcmp(ksp
->ks_class
, "nfs") == 0)
677 if (strcmp(ksp
->ks_class
, "iopath") == 0)
678 return (IODEV_IOPATH_LTI
);
679 if (strcmp(ksp
->ks_class
, "tape") == 0)
681 return (IODEV_UNKNOWN
);
684 /* get the lun/target/initiator from the name, return 1 on success */
687 char *lname
, int *l
, char *tname
, int *t
, char *iname
, int *i
)
691 num
= sscanf(s
, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z_]%d", lname
, l
,
693 return ((num
== 6) ? 1 : 0);
697 /* get the lun, target, and initiator name and instance */
699 get_path_info(struct iodev_snapshot
*io
, char *mod
, size_t modlen
, int *type
,
700 int *inst
, char *name
, size_t size
)
704 * If it is iopath or ssd then pad the name with i/t/l so we can sort
705 * by alpha order and set type for IOPATH to DISK since we want to
706 * have it grouped with its ssd parent. The lun can be 5 digits,
707 * the target can be 4 digits, and the initiator can be 3 digits and
708 * the padding is done appropriately for string comparisons.
710 if (disk_or_partition_or_iopath(io
->is_type
)) {
712 char tname
[KSTAT_STRLEN
], iname
[KSTAT_STRLEN
];
713 char *ptr
, lname
[KSTAT_STRLEN
];
716 (void) get_lti(io
->is_name
, lname
, &l1
, tname
, &t1
, iname
, &i1
);
718 if (io
->is_type
== IODEV_DISK
) {
719 (void) snprintf(name
, size
, "%s%05d", lname
, l1
);
720 } else if (io
->is_type
== IODEV_PARTITION
) {
721 ptr
= strchr(io
->is_name
, ',');
722 (void) snprintf(name
, size
, "%s%05d%s", lname
, l1
, ptr
);
724 (void) snprintf(name
, size
, "%s%05d.%s%04d.%s%03d",
725 lname
, l1
, tname
, t1
, iname
, i1
);
726 /* set to disk so we sort with disks */
729 (void) strlcpy(mod
, lname
, modlen
);
732 (void) strlcpy(mod
, io
->is_module
, modlen
);
733 (void) strlcpy(name
, io
->is_name
, size
);
735 *inst
= io
->is_instance
;
740 iodev_cmp(struct iodev_snapshot
*io1
, struct iodev_snapshot
*io2
)
744 char name1
[KSTAT_STRLEN
], name2
[KSTAT_STRLEN
];
745 char mod1
[KSTAT_STRLEN
], mod2
[KSTAT_STRLEN
];
747 get_path_info(io1
, mod1
, sizeof (mod1
), &type1
, &inst1
, name1
,
749 get_path_info(io2
, mod2
, sizeof (mod2
), &type2
, &inst2
, name2
,
751 if ((!disk_or_partition(type1
)) ||
752 (!disk_or_partition(type2
))) {
753 /* neutral sort order between disk and part */
762 /* controller doesn't have ksp */
763 if (io1
->is_ksp
&& io2
->is_ksp
) {
764 if (strcmp(mod1
, mod2
) != 0) {
765 return (strcmp(mod1
, mod2
));
774 if (io1
->is_id
.id
< io2
->is_id
.id
) {
777 if (io1
->is_id
.id
> io2
->is_id
.id
) {
782 return (strcmp(name1
, name2
));
785 /* update the target reads and writes */
787 update_target(struct iodev_snapshot
*tgt
, struct iodev_snapshot
*path
)
789 tgt
->is_stats
.reads
+= path
->is_stats
.reads
;
790 tgt
->is_stats
.writes
+= path
->is_stats
.writes
;
791 tgt
->is_stats
.nread
+= path
->is_stats
.nread
;
792 tgt
->is_stats
.nwritten
+= path
->is_stats
.nwritten
;
793 tgt
->is_stats
.wcnt
+= path
->is_stats
.wcnt
;
794 tgt
->is_stats
.rcnt
+= path
->is_stats
.rcnt
;
797 * Stash the t_delta in the crtime for use in show_disk
798 * NOTE: this can't be done in show_disk because the
799 * itl entry is removed for the old format
801 tgt
->is_crtime
+= hrtime_delta(path
->is_crtime
, path
->is_snaptime
);
802 tgt
->is_snaptime
+= path
->is_snaptime
;
803 tgt
->is_nr_children
+= 1;
807 * Create a new synthetic device entry of the specified type. The supported
808 * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI.
810 static struct iodev_snapshot
*
811 make_extended_device(int type
, struct iodev_snapshot
*old
)
813 struct iodev_snapshot
*tptr
= NULL
;
815 int lun
, tgt
, initiator
;
816 char lun_name
[KSTAT_STRLEN
];
817 char tgt_name
[KSTAT_STRLEN
];
818 char initiator_name
[KSTAT_STRLEN
];
822 if (get_lti(old
->is_name
,
823 lun_name
, &lun
, tgt_name
, &tgt
, initiator_name
, &initiator
) != 1) {
826 tptr
= safe_alloc(sizeof (*old
));
827 bzero(tptr
, sizeof (*old
));
828 if (old
->is_pretty
!= NULL
) {
829 tptr
->is_pretty
= safe_alloc(strlen(old
->is_pretty
) + 1);
830 (void) strcpy(tptr
->is_pretty
, old
->is_pretty
);
832 bcopy(&old
->is_parent_id
, &tptr
->is_parent_id
,
833 sizeof (old
->is_parent_id
));
835 tptr
->is_type
= type
;
837 if (type
== IODEV_IOPATH_LT
) {
838 /* make new synthetic entry that is the LT */
839 /* set the id to the target id */
840 tptr
->is_id
.id
= tgt
;
841 (void) snprintf(tptr
->is_id
.tid
, sizeof (tptr
->is_id
.tid
),
842 "%s%d", tgt_name
, tgt
);
843 (void) snprintf(tptr
->is_name
, sizeof (tptr
->is_name
),
844 "%s%d.%s%d", lun_name
, lun
, tgt_name
, tgt
);
846 if (old
->is_pretty
) {
847 ptr
= strrchr(tptr
->is_pretty
, '.');
851 } else if (type
== IODEV_IOPATH_LI
) {
852 /* make new synthetic entry that is the LI */
853 /* set the id to the initiator number */
854 tptr
->is_id
.id
= initiator
;
855 (void) snprintf(tptr
->is_id
.tid
, sizeof (tptr
->is_id
.tid
),
856 "%s%d", initiator_name
, initiator
);
857 (void) snprintf(tptr
->is_name
, sizeof (tptr
->is_name
),
858 "%s%d.%s%d", lun_name
, lun
, initiator_name
, initiator
);
860 if (old
->is_pretty
) {
861 ptr
= strchr(tptr
->is_pretty
, '.');
863 (void) snprintf(ptr
+ 1,
864 strlen(tptr
->is_pretty
) + 1,
865 "%s%d", initiator_name
, initiator
);
872 * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat
873 * is found - traverse the children looking for the same initiator and sum
874 * them up. Add an LI entry and delete all of the LTI entries with the same
878 create_li_delete_lti(struct snapshot
*ss
, struct iodev_snapshot
*list
)
880 struct iodev_snapshot
*pos
, *entry
, *parent
;
881 int lun
, tgt
, initiator
;
882 char lun_name
[KSTAT_STRLEN
];
883 char tgt_name
[KSTAT_STRLEN
];
884 char initiator_name
[KSTAT_STRLEN
];
887 for (entry
= list
; entry
; entry
= entry
->is_next
) {
888 if ((err
= create_li_delete_lti(ss
, entry
->is_children
)) != 0)
891 if (entry
->is_type
== IODEV_IOPATH_LTI
) {
892 parent
= find_parent(ss
, entry
);
893 if (get_lti(entry
->is_name
, lun_name
, &lun
,
894 tgt_name
, &tgt
, initiator_name
, &initiator
) != 1) {
898 pos
= (parent
== NULL
) ? NULL
: parent
->is_children
;
899 for (; pos
; pos
= pos
->is_next
) {
900 if (pos
->is_id
.id
!= -1 &&
901 pos
->is_id
.id
== initiator
&&
902 pos
->is_type
== IODEV_IOPATH_LI
) {
903 /* found the same initiator */
904 update_target(pos
, entry
);
905 list_del(&parent
->is_children
, entry
);
907 parent
->is_nr_children
--;
914 /* make the first LI entry */
915 pos
= make_extended_device(
916 IODEV_IOPATH_LI
, entry
);
917 update_target(pos
, entry
);
920 insert_before(&parent
->is_children
,
922 list_del(&parent
->is_children
, entry
);
925 insert_before(&ss
->s_iodevs
, entry
,
927 list_del(&ss
->s_iodevs
, entry
);
938 * We have the LTI kstat, now add an entry for the LT that sums up all of
939 * the LTI's with the same target(t).
942 create_lt(struct snapshot
*ss
, struct iodev_snapshot
*list
)
944 struct iodev_snapshot
*entry
, *parent
, *pos
;
945 int lun
, tgt
, initiator
;
946 char lun_name
[KSTAT_STRLEN
];
947 char tgt_name
[KSTAT_STRLEN
];
948 char initiator_name
[KSTAT_STRLEN
];
951 for (entry
= list
; entry
; entry
= entry
->is_next
) {
952 if ((err
= create_lt(ss
, entry
->is_children
)) != 0)
955 if (entry
->is_type
== IODEV_IOPATH_LTI
) {
956 parent
= find_parent(ss
, entry
);
957 if (get_lti(entry
->is_name
, lun_name
, &lun
,
958 tgt_name
, &tgt
, initiator_name
, &initiator
) != 1) {
962 pos
= (parent
== NULL
) ? NULL
: parent
->is_children
;
963 for (; pos
; pos
= pos
->is_next
) {
964 if (pos
->is_id
.id
!= -1 &&
965 pos
->is_id
.id
== tgt
&&
966 pos
->is_type
== IODEV_IOPATH_LT
) {
967 /* found the same target */
968 update_target(pos
, entry
);
974 pos
= make_extended_device(
975 IODEV_IOPATH_LT
, entry
);
976 update_target(pos
, entry
);
979 insert_before(&parent
->is_children
,
981 parent
->is_nr_children
++;
983 insert_before(&ss
->s_iodevs
,
992 /* Find the longest is_name field to aid formatting of output */
994 iodevs_is_name_maxlen(struct iodev_snapshot
*list
)
996 struct iodev_snapshot
*entry
;
997 int max
= 0, cmax
, len
;
999 for (entry
= list
; entry
; entry
= entry
->is_next
) {
1000 cmax
= iodevs_is_name_maxlen(entry
->is_children
);
1001 max
= (cmax
> max
) ? cmax
: max
;
1002 len
= strlen(entry
->is_name
);
1003 max
= (len
> max
) ? len
: max
;
1009 acquire_iodevs(struct snapshot
*ss
, kstat_ctl_t
*kc
, struct iodev_filter
*df
)
1012 struct iodev_snapshot
*pos
;
1013 struct iodev_snapshot
*list
= NULL
;
1016 ss
->s_nr_iodevs
= 0;
1017 ss
->s_iodevs_is_name_maxlen
= 0;
1020 * Call cleanup_iodevs_snapshot() so that a cache miss in
1021 * lookup_ks_name() will result in a fresh snapshot.
1023 cleanup_iodevs_snapshot();
1025 for (ksp
= kc
->kc_chain
; ksp
; ksp
= ksp
->ks_next
) {
1026 enum iodev_type type
;
1028 if (ksp
->ks_type
!= KSTAT_TYPE_IO
)
1031 /* e.g. "usb_byte_count" is not handled */
1032 if ((type
= get_iodev_type(ksp
)) == IODEV_UNKNOWN
)
1035 if (df
&& !(type
& df
->if_allowed_types
))
1038 if ((pos
= malloc(sizeof (struct iodev_snapshot
))) == NULL
) {
1043 (void) memset(pos
, 0, sizeof (struct iodev_snapshot
));
1045 pos
->is_type
= type
;
1046 pos
->is_crtime
= ksp
->ks_crtime
;
1047 pos
->is_snaptime
= ksp
->ks_snaptime
;
1048 pos
->is_id
.id
= IODEV_NO_ID
;
1049 pos
->is_parent_id
.id
= IODEV_NO_ID
;
1051 pos
->is_instance
= ksp
->ks_instance
;
1053 (void) strlcpy(pos
->is_module
, ksp
->ks_module
, KSTAT_STRLEN
);
1054 (void) strlcpy(pos
->is_name
, ksp
->ks_name
, KSTAT_STRLEN
);
1055 get_pretty_name(ss
->s_types
, pos
, kc
);
1058 * We must insert in sort order so e.g. vmstat -l
1061 insert_into(&list
, pos
);
1064 choose_iodevs(ss
, list
, df
);
1066 /* before acquire_stats for collate_controller()'s benefit */
1067 if (ss
->s_types
& SNAP_IODEV_ERRORS
) {
1068 if ((err
= acquire_iodev_errors(ss
, kc
)) != 0)
1072 if ((err
= acquire_iodev_stats(ss
->s_iodevs
, kc
)) != 0)
1075 if (ss
->s_types
& SNAP_IOPATHS_LTI
) {
1077 * -Y: kstats are LTI, need to create a synthetic LT
1080 if ((err
= create_lt(ss
, ss
->s_iodevs
)) != 0) {
1084 if (ss
->s_types
& SNAP_IOPATHS_LI
) {
1086 * -X: kstats are LTI, need to create a synthetic LI and
1087 * delete the LTI for -X output
1089 if ((err
= create_li_delete_lti(ss
, ss
->s_iodevs
)) != 0) {
1094 /* determine width of longest is_name */
1095 ss
->s_iodevs_is_name_maxlen
= iodevs_is_name_maxlen(ss
->s_iodevs
);
1103 free_iodev(struct iodev_snapshot
*iodev
)
1105 while (iodev
->is_children
) {
1106 struct iodev_snapshot
*tmp
= iodev
->is_children
;
1107 iodev
->is_children
= iodev
->is_children
->is_next
;
1111 if (iodev
->avl_list
) {
1112 avl_remove(iodev
->avl_list
, iodev
);
1113 if (avl_numnodes(iodev
->avl_list
) == 0) {
1114 avl_destroy(iodev
->avl_list
);
1115 free(iodev
->avl_list
);
1119 free(iodev
->is_errors
.ks_data
);
1120 free(iodev
->is_pretty
);
1121 free(iodev
->is_dname
);
1122 free(iodev
->is_devid
);