17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5a08731ecScth * Common Development and Distribution License (the "License").
6a08731ecScth * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22fff11ba8SJonathan Cowper-Andrewes * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
237c478bd9Sstevel@tonic-gate */
247c478bd9Sstevel@tonic-gate
257c478bd9Sstevel@tonic-gate #include "statcommon.h"
267c478bd9Sstevel@tonic-gate #include "dsr.h"
277c478bd9Sstevel@tonic-gate
287c478bd9Sstevel@tonic-gate #include <sys/dklabel.h>
297c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
307c478bd9Sstevel@tonic-gate #include <stdlib.h>
317c478bd9Sstevel@tonic-gate #include <stdarg.h>
32*c5d54b67SRichard Lowe #include <stddef.h>
337c478bd9Sstevel@tonic-gate #include <unistd.h>
347c478bd9Sstevel@tonic-gate #include <strings.h>
357c478bd9Sstevel@tonic-gate #include <errno.h>
367c478bd9Sstevel@tonic-gate #include <limits.h>
377c478bd9Sstevel@tonic-gate
387c478bd9Sstevel@tonic-gate static void insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev);
397c478bd9Sstevel@tonic-gate
407c478bd9Sstevel@tonic-gate static struct iodev_snapshot *
make_controller(int cid)417c478bd9Sstevel@tonic-gate make_controller(int cid)
427c478bd9Sstevel@tonic-gate {
437c478bd9Sstevel@tonic-gate struct iodev_snapshot *new;
447c478bd9Sstevel@tonic-gate
457c478bd9Sstevel@tonic-gate new = safe_alloc(sizeof (struct iodev_snapshot));
467c478bd9Sstevel@tonic-gate (void) memset(new, 0, sizeof (struct iodev_snapshot));
477c478bd9Sstevel@tonic-gate new->is_type = IODEV_CONTROLLER;
487c478bd9Sstevel@tonic-gate new->is_id.id = cid;
497c478bd9Sstevel@tonic-gate new->is_parent_id.id = IODEV_NO_ID;
507c478bd9Sstevel@tonic-gate
517c478bd9Sstevel@tonic-gate (void) snprintf(new->is_name, sizeof (new->is_name), "c%d", cid);
527c478bd9Sstevel@tonic-gate
537c478bd9Sstevel@tonic-gate return (new);
547c478bd9Sstevel@tonic-gate }
557c478bd9Sstevel@tonic-gate
567c478bd9Sstevel@tonic-gate static struct iodev_snapshot *
find_iodev_by_name(struct iodev_snapshot * list,const char * name)577c478bd9Sstevel@tonic-gate find_iodev_by_name(struct iodev_snapshot *list, const char *name)
587c478bd9Sstevel@tonic-gate {
597c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos;
607c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos2;
617c478bd9Sstevel@tonic-gate
627c478bd9Sstevel@tonic-gate for (pos = list; pos; pos = pos->is_next) {
637c478bd9Sstevel@tonic-gate if (strcmp(pos->is_name, name) == 0)
647c478bd9Sstevel@tonic-gate return (pos);
657c478bd9Sstevel@tonic-gate
667c478bd9Sstevel@tonic-gate pos2 = find_iodev_by_name(pos->is_children, name);
677c478bd9Sstevel@tonic-gate if (pos2 != NULL)
687c478bd9Sstevel@tonic-gate return (pos2);
697c478bd9Sstevel@tonic-gate }
707c478bd9Sstevel@tonic-gate
717c478bd9Sstevel@tonic-gate return (NULL);
727c478bd9Sstevel@tonic-gate }
737c478bd9Sstevel@tonic-gate
747c478bd9Sstevel@tonic-gate static enum iodev_type
parent_iodev_type(enum iodev_type type)757c478bd9Sstevel@tonic-gate parent_iodev_type(enum iodev_type type)
767c478bd9Sstevel@tonic-gate {
777c478bd9Sstevel@tonic-gate switch (type) {
787c478bd9Sstevel@tonic-gate case IODEV_CONTROLLER: return (0);
7937fbbce5Scth case IODEV_IOPATH_LT: return (0);
8037fbbce5Scth case IODEV_IOPATH_LI: return (0);
817c478bd9Sstevel@tonic-gate case IODEV_NFS: return (0);
827c478bd9Sstevel@tonic-gate case IODEV_TAPE: return (0);
8337fbbce5Scth case IODEV_IOPATH_LTI: return (IODEV_DISK);
847c478bd9Sstevel@tonic-gate case IODEV_DISK: return (IODEV_CONTROLLER);
857c478bd9Sstevel@tonic-gate case IODEV_PARTITION: return (IODEV_DISK);
867c478bd9Sstevel@tonic-gate }
877c478bd9Sstevel@tonic-gate return (IODEV_UNKNOWN);
887c478bd9Sstevel@tonic-gate }
897c478bd9Sstevel@tonic-gate
907c478bd9Sstevel@tonic-gate static int
id_match(struct iodev_id * id1,struct iodev_id * id2)917c478bd9Sstevel@tonic-gate id_match(struct iodev_id *id1, struct iodev_id *id2)
927c478bd9Sstevel@tonic-gate {
937c478bd9Sstevel@tonic-gate return (id1->id == id2->id &&
947c478bd9Sstevel@tonic-gate strcmp(id1->tid, id2->tid) == 0);
957c478bd9Sstevel@tonic-gate }
967c478bd9Sstevel@tonic-gate
977c478bd9Sstevel@tonic-gate static struct iodev_snapshot *
find_parent(struct snapshot * ss,struct iodev_snapshot * iodev)987c478bd9Sstevel@tonic-gate find_parent(struct snapshot *ss, struct iodev_snapshot *iodev)
997c478bd9Sstevel@tonic-gate {
1007c478bd9Sstevel@tonic-gate enum iodev_type parent_type = parent_iodev_type(iodev->is_type);
1017c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos;
1027c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos2;
1037c478bd9Sstevel@tonic-gate
1047c478bd9Sstevel@tonic-gate if (parent_type == 0 || parent_type == IODEV_UNKNOWN)
1057c478bd9Sstevel@tonic-gate return (NULL);
1067c478bd9Sstevel@tonic-gate
1077c478bd9Sstevel@tonic-gate if (iodev->is_parent_id.id == IODEV_NO_ID &&
1087c478bd9Sstevel@tonic-gate iodev->is_parent_id.tid[0] == '\0')
1097c478bd9Sstevel@tonic-gate return (NULL);
1107c478bd9Sstevel@tonic-gate
1117c478bd9Sstevel@tonic-gate if (parent_type == IODEV_CONTROLLER) {
1127c478bd9Sstevel@tonic-gate for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
1137c478bd9Sstevel@tonic-gate if (pos->is_type != IODEV_CONTROLLER)
1147c478bd9Sstevel@tonic-gate continue;
1157c478bd9Sstevel@tonic-gate if (pos->is_id.id != iodev->is_parent_id.id)
1167c478bd9Sstevel@tonic-gate continue;
1177c478bd9Sstevel@tonic-gate return (pos);
1187c478bd9Sstevel@tonic-gate }
1197c478bd9Sstevel@tonic-gate
1207c478bd9Sstevel@tonic-gate if (!(ss->s_types & SNAP_CONTROLLERS))
1217c478bd9Sstevel@tonic-gate return (NULL);
1227c478bd9Sstevel@tonic-gate
1237c478bd9Sstevel@tonic-gate pos = make_controller(iodev->is_parent_id.id);
1247c478bd9Sstevel@tonic-gate insert_iodev(ss, pos);
1257c478bd9Sstevel@tonic-gate return (pos);
1267c478bd9Sstevel@tonic-gate }
1277c478bd9Sstevel@tonic-gate
1287c478bd9Sstevel@tonic-gate /* IODEV_DISK parent */
1297c478bd9Sstevel@tonic-gate for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
1307c478bd9Sstevel@tonic-gate if (id_match(&iodev->is_parent_id, &pos->is_id) &&
1317c478bd9Sstevel@tonic-gate pos->is_type == IODEV_DISK)
1327c478bd9Sstevel@tonic-gate return (pos);
1337c478bd9Sstevel@tonic-gate if (pos->is_type != IODEV_CONTROLLER)
1347c478bd9Sstevel@tonic-gate continue;
1357c478bd9Sstevel@tonic-gate for (pos2 = pos->is_children; pos2; pos2 = pos2->is_next) {
1367c478bd9Sstevel@tonic-gate if (pos2->is_type != IODEV_DISK)
1377c478bd9Sstevel@tonic-gate continue;
1387c478bd9Sstevel@tonic-gate if (id_match(&iodev->is_parent_id, &pos2->is_id))
1397c478bd9Sstevel@tonic-gate return (pos2);
1407c478bd9Sstevel@tonic-gate }
1417c478bd9Sstevel@tonic-gate }
1427c478bd9Sstevel@tonic-gate
1437c478bd9Sstevel@tonic-gate return (NULL);
1447c478bd9Sstevel@tonic-gate }
1457c478bd9Sstevel@tonic-gate
1464be70790Swroche /*
1474be70790Swroche * Introduce an index into the list to speed up insert_into looking for the
1484be70790Swroche * right position in the list. This index is an AVL tree of all the
1494be70790Swroche * iodev_snapshot in the list.
1504be70790Swroche */
1514be70790Swroche static int
avl_iodev_cmp(const void * is1,const void * is2)1524be70790Swroche avl_iodev_cmp(const void* is1, const void* is2)
1534be70790Swroche {
1544be70790Swroche int c = iodev_cmp((struct iodev_snapshot *)is1,
1554be70790Swroche (struct iodev_snapshot *)is2);
1564be70790Swroche
1574be70790Swroche if (c > 0)
1584be70790Swroche return (1);
1594be70790Swroche
1604be70790Swroche if (c < 0)
1614be70790Swroche return (-1);
1624be70790Swroche
1634be70790Swroche return (0);
1644be70790Swroche }
1654be70790Swroche
1664be70790Swroche static void
ix_new_list(struct iodev_snapshot * elem)1674be70790Swroche ix_new_list(struct iodev_snapshot *elem)
1684be70790Swroche {
1694be70790Swroche avl_tree_t *l = malloc(sizeof (avl_tree_t));
1704be70790Swroche
1714be70790Swroche elem->avl_list = l;
1724be70790Swroche if (l == NULL)
1734be70790Swroche return;
1744be70790Swroche
1754be70790Swroche avl_create(l, avl_iodev_cmp, sizeof (struct iodev_snapshot),
1764be70790Swroche offsetof(struct iodev_snapshot, avl_link));
1774be70790Swroche
1784be70790Swroche avl_add(l, elem);
1794be70790Swroche }
1804be70790Swroche
1814be70790Swroche static void
ix_list_del(struct iodev_snapshot * elem)1824be70790Swroche ix_list_del(struct iodev_snapshot *elem)
1834be70790Swroche {
1844be70790Swroche avl_tree_t *l = elem->avl_list;
1854be70790Swroche
1864be70790Swroche if (l == NULL)
1874be70790Swroche return;
1884be70790Swroche
1894be70790Swroche elem->avl_list = NULL;
1904be70790Swroche
1914be70790Swroche avl_remove(l, elem);
1924be70790Swroche if (avl_numnodes(l) == 0) {
1934be70790Swroche avl_destroy(l);
1944be70790Swroche free(l);
1954be70790Swroche }
1964be70790Swroche }
1974be70790Swroche
1984be70790Swroche static void
ix_insert_here(struct iodev_snapshot * pos,struct iodev_snapshot * elem,int ba)1994be70790Swroche ix_insert_here(struct iodev_snapshot *pos, struct iodev_snapshot *elem, int ba)
2004be70790Swroche {
2014be70790Swroche avl_tree_t *l = pos->avl_list;
2024be70790Swroche elem->avl_list = l;
2034be70790Swroche
2044be70790Swroche if (l == NULL)
2054be70790Swroche return;
2064be70790Swroche
2074be70790Swroche avl_insert_here(l, elem, pos, ba);
2084be70790Swroche }
2094be70790Swroche
2107c478bd9Sstevel@tonic-gate static void
list_del(struct iodev_snapshot ** list,struct iodev_snapshot * pos)2117c478bd9Sstevel@tonic-gate list_del(struct iodev_snapshot **list, struct iodev_snapshot *pos)
2127c478bd9Sstevel@tonic-gate {
2134be70790Swroche ix_list_del(pos);
2144be70790Swroche
2157c478bd9Sstevel@tonic-gate if (*list == pos)
2167c478bd9Sstevel@tonic-gate *list = pos->is_next;
2177c478bd9Sstevel@tonic-gate if (pos->is_next)
2187c478bd9Sstevel@tonic-gate pos->is_next->is_prev = pos->is_prev;
2197c478bd9Sstevel@tonic-gate if (pos->is_prev)
2207c478bd9Sstevel@tonic-gate pos->is_prev->is_next = pos->is_next;
2217c478bd9Sstevel@tonic-gate pos->is_prev = pos->is_next = NULL;
2227c478bd9Sstevel@tonic-gate }
2237c478bd9Sstevel@tonic-gate
2247c478bd9Sstevel@tonic-gate static void
insert_before(struct iodev_snapshot ** list,struct iodev_snapshot * pos,struct iodev_snapshot * new)2257c478bd9Sstevel@tonic-gate insert_before(struct iodev_snapshot **list, struct iodev_snapshot *pos,
2267c478bd9Sstevel@tonic-gate struct iodev_snapshot *new)
2277c478bd9Sstevel@tonic-gate {
2287c478bd9Sstevel@tonic-gate if (pos == NULL) {
2297c478bd9Sstevel@tonic-gate new->is_prev = new->is_next = NULL;
2307c478bd9Sstevel@tonic-gate *list = new;
2314be70790Swroche ix_new_list(new);
2327c478bd9Sstevel@tonic-gate return;
2337c478bd9Sstevel@tonic-gate }
2347c478bd9Sstevel@tonic-gate
2357c478bd9Sstevel@tonic-gate new->is_next = pos;
2367c478bd9Sstevel@tonic-gate new->is_prev = pos->is_prev;
2377c478bd9Sstevel@tonic-gate if (pos->is_prev)
2387c478bd9Sstevel@tonic-gate pos->is_prev->is_next = new;
2397c478bd9Sstevel@tonic-gate else
2407c478bd9Sstevel@tonic-gate *list = new;
2417c478bd9Sstevel@tonic-gate pos->is_prev = new;
2424be70790Swroche
2434be70790Swroche ix_insert_here(pos, new, AVL_BEFORE);
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate
2467c478bd9Sstevel@tonic-gate static void
insert_after(struct iodev_snapshot ** list,struct iodev_snapshot * pos,struct iodev_snapshot * new)2477c478bd9Sstevel@tonic-gate insert_after(struct iodev_snapshot **list, struct iodev_snapshot *pos,
2487c478bd9Sstevel@tonic-gate struct iodev_snapshot *new)
2497c478bd9Sstevel@tonic-gate {
2507c478bd9Sstevel@tonic-gate if (pos == NULL) {
2517c478bd9Sstevel@tonic-gate new->is_prev = new->is_next = NULL;
2527c478bd9Sstevel@tonic-gate *list = new;
2534be70790Swroche ix_new_list(new);
2547c478bd9Sstevel@tonic-gate return;
2557c478bd9Sstevel@tonic-gate }
2567c478bd9Sstevel@tonic-gate
2577c478bd9Sstevel@tonic-gate new->is_next = pos->is_next;
2587c478bd9Sstevel@tonic-gate new->is_prev = pos;
2597c478bd9Sstevel@tonic-gate if (pos->is_next)
2607c478bd9Sstevel@tonic-gate pos->is_next->is_prev = new;
2617c478bd9Sstevel@tonic-gate pos->is_next = new;
2624be70790Swroche
2634be70790Swroche ix_insert_here(pos, new, AVL_AFTER);
2647c478bd9Sstevel@tonic-gate }
2657c478bd9Sstevel@tonic-gate
2667c478bd9Sstevel@tonic-gate static void
insert_into(struct iodev_snapshot ** list,struct iodev_snapshot * iodev)2677c478bd9Sstevel@tonic-gate insert_into(struct iodev_snapshot **list, struct iodev_snapshot *iodev)
2687c478bd9Sstevel@tonic-gate {
2697c478bd9Sstevel@tonic-gate struct iodev_snapshot *tmp = *list;
2704be70790Swroche avl_tree_t *l;
2714be70790Swroche void *p;
2724be70790Swroche avl_index_t where;
2734be70790Swroche
2747c478bd9Sstevel@tonic-gate if (*list == NULL) {
2757c478bd9Sstevel@tonic-gate *list = iodev;
2764be70790Swroche ix_new_list(iodev);
2777c478bd9Sstevel@tonic-gate return;
2787c478bd9Sstevel@tonic-gate }
2797c478bd9Sstevel@tonic-gate
2804be70790Swroche /*
2814be70790Swroche * Optimize the search: instead of walking the entire list
2824be70790Swroche * (which can contain thousands of nodes), search in the AVL
2834be70790Swroche * tree the nearest node and reposition the startup point to
2844be70790Swroche * this node rather than always starting from the beginning
2854be70790Swroche * of the list.
2864be70790Swroche */
2874be70790Swroche l = tmp->avl_list;
2884be70790Swroche if (l != NULL) {
2894be70790Swroche p = avl_find(l, iodev, &where);
2904be70790Swroche if (p == NULL) {
2914be70790Swroche p = avl_nearest(l, where, AVL_BEFORE);
2924be70790Swroche }
2934be70790Swroche if (p != NULL) {
2944be70790Swroche tmp = (struct iodev_snapshot *)p;
2954be70790Swroche }
2964be70790Swroche }
2974be70790Swroche
2987c478bd9Sstevel@tonic-gate for (;;) {
2997c478bd9Sstevel@tonic-gate if (iodev_cmp(tmp, iodev) > 0) {
3007c478bd9Sstevel@tonic-gate insert_before(list, tmp, iodev);
3017c478bd9Sstevel@tonic-gate return;
3027c478bd9Sstevel@tonic-gate }
3037c478bd9Sstevel@tonic-gate
3047c478bd9Sstevel@tonic-gate if (tmp->is_next == NULL)
3057c478bd9Sstevel@tonic-gate break;
3067c478bd9Sstevel@tonic-gate
3077c478bd9Sstevel@tonic-gate tmp = tmp->is_next;
3087c478bd9Sstevel@tonic-gate }
3097c478bd9Sstevel@tonic-gate
3107c478bd9Sstevel@tonic-gate insert_after(list, tmp, iodev);
3117c478bd9Sstevel@tonic-gate }
3127c478bd9Sstevel@tonic-gate
3137c478bd9Sstevel@tonic-gate static int
disk_or_partition(enum iodev_type type)3147c478bd9Sstevel@tonic-gate disk_or_partition(enum iodev_type type)
3157c478bd9Sstevel@tonic-gate {
3167c478bd9Sstevel@tonic-gate return (type == IODEV_DISK || type == IODEV_PARTITION);
3177c478bd9Sstevel@tonic-gate }
3187c478bd9Sstevel@tonic-gate
31937fbbce5Scth static int
disk_or_partition_or_iopath(enum iodev_type type)32037fbbce5Scth disk_or_partition_or_iopath(enum iodev_type type)
32137fbbce5Scth {
32237fbbce5Scth return (type == IODEV_DISK || type == IODEV_PARTITION ||
32337fbbce5Scth type == IODEV_IOPATH_LTI);
32437fbbce5Scth }
32537fbbce5Scth
3267c478bd9Sstevel@tonic-gate static void
insert_iodev(struct snapshot * ss,struct iodev_snapshot * iodev)3277c478bd9Sstevel@tonic-gate insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev)
3287c478bd9Sstevel@tonic-gate {
3297c478bd9Sstevel@tonic-gate struct iodev_snapshot *parent = find_parent(ss, iodev);
3307c478bd9Sstevel@tonic-gate struct iodev_snapshot **list;
3317c478bd9Sstevel@tonic-gate
3327c478bd9Sstevel@tonic-gate if (parent != NULL) {
3337c478bd9Sstevel@tonic-gate list = &parent->is_children;
3347c478bd9Sstevel@tonic-gate parent->is_nr_children++;
3357c478bd9Sstevel@tonic-gate } else {
3367c478bd9Sstevel@tonic-gate list = &ss->s_iodevs;
3377c478bd9Sstevel@tonic-gate ss->s_nr_iodevs++;
3387c478bd9Sstevel@tonic-gate }
3397c478bd9Sstevel@tonic-gate
3407c478bd9Sstevel@tonic-gate insert_into(list, iodev);
3417c478bd9Sstevel@tonic-gate }
3427c478bd9Sstevel@tonic-gate
34337fbbce5Scth /* return 1 if dev passes filter */
3447c478bd9Sstevel@tonic-gate static int
iodev_match(struct iodev_snapshot * dev,struct iodev_filter * df)3457c478bd9Sstevel@tonic-gate iodev_match(struct iodev_snapshot *dev, struct iodev_filter *df)
3467c478bd9Sstevel@tonic-gate {
3477c478bd9Sstevel@tonic-gate int is_floppy = (strncmp(dev->is_name, "fd", 2) == 0);
34837fbbce5Scth char *isn, *ispn, *ifn;
34937fbbce5Scth char *path;
35037fbbce5Scth int ifnl;
35137fbbce5Scth size_t i;
3527c478bd9Sstevel@tonic-gate
3537c478bd9Sstevel@tonic-gate /* no filter, pass */
3547c478bd9Sstevel@tonic-gate if (df == NULL)
35537fbbce5Scth return (1); /* pass */
3567c478bd9Sstevel@tonic-gate
3577c478bd9Sstevel@tonic-gate /* no filtered names, pass if not floppy and skipped */
3587c478bd9Sstevel@tonic-gate if (df->if_nr_names == NULL)
3597c478bd9Sstevel@tonic-gate return (!(df->if_skip_floppy && is_floppy));
3607c478bd9Sstevel@tonic-gate
36137fbbce5Scth isn = dev->is_name;
36237fbbce5Scth ispn = dev->is_pretty;
3637c478bd9Sstevel@tonic-gate for (i = 0; i < df->if_nr_names; i++) {
36437fbbce5Scth ifn = df->if_names[i];
36537fbbce5Scth ifnl = strlen(ifn);
36637fbbce5Scth path = strchr(ifn, '.');
36737fbbce5Scth
36837fbbce5Scth if ((strcmp(isn, ifn) == 0) ||
36937fbbce5Scth (ispn && (strcmp(ispn, ifn) == 0)))
37037fbbce5Scth return (1); /* pass */
37137fbbce5Scth
37237fbbce5Scth /* if filter is a path allow partial match */
37337fbbce5Scth if (path &&
37437fbbce5Scth ((strncmp(isn, ifn, ifnl) == 0) ||
37537fbbce5Scth (ispn && (strncmp(ispn, ifn, ifnl) == 0))))
37637fbbce5Scth return (1); /* pass */
3777c478bd9Sstevel@tonic-gate }
3787c478bd9Sstevel@tonic-gate
37937fbbce5Scth return (0); /* fail */
38037fbbce5Scth }
38137fbbce5Scth
38237fbbce5Scth /* return 1 if path is an mpxio path associated with dev */
38337fbbce5Scth static int
iodev_path_match(struct iodev_snapshot * dev,struct iodev_snapshot * path)38437fbbce5Scth iodev_path_match(struct iodev_snapshot *dev, struct iodev_snapshot *path)
38537fbbce5Scth {
38637fbbce5Scth char *dn, *pn;
38737fbbce5Scth int dnl;
38837fbbce5Scth
38937fbbce5Scth dn = dev->is_name;
39037fbbce5Scth pn = path->is_name;
39137fbbce5Scth dnl = strlen(dn);
39237fbbce5Scth
39337fbbce5Scth if ((strncmp(pn, dn, dnl) == 0) && (pn[dnl] == '.'))
39437fbbce5Scth return (1); /* yes */
39537fbbce5Scth
39637fbbce5Scth return (0); /* no */
3977c478bd9Sstevel@tonic-gate }
3987c478bd9Sstevel@tonic-gate
3997c478bd9Sstevel@tonic-gate /* select which I/O devices to collect stats for */
4007c478bd9Sstevel@tonic-gate static void
choose_iodevs(struct snapshot * ss,struct iodev_snapshot * iodevs,struct iodev_filter * df)4017c478bd9Sstevel@tonic-gate choose_iodevs(struct snapshot *ss, struct iodev_snapshot *iodevs,
4027c478bd9Sstevel@tonic-gate struct iodev_filter *df)
4037c478bd9Sstevel@tonic-gate {
40437fbbce5Scth struct iodev_snapshot *pos, *ppos, *tmp, *ptmp;
40537fbbce5Scth int nr_iodevs;
40637fbbce5Scth int nr_iodevs_orig;
40737fbbce5Scth
40837fbbce5Scth nr_iodevs = df ? df->if_max_iodevs : UNLIMITED_IODEVS;
40937fbbce5Scth nr_iodevs_orig = nr_iodevs;
4107c478bd9Sstevel@tonic-gate
4117c478bd9Sstevel@tonic-gate if (nr_iodevs == UNLIMITED_IODEVS)
4127c478bd9Sstevel@tonic-gate nr_iodevs = INT_MAX;
4137c478bd9Sstevel@tonic-gate
41437fbbce5Scth /* add the full matches */
41537fbbce5Scth pos = iodevs;
4167c478bd9Sstevel@tonic-gate while (pos && nr_iodevs) {
41737fbbce5Scth tmp = pos;
4187c478bd9Sstevel@tonic-gate pos = pos->is_next;
4197c478bd9Sstevel@tonic-gate
4207c478bd9Sstevel@tonic-gate if (!iodev_match(tmp, df))
42137fbbce5Scth continue; /* failed full match */
4227c478bd9Sstevel@tonic-gate
4237c478bd9Sstevel@tonic-gate list_del(&iodevs, tmp);
4247c478bd9Sstevel@tonic-gate insert_iodev(ss, tmp);
4257c478bd9Sstevel@tonic-gate
42637fbbce5Scth /*
42737fbbce5Scth * Add all mpxio paths associated with match above. Added
42837fbbce5Scth * paths don't count against nr_iodevs.
42937fbbce5Scth */
43037fbbce5Scth if (strchr(tmp->is_name, '.') == NULL) {
43137fbbce5Scth ppos = iodevs;
43237fbbce5Scth while (ppos) {
43337fbbce5Scth ptmp = ppos;
43437fbbce5Scth ppos = ppos->is_next;
43537fbbce5Scth
43637fbbce5Scth if (!iodev_path_match(tmp, ptmp))
43737fbbce5Scth continue; /* not an mpxio path */
43837fbbce5Scth
43937fbbce5Scth list_del(&iodevs, ptmp);
44037fbbce5Scth insert_iodev(ss, ptmp);
44137fbbce5Scth if (pos == ptmp)
44237fbbce5Scth pos = ppos;
44337fbbce5Scth }
4447c478bd9Sstevel@tonic-gate }
4457c478bd9Sstevel@tonic-gate
44637fbbce5Scth nr_iodevs--;
44737fbbce5Scth }
4487c478bd9Sstevel@tonic-gate
44937fbbce5Scth /*
45037fbbce5Scth * If we had a filter, and *nothing* passed the filter then we
45137fbbce5Scth * don't want to fill the remaining slots - it is just confusing
45237fbbce5Scth * if we don that, it makes it look like the filter code is broken.
45337fbbce5Scth */
45437fbbce5Scth if ((df->if_nr_names == NULL) || (nr_iodevs != nr_iodevs_orig)) {
4557c478bd9Sstevel@tonic-gate /* now insert any iodevs into the remaining slots */
45637fbbce5Scth pos = iodevs;
4577c478bd9Sstevel@tonic-gate while (pos && nr_iodevs) {
45837fbbce5Scth tmp = pos;
4597c478bd9Sstevel@tonic-gate pos = pos->is_next;
4607c478bd9Sstevel@tonic-gate
4617c478bd9Sstevel@tonic-gate if (df && df->if_skip_floppy &&
4627c478bd9Sstevel@tonic-gate strncmp(tmp->is_name, "fd", 2) == 0)
4637c478bd9Sstevel@tonic-gate continue;
4647c478bd9Sstevel@tonic-gate
4657c478bd9Sstevel@tonic-gate list_del(&iodevs, tmp);
4667c478bd9Sstevel@tonic-gate insert_iodev(ss, tmp);
4677c478bd9Sstevel@tonic-gate
4687c478bd9Sstevel@tonic-gate --nr_iodevs;
4697c478bd9Sstevel@tonic-gate }
47037fbbce5Scth }
4717c478bd9Sstevel@tonic-gate
4727c478bd9Sstevel@tonic-gate /* clear the unwanted ones */
4737c478bd9Sstevel@tonic-gate pos = iodevs;
4747c478bd9Sstevel@tonic-gate while (pos) {
4757c478bd9Sstevel@tonic-gate struct iodev_snapshot *tmp = pos;
4767c478bd9Sstevel@tonic-gate pos = pos->is_next;
4777c478bd9Sstevel@tonic-gate free_iodev(tmp);
4787c478bd9Sstevel@tonic-gate }
4797c478bd9Sstevel@tonic-gate }
4807c478bd9Sstevel@tonic-gate
4817c478bd9Sstevel@tonic-gate static int
collate_controller(struct iodev_snapshot * controller,struct iodev_snapshot * disk)4827c478bd9Sstevel@tonic-gate collate_controller(struct iodev_snapshot *controller,
4837c478bd9Sstevel@tonic-gate struct iodev_snapshot *disk)
4847c478bd9Sstevel@tonic-gate {
4857c478bd9Sstevel@tonic-gate controller->is_stats.nread += disk->is_stats.nread;
4867c478bd9Sstevel@tonic-gate controller->is_stats.nwritten += disk->is_stats.nwritten;
4877c478bd9Sstevel@tonic-gate controller->is_stats.reads += disk->is_stats.reads;
4887c478bd9Sstevel@tonic-gate controller->is_stats.writes += disk->is_stats.writes;
4897c478bd9Sstevel@tonic-gate controller->is_stats.wtime += disk->is_stats.wtime;
4907c478bd9Sstevel@tonic-gate controller->is_stats.wlentime += disk->is_stats.wlentime;
4917c478bd9Sstevel@tonic-gate controller->is_stats.rtime += disk->is_stats.rtime;
4927c478bd9Sstevel@tonic-gate controller->is_stats.rlentime += disk->is_stats.rlentime;
4937c478bd9Sstevel@tonic-gate controller->is_crtime += disk->is_crtime;
4947c478bd9Sstevel@tonic-gate controller->is_snaptime += disk->is_snaptime;
4957c478bd9Sstevel@tonic-gate if (kstat_add(&disk->is_errors, &controller->is_errors))
4967c478bd9Sstevel@tonic-gate return (errno);
4977c478bd9Sstevel@tonic-gate return (0);
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate
5007c478bd9Sstevel@tonic-gate static int
acquire_iodev_stats(struct iodev_snapshot * list,kstat_ctl_t * kc)5017c478bd9Sstevel@tonic-gate acquire_iodev_stats(struct iodev_snapshot *list, kstat_ctl_t *kc)
5027c478bd9Sstevel@tonic-gate {
5037c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos;
5047c478bd9Sstevel@tonic-gate int err = 0;
5057c478bd9Sstevel@tonic-gate
5067c478bd9Sstevel@tonic-gate for (pos = list; pos; pos = pos->is_next) {
5077c478bd9Sstevel@tonic-gate /* controllers don't have stats (yet) */
5087c478bd9Sstevel@tonic-gate if (pos->is_ksp != NULL) {
5097c478bd9Sstevel@tonic-gate if (kstat_read(kc, pos->is_ksp, &pos->is_stats) == -1)
5107c478bd9Sstevel@tonic-gate return (errno);
5117c478bd9Sstevel@tonic-gate /* make sure crtime/snaptime is updated */
5127c478bd9Sstevel@tonic-gate pos->is_crtime = pos->is_ksp->ks_crtime;
5137c478bd9Sstevel@tonic-gate pos->is_snaptime = pos->is_ksp->ks_snaptime;
5147c478bd9Sstevel@tonic-gate }
5157c478bd9Sstevel@tonic-gate
5167c478bd9Sstevel@tonic-gate if ((err = acquire_iodev_stats(pos->is_children, kc)))
5177c478bd9Sstevel@tonic-gate return (err);
5187c478bd9Sstevel@tonic-gate
5197c478bd9Sstevel@tonic-gate if (pos->is_type == IODEV_CONTROLLER) {
5207c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos2 = pos->is_children;
5217c478bd9Sstevel@tonic-gate
5227c478bd9Sstevel@tonic-gate for (; pos2; pos2 = pos2->is_next) {
5237c478bd9Sstevel@tonic-gate if ((err = collate_controller(pos, pos2)))
5247c478bd9Sstevel@tonic-gate return (err);
5257c478bd9Sstevel@tonic-gate }
5267c478bd9Sstevel@tonic-gate }
5277c478bd9Sstevel@tonic-gate }
5287c478bd9Sstevel@tonic-gate
5297c478bd9Sstevel@tonic-gate return (0);
5307c478bd9Sstevel@tonic-gate }
5317c478bd9Sstevel@tonic-gate
5327c478bd9Sstevel@tonic-gate static int
acquire_iodev_errors(struct snapshot * ss,kstat_ctl_t * kc)5337c478bd9Sstevel@tonic-gate acquire_iodev_errors(struct snapshot *ss, kstat_ctl_t *kc)
5347c478bd9Sstevel@tonic-gate {
5357c478bd9Sstevel@tonic-gate kstat_t *ksp;
5367c478bd9Sstevel@tonic-gate
5377c478bd9Sstevel@tonic-gate for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
5387c478bd9Sstevel@tonic-gate char kstat_name[KSTAT_STRLEN];
5397c478bd9Sstevel@tonic-gate char *dname = kstat_name;
5407c478bd9Sstevel@tonic-gate char *ename = ksp->ks_name;
5417c478bd9Sstevel@tonic-gate struct iodev_snapshot *iodev;
5427c478bd9Sstevel@tonic-gate
5437c478bd9Sstevel@tonic-gate if (ksp->ks_type != KSTAT_TYPE_NAMED)
5447c478bd9Sstevel@tonic-gate continue;
5457c478bd9Sstevel@tonic-gate if (strncmp(ksp->ks_class, "device_error", 12) != 0 &&
5467c478bd9Sstevel@tonic-gate strncmp(ksp->ks_class, "iopath_error", 12) != 0)
5477c478bd9Sstevel@tonic-gate continue;
5487c478bd9Sstevel@tonic-gate
5497c478bd9Sstevel@tonic-gate /*
5507c478bd9Sstevel@tonic-gate * Some drivers may not follow the naming convention
5517c478bd9Sstevel@tonic-gate * for error kstats (i.e., drivername,err) so
5527c478bd9Sstevel@tonic-gate * be sure we don't walk off the end.
5537c478bd9Sstevel@tonic-gate */
5547c478bd9Sstevel@tonic-gate while (*ename && *ename != ',') {
5557c478bd9Sstevel@tonic-gate *dname = *ename;
5567c478bd9Sstevel@tonic-gate dname++;
5577c478bd9Sstevel@tonic-gate ename++;
5587c478bd9Sstevel@tonic-gate }
5597c478bd9Sstevel@tonic-gate *dname = '\0';
5607c478bd9Sstevel@tonic-gate
5617c478bd9Sstevel@tonic-gate iodev = find_iodev_by_name(ss->s_iodevs, kstat_name);
5627c478bd9Sstevel@tonic-gate
5637c478bd9Sstevel@tonic-gate if (iodev == NULL)
5647c478bd9Sstevel@tonic-gate continue;
5657c478bd9Sstevel@tonic-gate
5667c478bd9Sstevel@tonic-gate if (kstat_read(kc, ksp, NULL) == -1)
5677c478bd9Sstevel@tonic-gate return (errno);
5687c478bd9Sstevel@tonic-gate if (kstat_copy(ksp, &iodev->is_errors) == -1)
5697c478bd9Sstevel@tonic-gate return (errno);
5707c478bd9Sstevel@tonic-gate }
5717c478bd9Sstevel@tonic-gate
5727c478bd9Sstevel@tonic-gate return (0);
5737c478bd9Sstevel@tonic-gate }
5747c478bd9Sstevel@tonic-gate
5757c478bd9Sstevel@tonic-gate static void
get_ids(struct iodev_snapshot * iodev,const char * pretty)5767c478bd9Sstevel@tonic-gate get_ids(struct iodev_snapshot *iodev, const char *pretty)
5777c478bd9Sstevel@tonic-gate {
5787c478bd9Sstevel@tonic-gate int ctr, disk, slice, ret;
5797c478bd9Sstevel@tonic-gate char *target;
5807c478bd9Sstevel@tonic-gate const char *p1;
5817c478bd9Sstevel@tonic-gate const char *p2;
5827c478bd9Sstevel@tonic-gate
5837c478bd9Sstevel@tonic-gate if (pretty == NULL)
5847c478bd9Sstevel@tonic-gate return;
5857c478bd9Sstevel@tonic-gate
5867c478bd9Sstevel@tonic-gate if (sscanf(pretty, "c%d", &ctr) != 1)
5877c478bd9Sstevel@tonic-gate return;
5887c478bd9Sstevel@tonic-gate
5897c478bd9Sstevel@tonic-gate p1 = pretty;
5907c478bd9Sstevel@tonic-gate while (*p1 && *p1 != 't')
5917c478bd9Sstevel@tonic-gate ++p1;
5927c478bd9Sstevel@tonic-gate
5937c478bd9Sstevel@tonic-gate if (!*p1)
5947c478bd9Sstevel@tonic-gate return;
5957c478bd9Sstevel@tonic-gate ++p1;
5967c478bd9Sstevel@tonic-gate
5977c478bd9Sstevel@tonic-gate p2 = p1;
5987c478bd9Sstevel@tonic-gate while (*p2 && *p2 != 'd')
5997c478bd9Sstevel@tonic-gate ++p2;
6007c478bd9Sstevel@tonic-gate
6017c478bd9Sstevel@tonic-gate if (!*p2 || p2 == p1)
6027c478bd9Sstevel@tonic-gate return;
6037c478bd9Sstevel@tonic-gate
6047c478bd9Sstevel@tonic-gate target = safe_alloc(1 + p2 - p1);
6057c478bd9Sstevel@tonic-gate (void) strlcpy(target, p1, 1 + p2 - p1);
6067c478bd9Sstevel@tonic-gate
6077c478bd9Sstevel@tonic-gate ret = sscanf(p2, "d%d%*[sp]%d", &disk, &slice);
6087c478bd9Sstevel@tonic-gate
6097c478bd9Sstevel@tonic-gate if (ret == 2 && iodev->is_type == IODEV_PARTITION) {
6107c478bd9Sstevel@tonic-gate iodev->is_id.id = slice;
6117c478bd9Sstevel@tonic-gate iodev->is_parent_id.id = disk;
6127c478bd9Sstevel@tonic-gate (void) strlcpy(iodev->is_parent_id.tid, target, KSTAT_STRLEN);
6137c478bd9Sstevel@tonic-gate } else if (ret == 1) {
6147c478bd9Sstevel@tonic-gate if (iodev->is_type == IODEV_DISK) {
6157c478bd9Sstevel@tonic-gate iodev->is_id.id = disk;
6167c478bd9Sstevel@tonic-gate (void) strlcpy(iodev->is_id.tid, target, KSTAT_STRLEN);
6177c478bd9Sstevel@tonic-gate iodev->is_parent_id.id = ctr;
61837fbbce5Scth } else if (iodev->is_type == IODEV_IOPATH_LTI) {
6197c478bd9Sstevel@tonic-gate iodev->is_parent_id.id = disk;
6207c478bd9Sstevel@tonic-gate (void) strlcpy(iodev->is_parent_id.tid,
6217c478bd9Sstevel@tonic-gate target, KSTAT_STRLEN);
6227c478bd9Sstevel@tonic-gate }
6237c478bd9Sstevel@tonic-gate }
6247c478bd9Sstevel@tonic-gate
6257c478bd9Sstevel@tonic-gate free(target);
6267c478bd9Sstevel@tonic-gate }
6277c478bd9Sstevel@tonic-gate
6287c478bd9Sstevel@tonic-gate static void
get_pretty_name(enum snapshot_types types,struct iodev_snapshot * iodev,kstat_ctl_t * kc)6297c478bd9Sstevel@tonic-gate get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev,
6307c478bd9Sstevel@tonic-gate kstat_ctl_t *kc)
6317c478bd9Sstevel@tonic-gate {
6327c478bd9Sstevel@tonic-gate disk_list_t *dl;
6337c478bd9Sstevel@tonic-gate char *pretty = NULL;
6347c478bd9Sstevel@tonic-gate
6357c478bd9Sstevel@tonic-gate if (iodev->is_type == IODEV_NFS) {
6367c478bd9Sstevel@tonic-gate if (!(types & SNAP_IODEV_PRETTY))
6377c478bd9Sstevel@tonic-gate return;
6387c478bd9Sstevel@tonic-gate
6397c478bd9Sstevel@tonic-gate iodev->is_pretty = lookup_nfs_name(iodev->is_name, kc);
6407c478bd9Sstevel@tonic-gate return;
6417c478bd9Sstevel@tonic-gate }
6427c478bd9Sstevel@tonic-gate
64337fbbce5Scth /* lookup/translate the kstat name */
644a08731ecScth dl = lookup_ks_name(iodev->is_name, (types & SNAP_IODEV_DEVID) ? 1 : 0);
6457c478bd9Sstevel@tonic-gate if (dl == NULL)
6467c478bd9Sstevel@tonic-gate return;
6477c478bd9Sstevel@tonic-gate
6487c478bd9Sstevel@tonic-gate if (dl->dsk)
6497c478bd9Sstevel@tonic-gate pretty = safe_strdup(dl->dsk);
6507c478bd9Sstevel@tonic-gate
6517c478bd9Sstevel@tonic-gate if (types & SNAP_IODEV_PRETTY) {
6527c478bd9Sstevel@tonic-gate if (dl->dname)
6537c478bd9Sstevel@tonic-gate iodev->is_dname = safe_strdup(dl->dname);
6547c478bd9Sstevel@tonic-gate }
6557c478bd9Sstevel@tonic-gate
6567c478bd9Sstevel@tonic-gate if (dl->devidstr)
6577c478bd9Sstevel@tonic-gate iodev->is_devid = safe_strdup(dl->devidstr);
6587c478bd9Sstevel@tonic-gate
6597c478bd9Sstevel@tonic-gate get_ids(iodev, pretty);
6607c478bd9Sstevel@tonic-gate
66137fbbce5Scth /*
66237fbbce5Scth * we fill in pretty name wether it is asked for or not because
66337fbbce5Scth * it could be used in a filter by match_iodevs.
66437fbbce5Scth */
6657c478bd9Sstevel@tonic-gate iodev->is_pretty = pretty;
6667c478bd9Sstevel@tonic-gate }
6677c478bd9Sstevel@tonic-gate
6687c478bd9Sstevel@tonic-gate static enum iodev_type
get_iodev_type(kstat_t * ksp)6697c478bd9Sstevel@tonic-gate get_iodev_type(kstat_t *ksp)
6707c478bd9Sstevel@tonic-gate {
6717c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "disk") == 0)
6727c478bd9Sstevel@tonic-gate return (IODEV_DISK);
6737c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "partition") == 0)
6747c478bd9Sstevel@tonic-gate return (IODEV_PARTITION);
6757c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "nfs") == 0)
6767c478bd9Sstevel@tonic-gate return (IODEV_NFS);
6777c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "iopath") == 0)
67837fbbce5Scth return (IODEV_IOPATH_LTI);
6797c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "tape") == 0)
6807c478bd9Sstevel@tonic-gate return (IODEV_TAPE);
6817c478bd9Sstevel@tonic-gate return (IODEV_UNKNOWN);
6827c478bd9Sstevel@tonic-gate }
6837c478bd9Sstevel@tonic-gate
68437fbbce5Scth /* get the lun/target/initiator from the name, return 1 on success */
68537fbbce5Scth static int
get_lti(char * s,char * lname,int * l,char * tname,int * t,char * iname,int * i)68637fbbce5Scth get_lti(char *s,
68737fbbce5Scth char *lname, int *l, char *tname, int *t, char *iname, int *i)
68837fbbce5Scth {
68937fbbce5Scth int num = 0;
69037fbbce5Scth
6910c3de118SChris Liu num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z_]%d", lname, l,
69237fbbce5Scth tname, t, iname, i);
69337fbbce5Scth return ((num == 6) ? 1 : 0);
69437fbbce5Scth }
69537fbbce5Scth
69637fbbce5Scth
69737fbbce5Scth /* get the lun, target, and initiator name and instance */
69837fbbce5Scth static void
get_path_info(struct iodev_snapshot * io,char * mod,size_t modlen,int * type,int * inst,char * name,size_t size)699fff11ba8SJonathan Cowper-Andrewes get_path_info(struct iodev_snapshot *io, char *mod, size_t modlen, int *type,
700fff11ba8SJonathan Cowper-Andrewes int *inst, char *name, size_t size)
70137fbbce5Scth {
70237fbbce5Scth
70337fbbce5Scth /*
70437fbbce5Scth * If it is iopath or ssd then pad the name with i/t/l so we can sort
70537fbbce5Scth * by alpha order and set type for IOPATH to DISK since we want to
70637fbbce5Scth * have it grouped with its ssd parent. The lun can be 5 digits,
70737fbbce5Scth * the target can be 4 digits, and the initiator can be 3 digits and
70837fbbce5Scth * the padding is done appropriately for string comparisons.
70937fbbce5Scth */
71037fbbce5Scth if (disk_or_partition_or_iopath(io->is_type)) {
71137fbbce5Scth int i1, t1, l1;
71237fbbce5Scth char tname[KSTAT_STRLEN], iname[KSTAT_STRLEN];
71337fbbce5Scth char *ptr, lname[KSTAT_STRLEN];
71437fbbce5Scth
71537fbbce5Scth i1 = t1 = l1 = 0;
71637fbbce5Scth (void) get_lti(io->is_name, lname, &l1, tname, &t1, iname, &i1);
71737fbbce5Scth *type = io->is_type;
71837fbbce5Scth if (io->is_type == IODEV_DISK) {
71937fbbce5Scth (void) snprintf(name, size, "%s%05d", lname, l1);
72037fbbce5Scth } else if (io->is_type == IODEV_PARTITION) {
72137fbbce5Scth ptr = strchr(io->is_name, ',');
72237fbbce5Scth (void) snprintf(name, size, "%s%05d%s", lname, l1, ptr);
72337fbbce5Scth } else {
72437fbbce5Scth (void) snprintf(name, size, "%s%05d.%s%04d.%s%03d",
72537fbbce5Scth lname, l1, tname, t1, iname, i1);
72637fbbce5Scth /* set to disk so we sort with disks */
72737fbbce5Scth *type = IODEV_DISK;
72837fbbce5Scth }
729fff11ba8SJonathan Cowper-Andrewes (void) strlcpy(mod, lname, modlen);
73037fbbce5Scth *inst = l1;
73137fbbce5Scth } else {
732fff11ba8SJonathan Cowper-Andrewes (void) strlcpy(mod, io->is_module, modlen);
733fff11ba8SJonathan Cowper-Andrewes (void) strlcpy(name, io->is_name, size);
73437fbbce5Scth *type = io->is_type;
73537fbbce5Scth *inst = io->is_instance;
73637fbbce5Scth }
73737fbbce5Scth }
73837fbbce5Scth
7397c478bd9Sstevel@tonic-gate int
iodev_cmp(struct iodev_snapshot * io1,struct iodev_snapshot * io2)7407c478bd9Sstevel@tonic-gate iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2)
7417c478bd9Sstevel@tonic-gate {
74237fbbce5Scth int type1, type2;
74337fbbce5Scth int inst1, inst2;
74437fbbce5Scth char name1[KSTAT_STRLEN], name2[KSTAT_STRLEN];
74537fbbce5Scth char mod1[KSTAT_STRLEN], mod2[KSTAT_STRLEN];
74637fbbce5Scth
747fff11ba8SJonathan Cowper-Andrewes get_path_info(io1, mod1, sizeof (mod1), &type1, &inst1, name1,
748fff11ba8SJonathan Cowper-Andrewes sizeof (name1));
749fff11ba8SJonathan Cowper-Andrewes get_path_info(io2, mod2, sizeof (mod2), &type2, &inst2, name2,
750fff11ba8SJonathan Cowper-Andrewes sizeof (name2));
75137fbbce5Scth if ((!disk_or_partition(type1)) ||
75237fbbce5Scth (!disk_or_partition(type2))) {
7537c478bd9Sstevel@tonic-gate /* neutral sort order between disk and part */
75437fbbce5Scth if (type1 < type2) {
7557c478bd9Sstevel@tonic-gate return (-1);
75637fbbce5Scth }
75737fbbce5Scth if (type1 > type2) {
7587c478bd9Sstevel@tonic-gate return (1);
7597c478bd9Sstevel@tonic-gate }
76037fbbce5Scth }
7617c478bd9Sstevel@tonic-gate
7627c478bd9Sstevel@tonic-gate /* controller doesn't have ksp */
7637c478bd9Sstevel@tonic-gate if (io1->is_ksp && io2->is_ksp) {
76437fbbce5Scth if (strcmp(mod1, mod2) != 0) {
76537fbbce5Scth return (strcmp(mod1, mod2));
76637fbbce5Scth }
76737fbbce5Scth if (inst1 < inst2) {
7687c478bd9Sstevel@tonic-gate return (-1);
76937fbbce5Scth }
77037fbbce5Scth if (inst1 > inst2) {
7717c478bd9Sstevel@tonic-gate return (1);
77237fbbce5Scth }
7737c478bd9Sstevel@tonic-gate } else {
77437fbbce5Scth if (io1->is_id.id < io2->is_id.id) {
7757c478bd9Sstevel@tonic-gate return (-1);
77637fbbce5Scth }
77737fbbce5Scth if (io1->is_id.id > io2->is_id.id) {
77837fbbce5Scth return (1);
77937fbbce5Scth }
78037fbbce5Scth }
78137fbbce5Scth
78237fbbce5Scth return (strcmp(name1, name2));
78337fbbce5Scth }
78437fbbce5Scth
78537fbbce5Scth /* update the target reads and writes */
78637fbbce5Scth static void
update_target(struct iodev_snapshot * tgt,struct iodev_snapshot * path)78737fbbce5Scth update_target(struct iodev_snapshot *tgt, struct iodev_snapshot *path)
78837fbbce5Scth {
78937fbbce5Scth tgt->is_stats.reads += path->is_stats.reads;
79037fbbce5Scth tgt->is_stats.writes += path->is_stats.writes;
79137fbbce5Scth tgt->is_stats.nread += path->is_stats.nread;
79237fbbce5Scth tgt->is_stats.nwritten += path->is_stats.nwritten;
79337fbbce5Scth tgt->is_stats.wcnt += path->is_stats.wcnt;
79437fbbce5Scth tgt->is_stats.rcnt += path->is_stats.rcnt;
79537fbbce5Scth
79637fbbce5Scth /*
79737fbbce5Scth * Stash the t_delta in the crtime for use in show_disk
79837fbbce5Scth * NOTE: this can't be done in show_disk because the
79937fbbce5Scth * itl entry is removed for the old format
80037fbbce5Scth */
80137fbbce5Scth tgt->is_crtime += hrtime_delta(path->is_crtime, path->is_snaptime);
80237fbbce5Scth tgt->is_snaptime += path->is_snaptime;
80337fbbce5Scth tgt->is_nr_children += 1;
80437fbbce5Scth }
80537fbbce5Scth
80637fbbce5Scth /*
80737fbbce5Scth * Create a new synthetic device entry of the specified type. The supported
80837fbbce5Scth * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI.
80937fbbce5Scth */
81037fbbce5Scth static struct iodev_snapshot *
make_extended_device(int type,struct iodev_snapshot * old)81137fbbce5Scth make_extended_device(int type, struct iodev_snapshot *old)
81237fbbce5Scth {
81337fbbce5Scth struct iodev_snapshot *tptr = NULL;
81437fbbce5Scth char *ptr;
81537fbbce5Scth int lun, tgt, initiator;
81637fbbce5Scth char lun_name[KSTAT_STRLEN];
81737fbbce5Scth char tgt_name[KSTAT_STRLEN];
81837fbbce5Scth char initiator_name[KSTAT_STRLEN];
81937fbbce5Scth
82037fbbce5Scth if (old == NULL)
82137fbbce5Scth return (NULL);
82237fbbce5Scth if (get_lti(old->is_name,
82337fbbce5Scth lun_name, &lun, tgt_name, &tgt, initiator_name, &initiator) != 1) {
82437fbbce5Scth return (NULL);
82537fbbce5Scth }
82637fbbce5Scth tptr = safe_alloc(sizeof (*old));
82737fbbce5Scth bzero(tptr, sizeof (*old));
82837fbbce5Scth if (old->is_pretty != NULL) {
82937fbbce5Scth tptr->is_pretty = safe_alloc(strlen(old->is_pretty) + 1);
83037fbbce5Scth (void) strcpy(tptr->is_pretty, old->is_pretty);
83137fbbce5Scth }
83237fbbce5Scth bcopy(&old->is_parent_id, &tptr->is_parent_id,
83337fbbce5Scth sizeof (old->is_parent_id));
83437fbbce5Scth
83537fbbce5Scth tptr->is_type = type;
83637fbbce5Scth
83737fbbce5Scth if (type == IODEV_IOPATH_LT) {
83837fbbce5Scth /* make new synthetic entry that is the LT */
83937fbbce5Scth /* set the id to the target id */
84037fbbce5Scth tptr->is_id.id = tgt;
84137fbbce5Scth (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
84237fbbce5Scth "%s%d", tgt_name, tgt);
84337fbbce5Scth (void) snprintf(tptr->is_name, sizeof (tptr->is_name),
84437fbbce5Scth "%s%d.%s%d", lun_name, lun, tgt_name, tgt);
84537fbbce5Scth
84637fbbce5Scth if (old->is_pretty) {
84737fbbce5Scth ptr = strrchr(tptr->is_pretty, '.');
84837fbbce5Scth if (ptr)
84937fbbce5Scth *ptr = '\0';
85037fbbce5Scth }
85137fbbce5Scth } else if (type == IODEV_IOPATH_LI) {
85237fbbce5Scth /* make new synthetic entry that is the LI */
85337fbbce5Scth /* set the id to the initiator number */
85437fbbce5Scth tptr->is_id.id = initiator;
85537fbbce5Scth (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid),
85637fbbce5Scth "%s%d", initiator_name, initiator);
85737fbbce5Scth (void) snprintf(tptr->is_name, sizeof (tptr->is_name),
85837fbbce5Scth "%s%d.%s%d", lun_name, lun, initiator_name, initiator);
85937fbbce5Scth
86037fbbce5Scth if (old->is_pretty) {
86137fbbce5Scth ptr = strchr(tptr->is_pretty, '.');
86237fbbce5Scth if (ptr)
86337fbbce5Scth (void) snprintf(ptr + 1,
86437fbbce5Scth strlen(tptr->is_pretty) + 1,
86537fbbce5Scth "%s%d", initiator_name, initiator);
86637fbbce5Scth }
86737fbbce5Scth }
86837fbbce5Scth return (tptr);
86937fbbce5Scth }
87037fbbce5Scth
87137fbbce5Scth /*
87237fbbce5Scth * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat
87337fbbce5Scth * is found - traverse the children looking for the same initiator and sum
87437fbbce5Scth * them up. Add an LI entry and delete all of the LTI entries with the same
87537fbbce5Scth * initiator.
87637fbbce5Scth */
87737fbbce5Scth static int
create_li_delete_lti(struct snapshot * ss,struct iodev_snapshot * list)87837fbbce5Scth create_li_delete_lti(struct snapshot *ss, struct iodev_snapshot *list)
87937fbbce5Scth {
88037fbbce5Scth struct iodev_snapshot *pos, *entry, *parent;
88137fbbce5Scth int lun, tgt, initiator;
88237fbbce5Scth char lun_name[KSTAT_STRLEN];
88337fbbce5Scth char tgt_name[KSTAT_STRLEN];
88437fbbce5Scth char initiator_name[KSTAT_STRLEN];
88537fbbce5Scth int err;
88637fbbce5Scth
88737fbbce5Scth for (entry = list; entry; entry = entry->is_next) {
88837fbbce5Scth if ((err = create_li_delete_lti(ss, entry->is_children)) != 0)
88937fbbce5Scth return (err);
89037fbbce5Scth
89137fbbce5Scth if (entry->is_type == IODEV_IOPATH_LTI) {
89237fbbce5Scth parent = find_parent(ss, entry);
89337fbbce5Scth if (get_lti(entry->is_name, lun_name, &lun,
89437fbbce5Scth tgt_name, &tgt, initiator_name, &initiator) != 1) {
8957c478bd9Sstevel@tonic-gate return (1);
8967c478bd9Sstevel@tonic-gate }
8977c478bd9Sstevel@tonic-gate
89837fbbce5Scth pos = (parent == NULL) ? NULL : parent->is_children;
89937fbbce5Scth for (; pos; pos = pos->is_next) {
90037fbbce5Scth if (pos->is_id.id != -1 &&
90137fbbce5Scth pos->is_id.id == initiator &&
90237fbbce5Scth pos->is_type == IODEV_IOPATH_LI) {
90337fbbce5Scth /* found the same initiator */
90437fbbce5Scth update_target(pos, entry);
90537fbbce5Scth list_del(&parent->is_children, entry);
90637fbbce5Scth free_iodev(entry);
90737fbbce5Scth parent->is_nr_children--;
90837fbbce5Scth entry = pos;
90937fbbce5Scth break;
91037fbbce5Scth }
91137fbbce5Scth }
91237fbbce5Scth
91337fbbce5Scth if (!pos) {
91437fbbce5Scth /* make the first LI entry */
91537fbbce5Scth pos = make_extended_device(
91637fbbce5Scth IODEV_IOPATH_LI, entry);
91737fbbce5Scth update_target(pos, entry);
91837fbbce5Scth
91937fbbce5Scth if (parent) {
92037fbbce5Scth insert_before(&parent->is_children,
92137fbbce5Scth entry, pos);
92237fbbce5Scth list_del(&parent->is_children, entry);
92337fbbce5Scth free_iodev(entry);
92437fbbce5Scth } else {
92537fbbce5Scth insert_before(&ss->s_iodevs, entry,
92637fbbce5Scth pos);
92737fbbce5Scth list_del(&ss->s_iodevs, entry);
92837fbbce5Scth free_iodev(entry);
92937fbbce5Scth }
93037fbbce5Scth entry = pos;
93137fbbce5Scth }
93237fbbce5Scth }
93337fbbce5Scth }
93437fbbce5Scth return (0);
93537fbbce5Scth }
93637fbbce5Scth
93737fbbce5Scth /*
93837fbbce5Scth * We have the LTI kstat, now add an entry for the LT that sums up all of
93937fbbce5Scth * the LTI's with the same target(t).
94037fbbce5Scth */
94137fbbce5Scth static int
create_lt(struct snapshot * ss,struct iodev_snapshot * list)94237fbbce5Scth create_lt(struct snapshot *ss, struct iodev_snapshot *list)
94337fbbce5Scth {
94437fbbce5Scth struct iodev_snapshot *entry, *parent, *pos;
94537fbbce5Scth int lun, tgt, initiator;
94637fbbce5Scth char lun_name[KSTAT_STRLEN];
94737fbbce5Scth char tgt_name[KSTAT_STRLEN];
94837fbbce5Scth char initiator_name[KSTAT_STRLEN];
94937fbbce5Scth int err;
95037fbbce5Scth
95137fbbce5Scth for (entry = list; entry; entry = entry->is_next) {
95237fbbce5Scth if ((err = create_lt(ss, entry->is_children)) != 0)
95337fbbce5Scth return (err);
95437fbbce5Scth
95537fbbce5Scth if (entry->is_type == IODEV_IOPATH_LTI) {
95637fbbce5Scth parent = find_parent(ss, entry);
95737fbbce5Scth if (get_lti(entry->is_name, lun_name, &lun,
95837fbbce5Scth tgt_name, &tgt, initiator_name, &initiator) != 1) {
95937fbbce5Scth return (1);
96037fbbce5Scth }
96137fbbce5Scth
96237fbbce5Scth pos = (parent == NULL) ? NULL : parent->is_children;
96337fbbce5Scth for (; pos; pos = pos->is_next) {
96437fbbce5Scth if (pos->is_id.id != -1 &&
96537fbbce5Scth pos->is_id.id == tgt &&
96637fbbce5Scth pos->is_type == IODEV_IOPATH_LT) {
96737fbbce5Scth /* found the same target */
96837fbbce5Scth update_target(pos, entry);
96937fbbce5Scth break;
97037fbbce5Scth }
97137fbbce5Scth }
97237fbbce5Scth
97337fbbce5Scth if (!pos) {
97437fbbce5Scth pos = make_extended_device(
97537fbbce5Scth IODEV_IOPATH_LT, entry);
97637fbbce5Scth update_target(pos, entry);
97737fbbce5Scth
97837fbbce5Scth if (parent) {
97937fbbce5Scth insert_before(&parent->is_children,
98037fbbce5Scth entry, pos);
98137fbbce5Scth parent->is_nr_children++;
98237fbbce5Scth } else {
98337fbbce5Scth insert_before(&ss->s_iodevs,
98437fbbce5Scth entry, pos);
98537fbbce5Scth }
98637fbbce5Scth }
98737fbbce5Scth }
98837fbbce5Scth }
98937fbbce5Scth return (0);
99037fbbce5Scth }
99137fbbce5Scth
99237fbbce5Scth /* Find the longest is_name field to aid formatting of output */
99337fbbce5Scth static int
iodevs_is_name_maxlen(struct iodev_snapshot * list)99437fbbce5Scth iodevs_is_name_maxlen(struct iodev_snapshot *list)
99537fbbce5Scth {
99637fbbce5Scth struct iodev_snapshot *entry;
99737fbbce5Scth int max = 0, cmax, len;
99837fbbce5Scth
99937fbbce5Scth for (entry = list; entry; entry = entry->is_next) {
100037fbbce5Scth cmax = iodevs_is_name_maxlen(entry->is_children);
100137fbbce5Scth max = (cmax > max) ? cmax : max;
100237fbbce5Scth len = strlen(entry->is_name);
100337fbbce5Scth max = (len > max) ? len : max;
100437fbbce5Scth }
100537fbbce5Scth return (max);
10067c478bd9Sstevel@tonic-gate }
10077c478bd9Sstevel@tonic-gate
10087c478bd9Sstevel@tonic-gate int
acquire_iodevs(struct snapshot * ss,kstat_ctl_t * kc,struct iodev_filter * df)10097c478bd9Sstevel@tonic-gate acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df)
10107c478bd9Sstevel@tonic-gate {
10117c478bd9Sstevel@tonic-gate kstat_t *ksp;
10127c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos;
10137c478bd9Sstevel@tonic-gate struct iodev_snapshot *list = NULL;
101437fbbce5Scth int err = 0;
10157c478bd9Sstevel@tonic-gate
10167c478bd9Sstevel@tonic-gate ss->s_nr_iodevs = 0;
101737fbbce5Scth ss->s_iodevs_is_name_maxlen = 0;
10187c478bd9Sstevel@tonic-gate
1019a08731ecScth /*
1020a08731ecScth * Call cleanup_iodevs_snapshot() so that a cache miss in
1021a08731ecScth * lookup_ks_name() will result in a fresh snapshot.
1022a08731ecScth */
1023a08731ecScth cleanup_iodevs_snapshot();
1024a08731ecScth
10257c478bd9Sstevel@tonic-gate for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
10267c478bd9Sstevel@tonic-gate enum iodev_type type;
10277c478bd9Sstevel@tonic-gate
10287c478bd9Sstevel@tonic-gate if (ksp->ks_type != KSTAT_TYPE_IO)
10297c478bd9Sstevel@tonic-gate continue;
10307c478bd9Sstevel@tonic-gate
10317c478bd9Sstevel@tonic-gate /* e.g. "usb_byte_count" is not handled */
10327c478bd9Sstevel@tonic-gate if ((type = get_iodev_type(ksp)) == IODEV_UNKNOWN)
10337c478bd9Sstevel@tonic-gate continue;
10347c478bd9Sstevel@tonic-gate
10357c478bd9Sstevel@tonic-gate if (df && !(type & df->if_allowed_types))
10367c478bd9Sstevel@tonic-gate continue;
10377c478bd9Sstevel@tonic-gate
10387c478bd9Sstevel@tonic-gate if ((pos = malloc(sizeof (struct iodev_snapshot))) == NULL) {
10397c478bd9Sstevel@tonic-gate err = errno;
10407c478bd9Sstevel@tonic-gate goto out;
10417c478bd9Sstevel@tonic-gate }
10427c478bd9Sstevel@tonic-gate
10437c478bd9Sstevel@tonic-gate (void) memset(pos, 0, sizeof (struct iodev_snapshot));
10447c478bd9Sstevel@tonic-gate
10457c478bd9Sstevel@tonic-gate pos->is_type = type;
10467c478bd9Sstevel@tonic-gate pos->is_crtime = ksp->ks_crtime;
10477c478bd9Sstevel@tonic-gate pos->is_snaptime = ksp->ks_snaptime;
10487c478bd9Sstevel@tonic-gate pos->is_id.id = IODEV_NO_ID;
10497c478bd9Sstevel@tonic-gate pos->is_parent_id.id = IODEV_NO_ID;
10507c478bd9Sstevel@tonic-gate pos->is_ksp = ksp;
10517c478bd9Sstevel@tonic-gate pos->is_instance = ksp->ks_instance;
10527c478bd9Sstevel@tonic-gate
10537c478bd9Sstevel@tonic-gate (void) strlcpy(pos->is_module, ksp->ks_module, KSTAT_STRLEN);
10547c478bd9Sstevel@tonic-gate (void) strlcpy(pos->is_name, ksp->ks_name, KSTAT_STRLEN);
10557c478bd9Sstevel@tonic-gate get_pretty_name(ss->s_types, pos, kc);
10567c478bd9Sstevel@tonic-gate
10577c478bd9Sstevel@tonic-gate /*
10587c478bd9Sstevel@tonic-gate * We must insert in sort order so e.g. vmstat -l
10597c478bd9Sstevel@tonic-gate * chooses in order.
10607c478bd9Sstevel@tonic-gate */
10617c478bd9Sstevel@tonic-gate insert_into(&list, pos);
10627c478bd9Sstevel@tonic-gate }
10637c478bd9Sstevel@tonic-gate
10647c478bd9Sstevel@tonic-gate choose_iodevs(ss, list, df);
10657c478bd9Sstevel@tonic-gate
10667c478bd9Sstevel@tonic-gate /* before acquire_stats for collate_controller()'s benefit */
10677c478bd9Sstevel@tonic-gate if (ss->s_types & SNAP_IODEV_ERRORS) {
10687c478bd9Sstevel@tonic-gate if ((err = acquire_iodev_errors(ss, kc)) != 0)
10697c478bd9Sstevel@tonic-gate goto out;
10707c478bd9Sstevel@tonic-gate }
10717c478bd9Sstevel@tonic-gate
10727c478bd9Sstevel@tonic-gate if ((err = acquire_iodev_stats(ss->s_iodevs, kc)) != 0)
10737c478bd9Sstevel@tonic-gate goto out;
10747c478bd9Sstevel@tonic-gate
107537fbbce5Scth if (ss->s_types & SNAP_IOPATHS_LTI) {
107637fbbce5Scth /*
107737fbbce5Scth * -Y: kstats are LTI, need to create a synthetic LT
107837fbbce5Scth * for -Y output.
107937fbbce5Scth */
108037fbbce5Scth if ((err = create_lt(ss, ss->s_iodevs)) != 0) {
108137fbbce5Scth return (err);
108237fbbce5Scth }
108337fbbce5Scth }
108437fbbce5Scth if (ss->s_types & SNAP_IOPATHS_LI) {
108537fbbce5Scth /*
108637fbbce5Scth * -X: kstats are LTI, need to create a synthetic LI and
108737fbbce5Scth * delete the LTI for -X output
108837fbbce5Scth */
108937fbbce5Scth if ((err = create_li_delete_lti(ss, ss->s_iodevs)) != 0) {
109037fbbce5Scth return (err);
109137fbbce5Scth }
109237fbbce5Scth }
109337fbbce5Scth
109437fbbce5Scth /* determine width of longest is_name */
109537fbbce5Scth ss->s_iodevs_is_name_maxlen = iodevs_is_name_maxlen(ss->s_iodevs);
109637fbbce5Scth
10977c478bd9Sstevel@tonic-gate err = 0;
10987c478bd9Sstevel@tonic-gate out:
10997c478bd9Sstevel@tonic-gate return (err);
11007c478bd9Sstevel@tonic-gate }
11017c478bd9Sstevel@tonic-gate
11027c478bd9Sstevel@tonic-gate void
free_iodev(struct iodev_snapshot * iodev)11037c478bd9Sstevel@tonic-gate free_iodev(struct iodev_snapshot *iodev)
11047c478bd9Sstevel@tonic-gate {
11057c478bd9Sstevel@tonic-gate while (iodev->is_children) {
11067c478bd9Sstevel@tonic-gate struct iodev_snapshot *tmp = iodev->is_children;
11077c478bd9Sstevel@tonic-gate iodev->is_children = iodev->is_children->is_next;
11087c478bd9Sstevel@tonic-gate free_iodev(tmp);
11097c478bd9Sstevel@tonic-gate }
11107c478bd9Sstevel@tonic-gate
11114be70790Swroche if (iodev->avl_list) {
11124be70790Swroche avl_remove(iodev->avl_list, iodev);
11134be70790Swroche if (avl_numnodes(iodev->avl_list) == 0) {
11144be70790Swroche avl_destroy(iodev->avl_list);
11154be70790Swroche free(iodev->avl_list);
11164be70790Swroche }
11174be70790Swroche }
11184be70790Swroche
11197c478bd9Sstevel@tonic-gate free(iodev->is_errors.ks_data);
11207c478bd9Sstevel@tonic-gate free(iodev->is_pretty);
11217c478bd9Sstevel@tonic-gate free(iodev->is_dname);
11227c478bd9Sstevel@tonic-gate free(iodev->is_devid);
11237c478bd9Sstevel@tonic-gate free(iodev);
11247c478bd9Sstevel@tonic-gate }
1125