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 /* 22*4be70790Swroche * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate #include "statcommon.h" 297c478bd9Sstevel@tonic-gate #include "dsr.h" 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate #include <sys/dklabel.h> 327c478bd9Sstevel@tonic-gate #include <sys/dktp/fdisk.h> 337c478bd9Sstevel@tonic-gate #include <stdlib.h> 347c478bd9Sstevel@tonic-gate #include <stdarg.h> 357c478bd9Sstevel@tonic-gate #include <unistd.h> 367c478bd9Sstevel@tonic-gate #include <strings.h> 377c478bd9Sstevel@tonic-gate #include <errno.h> 387c478bd9Sstevel@tonic-gate #include <limits.h> 397c478bd9Sstevel@tonic-gate 407c478bd9Sstevel@tonic-gate static void insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev); 417c478bd9Sstevel@tonic-gate 427c478bd9Sstevel@tonic-gate static struct iodev_snapshot * 437c478bd9Sstevel@tonic-gate make_controller(int cid) 447c478bd9Sstevel@tonic-gate { 457c478bd9Sstevel@tonic-gate struct iodev_snapshot *new; 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate new = safe_alloc(sizeof (struct iodev_snapshot)); 487c478bd9Sstevel@tonic-gate (void) memset(new, 0, sizeof (struct iodev_snapshot)); 497c478bd9Sstevel@tonic-gate new->is_type = IODEV_CONTROLLER; 507c478bd9Sstevel@tonic-gate new->is_id.id = cid; 517c478bd9Sstevel@tonic-gate new->is_parent_id.id = IODEV_NO_ID; 527c478bd9Sstevel@tonic-gate 537c478bd9Sstevel@tonic-gate (void) snprintf(new->is_name, sizeof (new->is_name), "c%d", cid); 547c478bd9Sstevel@tonic-gate 557c478bd9Sstevel@tonic-gate return (new); 567c478bd9Sstevel@tonic-gate } 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate static struct iodev_snapshot * 597c478bd9Sstevel@tonic-gate find_iodev_by_name(struct iodev_snapshot *list, const char *name) 607c478bd9Sstevel@tonic-gate { 617c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos; 627c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos2; 637c478bd9Sstevel@tonic-gate 647c478bd9Sstevel@tonic-gate for (pos = list; pos; pos = pos->is_next) { 657c478bd9Sstevel@tonic-gate if (strcmp(pos->is_name, name) == 0) 667c478bd9Sstevel@tonic-gate return (pos); 677c478bd9Sstevel@tonic-gate 687c478bd9Sstevel@tonic-gate pos2 = find_iodev_by_name(pos->is_children, name); 697c478bd9Sstevel@tonic-gate if (pos2 != NULL) 707c478bd9Sstevel@tonic-gate return (pos2); 717c478bd9Sstevel@tonic-gate } 727c478bd9Sstevel@tonic-gate 737c478bd9Sstevel@tonic-gate return (NULL); 747c478bd9Sstevel@tonic-gate } 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate static enum iodev_type 777c478bd9Sstevel@tonic-gate parent_iodev_type(enum iodev_type type) 787c478bd9Sstevel@tonic-gate { 797c478bd9Sstevel@tonic-gate switch (type) { 807c478bd9Sstevel@tonic-gate case IODEV_CONTROLLER: return (0); 8137fbbce5Scth case IODEV_IOPATH_LT: return (0); 8237fbbce5Scth case IODEV_IOPATH_LI: return (0); 837c478bd9Sstevel@tonic-gate case IODEV_NFS: return (0); 847c478bd9Sstevel@tonic-gate case IODEV_TAPE: return (0); 8537fbbce5Scth case IODEV_IOPATH_LTI: return (IODEV_DISK); 867c478bd9Sstevel@tonic-gate case IODEV_DISK: return (IODEV_CONTROLLER); 877c478bd9Sstevel@tonic-gate case IODEV_PARTITION: return (IODEV_DISK); 887c478bd9Sstevel@tonic-gate } 897c478bd9Sstevel@tonic-gate return (IODEV_UNKNOWN); 907c478bd9Sstevel@tonic-gate } 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate static int 937c478bd9Sstevel@tonic-gate id_match(struct iodev_id *id1, struct iodev_id *id2) 947c478bd9Sstevel@tonic-gate { 957c478bd9Sstevel@tonic-gate return (id1->id == id2->id && 967c478bd9Sstevel@tonic-gate strcmp(id1->tid, id2->tid) == 0); 977c478bd9Sstevel@tonic-gate } 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate static struct iodev_snapshot * 1007c478bd9Sstevel@tonic-gate find_parent(struct snapshot *ss, struct iodev_snapshot *iodev) 1017c478bd9Sstevel@tonic-gate { 1027c478bd9Sstevel@tonic-gate enum iodev_type parent_type = parent_iodev_type(iodev->is_type); 1037c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos; 1047c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos2; 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate if (parent_type == 0 || parent_type == IODEV_UNKNOWN) 1077c478bd9Sstevel@tonic-gate return (NULL); 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate if (iodev->is_parent_id.id == IODEV_NO_ID && 1107c478bd9Sstevel@tonic-gate iodev->is_parent_id.tid[0] == '\0') 1117c478bd9Sstevel@tonic-gate return (NULL); 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate if (parent_type == IODEV_CONTROLLER) { 1147c478bd9Sstevel@tonic-gate for (pos = ss->s_iodevs; pos; pos = pos->is_next) { 1157c478bd9Sstevel@tonic-gate if (pos->is_type != IODEV_CONTROLLER) 1167c478bd9Sstevel@tonic-gate continue; 1177c478bd9Sstevel@tonic-gate if (pos->is_id.id != iodev->is_parent_id.id) 1187c478bd9Sstevel@tonic-gate continue; 1197c478bd9Sstevel@tonic-gate return (pos); 1207c478bd9Sstevel@tonic-gate } 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate if (!(ss->s_types & SNAP_CONTROLLERS)) 1237c478bd9Sstevel@tonic-gate return (NULL); 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate pos = make_controller(iodev->is_parent_id.id); 1267c478bd9Sstevel@tonic-gate insert_iodev(ss, pos); 1277c478bd9Sstevel@tonic-gate return (pos); 1287c478bd9Sstevel@tonic-gate } 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate /* IODEV_DISK parent */ 1317c478bd9Sstevel@tonic-gate for (pos = ss->s_iodevs; pos; pos = pos->is_next) { 1327c478bd9Sstevel@tonic-gate if (id_match(&iodev->is_parent_id, &pos->is_id) && 1337c478bd9Sstevel@tonic-gate pos->is_type == IODEV_DISK) 1347c478bd9Sstevel@tonic-gate return (pos); 1357c478bd9Sstevel@tonic-gate if (pos->is_type != IODEV_CONTROLLER) 1367c478bd9Sstevel@tonic-gate continue; 1377c478bd9Sstevel@tonic-gate for (pos2 = pos->is_children; pos2; pos2 = pos2->is_next) { 1387c478bd9Sstevel@tonic-gate if (pos2->is_type != IODEV_DISK) 1397c478bd9Sstevel@tonic-gate continue; 1407c478bd9Sstevel@tonic-gate if (id_match(&iodev->is_parent_id, &pos2->is_id)) 1417c478bd9Sstevel@tonic-gate return (pos2); 1427c478bd9Sstevel@tonic-gate } 1437c478bd9Sstevel@tonic-gate } 1447c478bd9Sstevel@tonic-gate 1457c478bd9Sstevel@tonic-gate return (NULL); 1467c478bd9Sstevel@tonic-gate } 1477c478bd9Sstevel@tonic-gate 148*4be70790Swroche /* 149*4be70790Swroche * Introduce an index into the list to speed up insert_into looking for the 150*4be70790Swroche * right position in the list. This index is an AVL tree of all the 151*4be70790Swroche * iodev_snapshot in the list. 152*4be70790Swroche */ 153*4be70790Swroche 154*4be70790Swroche #define offsetof(s, m) (size_t)(&(((s *)0)->m)) /* for avl_create */ 155*4be70790Swroche 156*4be70790Swroche static int 157*4be70790Swroche avl_iodev_cmp(const void* is1, const void* is2) 158*4be70790Swroche { 159*4be70790Swroche int c = iodev_cmp((struct iodev_snapshot *)is1, 160*4be70790Swroche (struct iodev_snapshot *)is2); 161*4be70790Swroche 162*4be70790Swroche if (c > 0) 163*4be70790Swroche return (1); 164*4be70790Swroche 165*4be70790Swroche if (c < 0) 166*4be70790Swroche return (-1); 167*4be70790Swroche 168*4be70790Swroche return (0); 169*4be70790Swroche } 170*4be70790Swroche 171*4be70790Swroche static void 172*4be70790Swroche ix_new_list(struct iodev_snapshot *elem) 173*4be70790Swroche { 174*4be70790Swroche avl_tree_t *l = malloc(sizeof (avl_tree_t)); 175*4be70790Swroche 176*4be70790Swroche elem->avl_list = l; 177*4be70790Swroche if (l == NULL) 178*4be70790Swroche return; 179*4be70790Swroche 180*4be70790Swroche avl_create(l, avl_iodev_cmp, sizeof (struct iodev_snapshot), 181*4be70790Swroche offsetof(struct iodev_snapshot, avl_link)); 182*4be70790Swroche 183*4be70790Swroche avl_add(l, elem); 184*4be70790Swroche } 185*4be70790Swroche 186*4be70790Swroche static void 187*4be70790Swroche ix_list_del(struct iodev_snapshot *elem) 188*4be70790Swroche { 189*4be70790Swroche avl_tree_t *l = elem->avl_list; 190*4be70790Swroche 191*4be70790Swroche if (l == NULL) 192*4be70790Swroche return; 193*4be70790Swroche 194*4be70790Swroche elem->avl_list = NULL; 195*4be70790Swroche 196*4be70790Swroche avl_remove(l, elem); 197*4be70790Swroche if (avl_numnodes(l) == 0) { 198*4be70790Swroche avl_destroy(l); 199*4be70790Swroche free(l); 200*4be70790Swroche } 201*4be70790Swroche } 202*4be70790Swroche 203*4be70790Swroche static void 204*4be70790Swroche ix_insert_here(struct iodev_snapshot *pos, struct iodev_snapshot *elem, int ba) 205*4be70790Swroche { 206*4be70790Swroche avl_tree_t *l = pos->avl_list; 207*4be70790Swroche elem->avl_list = l; 208*4be70790Swroche 209*4be70790Swroche if (l == NULL) 210*4be70790Swroche return; 211*4be70790Swroche 212*4be70790Swroche avl_insert_here(l, elem, pos, ba); 213*4be70790Swroche } 214*4be70790Swroche 2157c478bd9Sstevel@tonic-gate static void 2167c478bd9Sstevel@tonic-gate list_del(struct iodev_snapshot **list, struct iodev_snapshot *pos) 2177c478bd9Sstevel@tonic-gate { 218*4be70790Swroche ix_list_del(pos); 219*4be70790Swroche 2207c478bd9Sstevel@tonic-gate if (*list == pos) 2217c478bd9Sstevel@tonic-gate *list = pos->is_next; 2227c478bd9Sstevel@tonic-gate if (pos->is_next) 2237c478bd9Sstevel@tonic-gate pos->is_next->is_prev = pos->is_prev; 2247c478bd9Sstevel@tonic-gate if (pos->is_prev) 2257c478bd9Sstevel@tonic-gate pos->is_prev->is_next = pos->is_next; 2267c478bd9Sstevel@tonic-gate pos->is_prev = pos->is_next = NULL; 2277c478bd9Sstevel@tonic-gate } 2287c478bd9Sstevel@tonic-gate 2297c478bd9Sstevel@tonic-gate static void 2307c478bd9Sstevel@tonic-gate insert_before(struct iodev_snapshot **list, struct iodev_snapshot *pos, 2317c478bd9Sstevel@tonic-gate struct iodev_snapshot *new) 2327c478bd9Sstevel@tonic-gate { 2337c478bd9Sstevel@tonic-gate if (pos == NULL) { 2347c478bd9Sstevel@tonic-gate new->is_prev = new->is_next = NULL; 2357c478bd9Sstevel@tonic-gate *list = new; 236*4be70790Swroche ix_new_list(new); 2377c478bd9Sstevel@tonic-gate return; 2387c478bd9Sstevel@tonic-gate } 2397c478bd9Sstevel@tonic-gate 2407c478bd9Sstevel@tonic-gate new->is_next = pos; 2417c478bd9Sstevel@tonic-gate new->is_prev = pos->is_prev; 2427c478bd9Sstevel@tonic-gate if (pos->is_prev) 2437c478bd9Sstevel@tonic-gate pos->is_prev->is_next = new; 2447c478bd9Sstevel@tonic-gate else 2457c478bd9Sstevel@tonic-gate *list = new; 2467c478bd9Sstevel@tonic-gate pos->is_prev = new; 247*4be70790Swroche 248*4be70790Swroche ix_insert_here(pos, new, AVL_BEFORE); 2497c478bd9Sstevel@tonic-gate } 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate static void 2527c478bd9Sstevel@tonic-gate insert_after(struct iodev_snapshot **list, struct iodev_snapshot *pos, 2537c478bd9Sstevel@tonic-gate struct iodev_snapshot *new) 2547c478bd9Sstevel@tonic-gate { 2557c478bd9Sstevel@tonic-gate if (pos == NULL) { 2567c478bd9Sstevel@tonic-gate new->is_prev = new->is_next = NULL; 2577c478bd9Sstevel@tonic-gate *list = new; 258*4be70790Swroche ix_new_list(new); 2597c478bd9Sstevel@tonic-gate return; 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate new->is_next = pos->is_next; 2637c478bd9Sstevel@tonic-gate new->is_prev = pos; 2647c478bd9Sstevel@tonic-gate if (pos->is_next) 2657c478bd9Sstevel@tonic-gate pos->is_next->is_prev = new; 2667c478bd9Sstevel@tonic-gate pos->is_next = new; 267*4be70790Swroche 268*4be70790Swroche ix_insert_here(pos, new, AVL_AFTER); 2697c478bd9Sstevel@tonic-gate } 2707c478bd9Sstevel@tonic-gate 2717c478bd9Sstevel@tonic-gate static void 2727c478bd9Sstevel@tonic-gate insert_into(struct iodev_snapshot **list, struct iodev_snapshot *iodev) 2737c478bd9Sstevel@tonic-gate { 2747c478bd9Sstevel@tonic-gate struct iodev_snapshot *tmp = *list; 275*4be70790Swroche avl_tree_t *l; 276*4be70790Swroche void *p; 277*4be70790Swroche avl_index_t where; 278*4be70790Swroche 2797c478bd9Sstevel@tonic-gate if (*list == NULL) { 2807c478bd9Sstevel@tonic-gate *list = iodev; 281*4be70790Swroche ix_new_list(iodev); 2827c478bd9Sstevel@tonic-gate return; 2837c478bd9Sstevel@tonic-gate } 2847c478bd9Sstevel@tonic-gate 285*4be70790Swroche /* 286*4be70790Swroche * Optimize the search: instead of walking the entire list 287*4be70790Swroche * (which can contain thousands of nodes), search in the AVL 288*4be70790Swroche * tree the nearest node and reposition the startup point to 289*4be70790Swroche * this node rather than always starting from the beginning 290*4be70790Swroche * of the list. 291*4be70790Swroche */ 292*4be70790Swroche l = tmp->avl_list; 293*4be70790Swroche if (l != NULL) { 294*4be70790Swroche p = avl_find(l, iodev, &where); 295*4be70790Swroche if (p == NULL) { 296*4be70790Swroche p = avl_nearest(l, where, AVL_BEFORE); 297*4be70790Swroche } 298*4be70790Swroche if (p != NULL) { 299*4be70790Swroche tmp = (struct iodev_snapshot *)p; 300*4be70790Swroche } 301*4be70790Swroche } 302*4be70790Swroche 3037c478bd9Sstevel@tonic-gate for (;;) { 3047c478bd9Sstevel@tonic-gate if (iodev_cmp(tmp, iodev) > 0) { 3057c478bd9Sstevel@tonic-gate insert_before(list, tmp, iodev); 3067c478bd9Sstevel@tonic-gate return; 3077c478bd9Sstevel@tonic-gate } 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate if (tmp->is_next == NULL) 3107c478bd9Sstevel@tonic-gate break; 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate tmp = tmp->is_next; 3137c478bd9Sstevel@tonic-gate } 3147c478bd9Sstevel@tonic-gate 3157c478bd9Sstevel@tonic-gate insert_after(list, tmp, iodev); 3167c478bd9Sstevel@tonic-gate } 3177c478bd9Sstevel@tonic-gate 3187c478bd9Sstevel@tonic-gate static int 3197c478bd9Sstevel@tonic-gate disk_or_partition(enum iodev_type type) 3207c478bd9Sstevel@tonic-gate { 3217c478bd9Sstevel@tonic-gate return (type == IODEV_DISK || type == IODEV_PARTITION); 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate 32437fbbce5Scth static int 32537fbbce5Scth disk_or_partition_or_iopath(enum iodev_type type) 32637fbbce5Scth { 32737fbbce5Scth return (type == IODEV_DISK || type == IODEV_PARTITION || 32837fbbce5Scth type == IODEV_IOPATH_LTI); 32937fbbce5Scth } 33037fbbce5Scth 3317c478bd9Sstevel@tonic-gate static void 3327c478bd9Sstevel@tonic-gate insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev) 3337c478bd9Sstevel@tonic-gate { 3347c478bd9Sstevel@tonic-gate struct iodev_snapshot *parent = find_parent(ss, iodev); 3357c478bd9Sstevel@tonic-gate struct iodev_snapshot **list; 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate if (parent != NULL) { 3387c478bd9Sstevel@tonic-gate list = &parent->is_children; 3397c478bd9Sstevel@tonic-gate parent->is_nr_children++; 3407c478bd9Sstevel@tonic-gate } else { 3417c478bd9Sstevel@tonic-gate list = &ss->s_iodevs; 3427c478bd9Sstevel@tonic-gate ss->s_nr_iodevs++; 3437c478bd9Sstevel@tonic-gate } 3447c478bd9Sstevel@tonic-gate 3457c478bd9Sstevel@tonic-gate insert_into(list, iodev); 3467c478bd9Sstevel@tonic-gate } 3477c478bd9Sstevel@tonic-gate 34837fbbce5Scth /* return 1 if dev passes filter */ 3497c478bd9Sstevel@tonic-gate static int 3507c478bd9Sstevel@tonic-gate iodev_match(struct iodev_snapshot *dev, struct iodev_filter *df) 3517c478bd9Sstevel@tonic-gate { 3527c478bd9Sstevel@tonic-gate int is_floppy = (strncmp(dev->is_name, "fd", 2) == 0); 35337fbbce5Scth char *isn, *ispn, *ifn; 35437fbbce5Scth char *path; 35537fbbce5Scth int ifnl; 35637fbbce5Scth size_t i; 3577c478bd9Sstevel@tonic-gate 3587c478bd9Sstevel@tonic-gate /* no filter, pass */ 3597c478bd9Sstevel@tonic-gate if (df == NULL) 36037fbbce5Scth return (1); /* pass */ 3617c478bd9Sstevel@tonic-gate 3627c478bd9Sstevel@tonic-gate /* no filtered names, pass if not floppy and skipped */ 3637c478bd9Sstevel@tonic-gate if (df->if_nr_names == NULL) 3647c478bd9Sstevel@tonic-gate return (!(df->if_skip_floppy && is_floppy)); 3657c478bd9Sstevel@tonic-gate 36637fbbce5Scth isn = dev->is_name; 36737fbbce5Scth ispn = dev->is_pretty; 3687c478bd9Sstevel@tonic-gate for (i = 0; i < df->if_nr_names; i++) { 36937fbbce5Scth ifn = df->if_names[i]; 37037fbbce5Scth ifnl = strlen(ifn); 37137fbbce5Scth path = strchr(ifn, '.'); 37237fbbce5Scth 37337fbbce5Scth if ((strcmp(isn, ifn) == 0) || 37437fbbce5Scth (ispn && (strcmp(ispn, ifn) == 0))) 37537fbbce5Scth return (1); /* pass */ 37637fbbce5Scth 37737fbbce5Scth /* if filter is a path allow partial match */ 37837fbbce5Scth if (path && 37937fbbce5Scth ((strncmp(isn, ifn, ifnl) == 0) || 38037fbbce5Scth (ispn && (strncmp(ispn, ifn, ifnl) == 0)))) 38137fbbce5Scth return (1); /* pass */ 3827c478bd9Sstevel@tonic-gate } 3837c478bd9Sstevel@tonic-gate 38437fbbce5Scth return (0); /* fail */ 38537fbbce5Scth } 38637fbbce5Scth 38737fbbce5Scth /* return 1 if path is an mpxio path associated with dev */ 38837fbbce5Scth static int 38937fbbce5Scth iodev_path_match(struct iodev_snapshot *dev, struct iodev_snapshot *path) 39037fbbce5Scth { 39137fbbce5Scth char *dn, *pn; 39237fbbce5Scth int dnl; 39337fbbce5Scth 39437fbbce5Scth dn = dev->is_name; 39537fbbce5Scth pn = path->is_name; 39637fbbce5Scth dnl = strlen(dn); 39737fbbce5Scth 39837fbbce5Scth if ((strncmp(pn, dn, dnl) == 0) && (pn[dnl] == '.')) 39937fbbce5Scth return (1); /* yes */ 40037fbbce5Scth 40137fbbce5Scth return (0); /* no */ 4027c478bd9Sstevel@tonic-gate } 4037c478bd9Sstevel@tonic-gate 4047c478bd9Sstevel@tonic-gate /* select which I/O devices to collect stats for */ 4057c478bd9Sstevel@tonic-gate static void 4067c478bd9Sstevel@tonic-gate choose_iodevs(struct snapshot *ss, struct iodev_snapshot *iodevs, 4077c478bd9Sstevel@tonic-gate struct iodev_filter *df) 4087c478bd9Sstevel@tonic-gate { 40937fbbce5Scth struct iodev_snapshot *pos, *ppos, *tmp, *ptmp; 41037fbbce5Scth int nr_iodevs; 41137fbbce5Scth int nr_iodevs_orig; 41237fbbce5Scth 41337fbbce5Scth nr_iodevs = df ? df->if_max_iodevs : UNLIMITED_IODEVS; 41437fbbce5Scth nr_iodevs_orig = nr_iodevs; 4157c478bd9Sstevel@tonic-gate 4167c478bd9Sstevel@tonic-gate if (nr_iodevs == UNLIMITED_IODEVS) 4177c478bd9Sstevel@tonic-gate nr_iodevs = INT_MAX; 4187c478bd9Sstevel@tonic-gate 41937fbbce5Scth /* add the full matches */ 42037fbbce5Scth pos = iodevs; 4217c478bd9Sstevel@tonic-gate while (pos && nr_iodevs) { 42237fbbce5Scth tmp = pos; 4237c478bd9Sstevel@tonic-gate pos = pos->is_next; 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate if (!iodev_match(tmp, df)) 42637fbbce5Scth continue; /* failed full match */ 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate list_del(&iodevs, tmp); 4297c478bd9Sstevel@tonic-gate insert_iodev(ss, tmp); 4307c478bd9Sstevel@tonic-gate 43137fbbce5Scth /* 43237fbbce5Scth * Add all mpxio paths associated with match above. Added 43337fbbce5Scth * paths don't count against nr_iodevs. 43437fbbce5Scth */ 43537fbbce5Scth if (strchr(tmp->is_name, '.') == NULL) { 43637fbbce5Scth ppos = iodevs; 43737fbbce5Scth while (ppos) { 43837fbbce5Scth ptmp = ppos; 43937fbbce5Scth ppos = ppos->is_next; 44037fbbce5Scth 44137fbbce5Scth if (!iodev_path_match(tmp, ptmp)) 44237fbbce5Scth continue; /* not an mpxio path */ 44337fbbce5Scth 44437fbbce5Scth list_del(&iodevs, ptmp); 44537fbbce5Scth insert_iodev(ss, ptmp); 44637fbbce5Scth if (pos == ptmp) 44737fbbce5Scth pos = ppos; 44837fbbce5Scth } 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate 45137fbbce5Scth nr_iodevs--; 45237fbbce5Scth } 4537c478bd9Sstevel@tonic-gate 45437fbbce5Scth /* 45537fbbce5Scth * If we had a filter, and *nothing* passed the filter then we 45637fbbce5Scth * don't want to fill the remaining slots - it is just confusing 45737fbbce5Scth * if we don that, it makes it look like the filter code is broken. 45837fbbce5Scth */ 45937fbbce5Scth if ((df->if_nr_names == NULL) || (nr_iodevs != nr_iodevs_orig)) { 4607c478bd9Sstevel@tonic-gate /* now insert any iodevs into the remaining slots */ 46137fbbce5Scth pos = iodevs; 4627c478bd9Sstevel@tonic-gate while (pos && nr_iodevs) { 46337fbbce5Scth tmp = pos; 4647c478bd9Sstevel@tonic-gate pos = pos->is_next; 4657c478bd9Sstevel@tonic-gate 4667c478bd9Sstevel@tonic-gate if (df && df->if_skip_floppy && 4677c478bd9Sstevel@tonic-gate strncmp(tmp->is_name, "fd", 2) == 0) 4687c478bd9Sstevel@tonic-gate continue; 4697c478bd9Sstevel@tonic-gate 4707c478bd9Sstevel@tonic-gate list_del(&iodevs, tmp); 4717c478bd9Sstevel@tonic-gate insert_iodev(ss, tmp); 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate --nr_iodevs; 4747c478bd9Sstevel@tonic-gate } 47537fbbce5Scth } 4767c478bd9Sstevel@tonic-gate 4777c478bd9Sstevel@tonic-gate /* clear the unwanted ones */ 4787c478bd9Sstevel@tonic-gate pos = iodevs; 4797c478bd9Sstevel@tonic-gate while (pos) { 4807c478bd9Sstevel@tonic-gate struct iodev_snapshot *tmp = pos; 4817c478bd9Sstevel@tonic-gate pos = pos->is_next; 4827c478bd9Sstevel@tonic-gate free_iodev(tmp); 4837c478bd9Sstevel@tonic-gate } 4847c478bd9Sstevel@tonic-gate } 4857c478bd9Sstevel@tonic-gate 4867c478bd9Sstevel@tonic-gate static int 4877c478bd9Sstevel@tonic-gate collate_controller(struct iodev_snapshot *controller, 4887c478bd9Sstevel@tonic-gate struct iodev_snapshot *disk) 4897c478bd9Sstevel@tonic-gate { 4907c478bd9Sstevel@tonic-gate controller->is_stats.nread += disk->is_stats.nread; 4917c478bd9Sstevel@tonic-gate controller->is_stats.nwritten += disk->is_stats.nwritten; 4927c478bd9Sstevel@tonic-gate controller->is_stats.reads += disk->is_stats.reads; 4937c478bd9Sstevel@tonic-gate controller->is_stats.writes += disk->is_stats.writes; 4947c478bd9Sstevel@tonic-gate controller->is_stats.wtime += disk->is_stats.wtime; 4957c478bd9Sstevel@tonic-gate controller->is_stats.wlentime += disk->is_stats.wlentime; 4967c478bd9Sstevel@tonic-gate controller->is_stats.rtime += disk->is_stats.rtime; 4977c478bd9Sstevel@tonic-gate controller->is_stats.rlentime += disk->is_stats.rlentime; 4987c478bd9Sstevel@tonic-gate controller->is_crtime += disk->is_crtime; 4997c478bd9Sstevel@tonic-gate controller->is_snaptime += disk->is_snaptime; 5007c478bd9Sstevel@tonic-gate if (kstat_add(&disk->is_errors, &controller->is_errors)) 5017c478bd9Sstevel@tonic-gate return (errno); 5027c478bd9Sstevel@tonic-gate return (0); 5037c478bd9Sstevel@tonic-gate } 5047c478bd9Sstevel@tonic-gate 5057c478bd9Sstevel@tonic-gate static int 5067c478bd9Sstevel@tonic-gate acquire_iodev_stats(struct iodev_snapshot *list, kstat_ctl_t *kc) 5077c478bd9Sstevel@tonic-gate { 5087c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos; 5097c478bd9Sstevel@tonic-gate int err = 0; 5107c478bd9Sstevel@tonic-gate 5117c478bd9Sstevel@tonic-gate for (pos = list; pos; pos = pos->is_next) { 5127c478bd9Sstevel@tonic-gate /* controllers don't have stats (yet) */ 5137c478bd9Sstevel@tonic-gate if (pos->is_ksp != NULL) { 5147c478bd9Sstevel@tonic-gate if (kstat_read(kc, pos->is_ksp, &pos->is_stats) == -1) 5157c478bd9Sstevel@tonic-gate return (errno); 5167c478bd9Sstevel@tonic-gate /* make sure crtime/snaptime is updated */ 5177c478bd9Sstevel@tonic-gate pos->is_crtime = pos->is_ksp->ks_crtime; 5187c478bd9Sstevel@tonic-gate pos->is_snaptime = pos->is_ksp->ks_snaptime; 5197c478bd9Sstevel@tonic-gate } 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate if ((err = acquire_iodev_stats(pos->is_children, kc))) 5227c478bd9Sstevel@tonic-gate return (err); 5237c478bd9Sstevel@tonic-gate 5247c478bd9Sstevel@tonic-gate if (pos->is_type == IODEV_CONTROLLER) { 5257c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos2 = pos->is_children; 5267c478bd9Sstevel@tonic-gate 5277c478bd9Sstevel@tonic-gate for (; pos2; pos2 = pos2->is_next) { 5287c478bd9Sstevel@tonic-gate if ((err = collate_controller(pos, pos2))) 5297c478bd9Sstevel@tonic-gate return (err); 5307c478bd9Sstevel@tonic-gate } 5317c478bd9Sstevel@tonic-gate } 5327c478bd9Sstevel@tonic-gate } 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate return (0); 5357c478bd9Sstevel@tonic-gate } 5367c478bd9Sstevel@tonic-gate 5377c478bd9Sstevel@tonic-gate static int 5387c478bd9Sstevel@tonic-gate acquire_iodev_errors(struct snapshot *ss, kstat_ctl_t *kc) 5397c478bd9Sstevel@tonic-gate { 5407c478bd9Sstevel@tonic-gate kstat_t *ksp; 5417c478bd9Sstevel@tonic-gate 5427c478bd9Sstevel@tonic-gate if (!(ss->s_types && SNAP_IODEV_ERRORS)) 5437c478bd9Sstevel@tonic-gate return (0); 5447c478bd9Sstevel@tonic-gate 5457c478bd9Sstevel@tonic-gate for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 5467c478bd9Sstevel@tonic-gate char kstat_name[KSTAT_STRLEN]; 5477c478bd9Sstevel@tonic-gate char *dname = kstat_name; 5487c478bd9Sstevel@tonic-gate char *ename = ksp->ks_name; 5497c478bd9Sstevel@tonic-gate struct iodev_snapshot *iodev; 5507c478bd9Sstevel@tonic-gate 5517c478bd9Sstevel@tonic-gate if (ksp->ks_type != KSTAT_TYPE_NAMED) 5527c478bd9Sstevel@tonic-gate continue; 5537c478bd9Sstevel@tonic-gate if (strncmp(ksp->ks_class, "device_error", 12) != 0 && 5547c478bd9Sstevel@tonic-gate strncmp(ksp->ks_class, "iopath_error", 12) != 0) 5557c478bd9Sstevel@tonic-gate continue; 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate /* 5587c478bd9Sstevel@tonic-gate * Some drivers may not follow the naming convention 5597c478bd9Sstevel@tonic-gate * for error kstats (i.e., drivername,err) so 5607c478bd9Sstevel@tonic-gate * be sure we don't walk off the end. 5617c478bd9Sstevel@tonic-gate */ 5627c478bd9Sstevel@tonic-gate while (*ename && *ename != ',') { 5637c478bd9Sstevel@tonic-gate *dname = *ename; 5647c478bd9Sstevel@tonic-gate dname++; 5657c478bd9Sstevel@tonic-gate ename++; 5667c478bd9Sstevel@tonic-gate } 5677c478bd9Sstevel@tonic-gate *dname = '\0'; 5687c478bd9Sstevel@tonic-gate 5697c478bd9Sstevel@tonic-gate iodev = find_iodev_by_name(ss->s_iodevs, kstat_name); 5707c478bd9Sstevel@tonic-gate 5717c478bd9Sstevel@tonic-gate if (iodev == NULL) 5727c478bd9Sstevel@tonic-gate continue; 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate if (kstat_read(kc, ksp, NULL) == -1) 5757c478bd9Sstevel@tonic-gate return (errno); 5767c478bd9Sstevel@tonic-gate if (kstat_copy(ksp, &iodev->is_errors) == -1) 5777c478bd9Sstevel@tonic-gate return (errno); 5787c478bd9Sstevel@tonic-gate } 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate return (0); 5817c478bd9Sstevel@tonic-gate } 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate static void 5847c478bd9Sstevel@tonic-gate get_ids(struct iodev_snapshot *iodev, const char *pretty) 5857c478bd9Sstevel@tonic-gate { 5867c478bd9Sstevel@tonic-gate int ctr, disk, slice, ret; 5877c478bd9Sstevel@tonic-gate char *target; 5887c478bd9Sstevel@tonic-gate const char *p1; 5897c478bd9Sstevel@tonic-gate const char *p2; 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate if (pretty == NULL) 5927c478bd9Sstevel@tonic-gate return; 5937c478bd9Sstevel@tonic-gate 5947c478bd9Sstevel@tonic-gate if (sscanf(pretty, "c%d", &ctr) != 1) 5957c478bd9Sstevel@tonic-gate return; 5967c478bd9Sstevel@tonic-gate 5977c478bd9Sstevel@tonic-gate p1 = pretty; 5987c478bd9Sstevel@tonic-gate while (*p1 && *p1 != 't') 5997c478bd9Sstevel@tonic-gate ++p1; 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate if (!*p1) 6027c478bd9Sstevel@tonic-gate return; 6037c478bd9Sstevel@tonic-gate ++p1; 6047c478bd9Sstevel@tonic-gate 6057c478bd9Sstevel@tonic-gate p2 = p1; 6067c478bd9Sstevel@tonic-gate while (*p2 && *p2 != 'd') 6077c478bd9Sstevel@tonic-gate ++p2; 6087c478bd9Sstevel@tonic-gate 6097c478bd9Sstevel@tonic-gate if (!*p2 || p2 == p1) 6107c478bd9Sstevel@tonic-gate return; 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate target = safe_alloc(1 + p2 - p1); 6137c478bd9Sstevel@tonic-gate (void) strlcpy(target, p1, 1 + p2 - p1); 6147c478bd9Sstevel@tonic-gate 6157c478bd9Sstevel@tonic-gate ret = sscanf(p2, "d%d%*[sp]%d", &disk, &slice); 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate if (ret == 2 && iodev->is_type == IODEV_PARTITION) { 6187c478bd9Sstevel@tonic-gate iodev->is_id.id = slice; 6197c478bd9Sstevel@tonic-gate iodev->is_parent_id.id = disk; 6207c478bd9Sstevel@tonic-gate (void) strlcpy(iodev->is_parent_id.tid, target, KSTAT_STRLEN); 6217c478bd9Sstevel@tonic-gate } else if (ret == 1) { 6227c478bd9Sstevel@tonic-gate if (iodev->is_type == IODEV_DISK) { 6237c478bd9Sstevel@tonic-gate iodev->is_id.id = disk; 6247c478bd9Sstevel@tonic-gate (void) strlcpy(iodev->is_id.tid, target, KSTAT_STRLEN); 6257c478bd9Sstevel@tonic-gate iodev->is_parent_id.id = ctr; 62637fbbce5Scth } else if (iodev->is_type == IODEV_IOPATH_LTI) { 6277c478bd9Sstevel@tonic-gate iodev->is_parent_id.id = disk; 6287c478bd9Sstevel@tonic-gate (void) strlcpy(iodev->is_parent_id.tid, 6297c478bd9Sstevel@tonic-gate target, KSTAT_STRLEN); 6307c478bd9Sstevel@tonic-gate } 6317c478bd9Sstevel@tonic-gate } 6327c478bd9Sstevel@tonic-gate 6337c478bd9Sstevel@tonic-gate free(target); 6347c478bd9Sstevel@tonic-gate } 6357c478bd9Sstevel@tonic-gate 6367c478bd9Sstevel@tonic-gate static void 6377c478bd9Sstevel@tonic-gate get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev, 6387c478bd9Sstevel@tonic-gate kstat_ctl_t *kc) 6397c478bd9Sstevel@tonic-gate { 6407c478bd9Sstevel@tonic-gate disk_list_t *dl; 6417c478bd9Sstevel@tonic-gate char *pretty = NULL; 6427c478bd9Sstevel@tonic-gate 6437c478bd9Sstevel@tonic-gate if (iodev->is_type == IODEV_NFS) { 6447c478bd9Sstevel@tonic-gate if (!(types & SNAP_IODEV_PRETTY)) 6457c478bd9Sstevel@tonic-gate return; 6467c478bd9Sstevel@tonic-gate 6477c478bd9Sstevel@tonic-gate iodev->is_pretty = lookup_nfs_name(iodev->is_name, kc); 6487c478bd9Sstevel@tonic-gate return; 6497c478bd9Sstevel@tonic-gate } 6507c478bd9Sstevel@tonic-gate 65137fbbce5Scth /* lookup/translate the kstat name */ 652a08731ecScth dl = lookup_ks_name(iodev->is_name, (types & SNAP_IODEV_DEVID) ? 1 : 0); 6537c478bd9Sstevel@tonic-gate if (dl == NULL) 6547c478bd9Sstevel@tonic-gate return; 6557c478bd9Sstevel@tonic-gate 6567c478bd9Sstevel@tonic-gate if (dl->dsk) 6577c478bd9Sstevel@tonic-gate pretty = safe_strdup(dl->dsk); 6587c478bd9Sstevel@tonic-gate 6597c478bd9Sstevel@tonic-gate if (types & SNAP_IODEV_PRETTY) { 6607c478bd9Sstevel@tonic-gate if (dl->dname) 6617c478bd9Sstevel@tonic-gate iodev->is_dname = safe_strdup(dl->dname); 6627c478bd9Sstevel@tonic-gate } 6637c478bd9Sstevel@tonic-gate 6647c478bd9Sstevel@tonic-gate if (dl->devidstr) 6657c478bd9Sstevel@tonic-gate iodev->is_devid = safe_strdup(dl->devidstr); 6667c478bd9Sstevel@tonic-gate 6677c478bd9Sstevel@tonic-gate get_ids(iodev, pretty); 6687c478bd9Sstevel@tonic-gate 66937fbbce5Scth /* 67037fbbce5Scth * we fill in pretty name wether it is asked for or not because 67137fbbce5Scth * it could be used in a filter by match_iodevs. 67237fbbce5Scth */ 6737c478bd9Sstevel@tonic-gate iodev->is_pretty = pretty; 6747c478bd9Sstevel@tonic-gate } 6757c478bd9Sstevel@tonic-gate 6767c478bd9Sstevel@tonic-gate static enum iodev_type 6777c478bd9Sstevel@tonic-gate get_iodev_type(kstat_t *ksp) 6787c478bd9Sstevel@tonic-gate { 6797c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "disk") == 0) 6807c478bd9Sstevel@tonic-gate return (IODEV_DISK); 6817c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "partition") == 0) 6827c478bd9Sstevel@tonic-gate return (IODEV_PARTITION); 6837c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "nfs") == 0) 6847c478bd9Sstevel@tonic-gate return (IODEV_NFS); 6857c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "iopath") == 0) 68637fbbce5Scth return (IODEV_IOPATH_LTI); 6877c478bd9Sstevel@tonic-gate if (strcmp(ksp->ks_class, "tape") == 0) 6887c478bd9Sstevel@tonic-gate return (IODEV_TAPE); 6897c478bd9Sstevel@tonic-gate return (IODEV_UNKNOWN); 6907c478bd9Sstevel@tonic-gate } 6917c478bd9Sstevel@tonic-gate 69237fbbce5Scth /* get the lun/target/initiator from the name, return 1 on success */ 69337fbbce5Scth static int 69437fbbce5Scth get_lti(char *s, 69537fbbce5Scth char *lname, int *l, char *tname, int *t, char *iname, int *i) 69637fbbce5Scth { 69737fbbce5Scth int num = 0; 69837fbbce5Scth 69937fbbce5Scth num = sscanf(s, "%[a-z]%d%*[.]%[a-z]%d%*[.]%[a-z]%d", lname, l, 70037fbbce5Scth tname, t, iname, i); 70137fbbce5Scth return ((num == 6) ? 1 : 0); 70237fbbce5Scth } 70337fbbce5Scth 70437fbbce5Scth 70537fbbce5Scth /* get the lun, target, and initiator name and instance */ 70637fbbce5Scth static void 70737fbbce5Scth get_path_info(struct iodev_snapshot *io, char *mod, int *type, int *inst, 70837fbbce5Scth char *name, size_t size) 70937fbbce5Scth { 71037fbbce5Scth 71137fbbce5Scth /* 71237fbbce5Scth * If it is iopath or ssd then pad the name with i/t/l so we can sort 71337fbbce5Scth * by alpha order and set type for IOPATH to DISK since we want to 71437fbbce5Scth * have it grouped with its ssd parent. The lun can be 5 digits, 71537fbbce5Scth * the target can be 4 digits, and the initiator can be 3 digits and 71637fbbce5Scth * the padding is done appropriately for string comparisons. 71737fbbce5Scth */ 71837fbbce5Scth if (disk_or_partition_or_iopath(io->is_type)) { 71937fbbce5Scth int i1, t1, l1; 72037fbbce5Scth char tname[KSTAT_STRLEN], iname[KSTAT_STRLEN]; 72137fbbce5Scth char *ptr, lname[KSTAT_STRLEN]; 72237fbbce5Scth 72337fbbce5Scth i1 = t1 = l1 = 0; 72437fbbce5Scth (void) get_lti(io->is_name, lname, &l1, tname, &t1, iname, &i1); 72537fbbce5Scth *type = io->is_type; 72637fbbce5Scth if (io->is_type == IODEV_DISK) { 72737fbbce5Scth (void) snprintf(name, size, "%s%05d", lname, l1); 72837fbbce5Scth } else if (io->is_type == IODEV_PARTITION) { 72937fbbce5Scth ptr = strchr(io->is_name, ','); 73037fbbce5Scth (void) snprintf(name, size, "%s%05d%s", lname, l1, ptr); 73137fbbce5Scth } else { 73237fbbce5Scth (void) snprintf(name, size, "%s%05d.%s%04d.%s%03d", 73337fbbce5Scth lname, l1, tname, t1, iname, i1); 73437fbbce5Scth /* set to disk so we sort with disks */ 73537fbbce5Scth *type = IODEV_DISK; 73637fbbce5Scth } 73737fbbce5Scth (void) strcpy(mod, lname); 73837fbbce5Scth *inst = l1; 73937fbbce5Scth } else { 74037fbbce5Scth (void) strcpy(mod, io->is_module); 74137fbbce5Scth (void) strcpy(name, io->is_name); 74237fbbce5Scth *type = io->is_type; 74337fbbce5Scth *inst = io->is_instance; 74437fbbce5Scth } 74537fbbce5Scth } 74637fbbce5Scth 7477c478bd9Sstevel@tonic-gate int 7487c478bd9Sstevel@tonic-gate iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2) 7497c478bd9Sstevel@tonic-gate { 75037fbbce5Scth int type1, type2; 75137fbbce5Scth int inst1, inst2; 75237fbbce5Scth char name1[KSTAT_STRLEN], name2[KSTAT_STRLEN]; 75337fbbce5Scth char mod1[KSTAT_STRLEN], mod2[KSTAT_STRLEN]; 75437fbbce5Scth 75537fbbce5Scth get_path_info(io1, mod1, &type1, &inst1, name1, sizeof (name1)); 75637fbbce5Scth get_path_info(io2, mod2, &type2, &inst2, name2, sizeof (name2)); 75737fbbce5Scth if ((!disk_or_partition(type1)) || 75837fbbce5Scth (!disk_or_partition(type2))) { 7597c478bd9Sstevel@tonic-gate /* neutral sort order between disk and part */ 76037fbbce5Scth if (type1 < type2) { 7617c478bd9Sstevel@tonic-gate return (-1); 76237fbbce5Scth } 76337fbbce5Scth if (type1 > type2) { 7647c478bd9Sstevel@tonic-gate return (1); 7657c478bd9Sstevel@tonic-gate } 76637fbbce5Scth } 7677c478bd9Sstevel@tonic-gate 7687c478bd9Sstevel@tonic-gate /* controller doesn't have ksp */ 7697c478bd9Sstevel@tonic-gate if (io1->is_ksp && io2->is_ksp) { 77037fbbce5Scth if (strcmp(mod1, mod2) != 0) { 77137fbbce5Scth return (strcmp(mod1, mod2)); 77237fbbce5Scth } 77337fbbce5Scth if (inst1 < inst2) { 7747c478bd9Sstevel@tonic-gate return (-1); 77537fbbce5Scth } 77637fbbce5Scth if (inst1 > inst2) { 7777c478bd9Sstevel@tonic-gate return (1); 77837fbbce5Scth } 7797c478bd9Sstevel@tonic-gate } else { 78037fbbce5Scth if (io1->is_id.id < io2->is_id.id) { 7817c478bd9Sstevel@tonic-gate return (-1); 78237fbbce5Scth } 78337fbbce5Scth if (io1->is_id.id > io2->is_id.id) { 78437fbbce5Scth return (1); 78537fbbce5Scth } 78637fbbce5Scth } 78737fbbce5Scth 78837fbbce5Scth return (strcmp(name1, name2)); 78937fbbce5Scth } 79037fbbce5Scth 79137fbbce5Scth /* update the target reads and writes */ 79237fbbce5Scth static void 79337fbbce5Scth update_target(struct iodev_snapshot *tgt, struct iodev_snapshot *path) 79437fbbce5Scth { 79537fbbce5Scth tgt->is_stats.reads += path->is_stats.reads; 79637fbbce5Scth tgt->is_stats.writes += path->is_stats.writes; 79737fbbce5Scth tgt->is_stats.nread += path->is_stats.nread; 79837fbbce5Scth tgt->is_stats.nwritten += path->is_stats.nwritten; 79937fbbce5Scth tgt->is_stats.wcnt += path->is_stats.wcnt; 80037fbbce5Scth tgt->is_stats.rcnt += path->is_stats.rcnt; 80137fbbce5Scth 80237fbbce5Scth /* 80337fbbce5Scth * Stash the t_delta in the crtime for use in show_disk 80437fbbce5Scth * NOTE: this can't be done in show_disk because the 80537fbbce5Scth * itl entry is removed for the old format 80637fbbce5Scth */ 80737fbbce5Scth tgt->is_crtime += hrtime_delta(path->is_crtime, path->is_snaptime); 80837fbbce5Scth tgt->is_snaptime += path->is_snaptime; 80937fbbce5Scth tgt->is_nr_children += 1; 81037fbbce5Scth } 81137fbbce5Scth 81237fbbce5Scth /* 81337fbbce5Scth * Create a new synthetic device entry of the specified type. The supported 81437fbbce5Scth * synthetic types are IODEV_IOPATH_LT and IODEV_IOPATH_LI. 81537fbbce5Scth */ 81637fbbce5Scth static struct iodev_snapshot * 81737fbbce5Scth make_extended_device(int type, struct iodev_snapshot *old) 81837fbbce5Scth { 81937fbbce5Scth struct iodev_snapshot *tptr = NULL; 82037fbbce5Scth char *ptr; 82137fbbce5Scth int lun, tgt, initiator; 82237fbbce5Scth char lun_name[KSTAT_STRLEN]; 82337fbbce5Scth char tgt_name[KSTAT_STRLEN]; 82437fbbce5Scth char initiator_name[KSTAT_STRLEN]; 82537fbbce5Scth 82637fbbce5Scth if (old == NULL) 82737fbbce5Scth return (NULL); 82837fbbce5Scth if (get_lti(old->is_name, 82937fbbce5Scth lun_name, &lun, tgt_name, &tgt, initiator_name, &initiator) != 1) { 83037fbbce5Scth return (NULL); 83137fbbce5Scth } 83237fbbce5Scth tptr = safe_alloc(sizeof (*old)); 83337fbbce5Scth bzero(tptr, sizeof (*old)); 83437fbbce5Scth if (old->is_pretty != NULL) { 83537fbbce5Scth tptr->is_pretty = safe_alloc(strlen(old->is_pretty) + 1); 83637fbbce5Scth (void) strcpy(tptr->is_pretty, old->is_pretty); 83737fbbce5Scth } 83837fbbce5Scth bcopy(&old->is_parent_id, &tptr->is_parent_id, 83937fbbce5Scth sizeof (old->is_parent_id)); 84037fbbce5Scth 84137fbbce5Scth tptr->is_type = type; 84237fbbce5Scth 84337fbbce5Scth if (type == IODEV_IOPATH_LT) { 84437fbbce5Scth /* make new synthetic entry that is the LT */ 84537fbbce5Scth /* set the id to the target id */ 84637fbbce5Scth tptr->is_id.id = tgt; 84737fbbce5Scth (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid), 84837fbbce5Scth "%s%d", tgt_name, tgt); 84937fbbce5Scth (void) snprintf(tptr->is_name, sizeof (tptr->is_name), 85037fbbce5Scth "%s%d.%s%d", lun_name, lun, tgt_name, tgt); 85137fbbce5Scth 85237fbbce5Scth if (old->is_pretty) { 85337fbbce5Scth ptr = strrchr(tptr->is_pretty, '.'); 85437fbbce5Scth if (ptr) 85537fbbce5Scth *ptr = '\0'; 85637fbbce5Scth } 85737fbbce5Scth } else if (type == IODEV_IOPATH_LI) { 85837fbbce5Scth /* make new synthetic entry that is the LI */ 85937fbbce5Scth /* set the id to the initiator number */ 86037fbbce5Scth tptr->is_id.id = initiator; 86137fbbce5Scth (void) snprintf(tptr->is_id.tid, sizeof (tptr->is_id.tid), 86237fbbce5Scth "%s%d", initiator_name, initiator); 86337fbbce5Scth (void) snprintf(tptr->is_name, sizeof (tptr->is_name), 86437fbbce5Scth "%s%d.%s%d", lun_name, lun, initiator_name, initiator); 86537fbbce5Scth 86637fbbce5Scth if (old->is_pretty) { 86737fbbce5Scth ptr = strchr(tptr->is_pretty, '.'); 86837fbbce5Scth if (ptr) 86937fbbce5Scth (void) snprintf(ptr + 1, 87037fbbce5Scth strlen(tptr->is_pretty) + 1, 87137fbbce5Scth "%s%d", initiator_name, initiator); 87237fbbce5Scth } 87337fbbce5Scth } 87437fbbce5Scth return (tptr); 87537fbbce5Scth } 87637fbbce5Scth 87737fbbce5Scth /* 87837fbbce5Scth * This is to get the original -X LI format (e.g. ssd1.fp0). When an LTI kstat 87937fbbce5Scth * is found - traverse the children looking for the same initiator and sum 88037fbbce5Scth * them up. Add an LI entry and delete all of the LTI entries with the same 88137fbbce5Scth * initiator. 88237fbbce5Scth */ 88337fbbce5Scth static int 88437fbbce5Scth create_li_delete_lti(struct snapshot *ss, struct iodev_snapshot *list) 88537fbbce5Scth { 88637fbbce5Scth struct iodev_snapshot *pos, *entry, *parent; 88737fbbce5Scth int lun, tgt, initiator; 88837fbbce5Scth char lun_name[KSTAT_STRLEN]; 88937fbbce5Scth char tgt_name[KSTAT_STRLEN]; 89037fbbce5Scth char initiator_name[KSTAT_STRLEN]; 89137fbbce5Scth int err; 89237fbbce5Scth 89337fbbce5Scth for (entry = list; entry; entry = entry->is_next) { 89437fbbce5Scth if ((err = create_li_delete_lti(ss, entry->is_children)) != 0) 89537fbbce5Scth return (err); 89637fbbce5Scth 89737fbbce5Scth if (entry->is_type == IODEV_IOPATH_LTI) { 89837fbbce5Scth parent = find_parent(ss, entry); 89937fbbce5Scth if (get_lti(entry->is_name, lun_name, &lun, 90037fbbce5Scth tgt_name, &tgt, initiator_name, &initiator) != 1) { 9017c478bd9Sstevel@tonic-gate return (1); 9027c478bd9Sstevel@tonic-gate } 9037c478bd9Sstevel@tonic-gate 90437fbbce5Scth pos = (parent == NULL) ? NULL : parent->is_children; 90537fbbce5Scth for (; pos; pos = pos->is_next) { 90637fbbce5Scth if (pos->is_id.id != -1 && 90737fbbce5Scth pos->is_id.id == initiator && 90837fbbce5Scth pos->is_type == IODEV_IOPATH_LI) { 90937fbbce5Scth /* found the same initiator */ 91037fbbce5Scth update_target(pos, entry); 91137fbbce5Scth list_del(&parent->is_children, entry); 91237fbbce5Scth free_iodev(entry); 91337fbbce5Scth parent->is_nr_children--; 91437fbbce5Scth entry = pos; 91537fbbce5Scth break; 91637fbbce5Scth } 91737fbbce5Scth } 91837fbbce5Scth 91937fbbce5Scth if (!pos) { 92037fbbce5Scth /* make the first LI entry */ 92137fbbce5Scth pos = make_extended_device( 92237fbbce5Scth IODEV_IOPATH_LI, entry); 92337fbbce5Scth update_target(pos, entry); 92437fbbce5Scth 92537fbbce5Scth if (parent) { 92637fbbce5Scth insert_before(&parent->is_children, 92737fbbce5Scth entry, pos); 92837fbbce5Scth list_del(&parent->is_children, entry); 92937fbbce5Scth free_iodev(entry); 93037fbbce5Scth } else { 93137fbbce5Scth insert_before(&ss->s_iodevs, entry, 93237fbbce5Scth pos); 93337fbbce5Scth list_del(&ss->s_iodevs, entry); 93437fbbce5Scth free_iodev(entry); 93537fbbce5Scth } 93637fbbce5Scth entry = pos; 93737fbbce5Scth } 93837fbbce5Scth } 93937fbbce5Scth } 94037fbbce5Scth return (0); 94137fbbce5Scth } 94237fbbce5Scth 94337fbbce5Scth /* 94437fbbce5Scth * We have the LTI kstat, now add an entry for the LT that sums up all of 94537fbbce5Scth * the LTI's with the same target(t). 94637fbbce5Scth */ 94737fbbce5Scth static int 94837fbbce5Scth create_lt(struct snapshot *ss, struct iodev_snapshot *list) 94937fbbce5Scth { 95037fbbce5Scth struct iodev_snapshot *entry, *parent, *pos; 95137fbbce5Scth int lun, tgt, initiator; 95237fbbce5Scth char lun_name[KSTAT_STRLEN]; 95337fbbce5Scth char tgt_name[KSTAT_STRLEN]; 95437fbbce5Scth char initiator_name[KSTAT_STRLEN]; 95537fbbce5Scth int err; 95637fbbce5Scth 95737fbbce5Scth for (entry = list; entry; entry = entry->is_next) { 95837fbbce5Scth if ((err = create_lt(ss, entry->is_children)) != 0) 95937fbbce5Scth return (err); 96037fbbce5Scth 96137fbbce5Scth if (entry->is_type == IODEV_IOPATH_LTI) { 96237fbbce5Scth parent = find_parent(ss, entry); 96337fbbce5Scth if (get_lti(entry->is_name, lun_name, &lun, 96437fbbce5Scth tgt_name, &tgt, initiator_name, &initiator) != 1) { 96537fbbce5Scth return (1); 96637fbbce5Scth } 96737fbbce5Scth 96837fbbce5Scth pos = (parent == NULL) ? NULL : parent->is_children; 96937fbbce5Scth for (; pos; pos = pos->is_next) { 97037fbbce5Scth if (pos->is_id.id != -1 && 97137fbbce5Scth pos->is_id.id == tgt && 97237fbbce5Scth pos->is_type == IODEV_IOPATH_LT) { 97337fbbce5Scth /* found the same target */ 97437fbbce5Scth update_target(pos, entry); 97537fbbce5Scth break; 97637fbbce5Scth } 97737fbbce5Scth } 97837fbbce5Scth 97937fbbce5Scth if (!pos) { 98037fbbce5Scth pos = make_extended_device( 98137fbbce5Scth IODEV_IOPATH_LT, entry); 98237fbbce5Scth update_target(pos, entry); 98337fbbce5Scth 98437fbbce5Scth if (parent) { 98537fbbce5Scth insert_before(&parent->is_children, 98637fbbce5Scth entry, pos); 98737fbbce5Scth parent->is_nr_children++; 98837fbbce5Scth } else { 98937fbbce5Scth insert_before(&ss->s_iodevs, 99037fbbce5Scth entry, pos); 99137fbbce5Scth } 99237fbbce5Scth } 99337fbbce5Scth } 99437fbbce5Scth } 99537fbbce5Scth return (0); 99637fbbce5Scth } 99737fbbce5Scth 99837fbbce5Scth /* Find the longest is_name field to aid formatting of output */ 99937fbbce5Scth static int 100037fbbce5Scth iodevs_is_name_maxlen(struct iodev_snapshot *list) 100137fbbce5Scth { 100237fbbce5Scth struct iodev_snapshot *entry; 100337fbbce5Scth int max = 0, cmax, len; 100437fbbce5Scth 100537fbbce5Scth for (entry = list; entry; entry = entry->is_next) { 100637fbbce5Scth cmax = iodevs_is_name_maxlen(entry->is_children); 100737fbbce5Scth max = (cmax > max) ? cmax : max; 100837fbbce5Scth len = strlen(entry->is_name); 100937fbbce5Scth max = (len > max) ? len : max; 101037fbbce5Scth } 101137fbbce5Scth return (max); 10127c478bd9Sstevel@tonic-gate } 10137c478bd9Sstevel@tonic-gate 10147c478bd9Sstevel@tonic-gate int 10157c478bd9Sstevel@tonic-gate acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df) 10167c478bd9Sstevel@tonic-gate { 10177c478bd9Sstevel@tonic-gate kstat_t *ksp; 10187c478bd9Sstevel@tonic-gate struct iodev_snapshot *pos; 10197c478bd9Sstevel@tonic-gate struct iodev_snapshot *list = NULL; 102037fbbce5Scth int err = 0; 10217c478bd9Sstevel@tonic-gate 10227c478bd9Sstevel@tonic-gate ss->s_nr_iodevs = 0; 102337fbbce5Scth ss->s_iodevs_is_name_maxlen = 0; 10247c478bd9Sstevel@tonic-gate 1025a08731ecScth /* 1026a08731ecScth * Call cleanup_iodevs_snapshot() so that a cache miss in 1027a08731ecScth * lookup_ks_name() will result in a fresh snapshot. 1028a08731ecScth */ 1029a08731ecScth cleanup_iodevs_snapshot(); 1030a08731ecScth 10317c478bd9Sstevel@tonic-gate for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) { 10327c478bd9Sstevel@tonic-gate enum iodev_type type; 10337c478bd9Sstevel@tonic-gate 10347c478bd9Sstevel@tonic-gate if (ksp->ks_type != KSTAT_TYPE_IO) 10357c478bd9Sstevel@tonic-gate continue; 10367c478bd9Sstevel@tonic-gate 10377c478bd9Sstevel@tonic-gate /* e.g. "usb_byte_count" is not handled */ 10387c478bd9Sstevel@tonic-gate if ((type = get_iodev_type(ksp)) == IODEV_UNKNOWN) 10397c478bd9Sstevel@tonic-gate continue; 10407c478bd9Sstevel@tonic-gate 10417c478bd9Sstevel@tonic-gate if (df && !(type & df->if_allowed_types)) 10427c478bd9Sstevel@tonic-gate continue; 10437c478bd9Sstevel@tonic-gate 10447c478bd9Sstevel@tonic-gate if ((pos = malloc(sizeof (struct iodev_snapshot))) == NULL) { 10457c478bd9Sstevel@tonic-gate err = errno; 10467c478bd9Sstevel@tonic-gate goto out; 10477c478bd9Sstevel@tonic-gate } 10487c478bd9Sstevel@tonic-gate 10497c478bd9Sstevel@tonic-gate (void) memset(pos, 0, sizeof (struct iodev_snapshot)); 10507c478bd9Sstevel@tonic-gate 10517c478bd9Sstevel@tonic-gate pos->is_type = type; 10527c478bd9Sstevel@tonic-gate pos->is_crtime = ksp->ks_crtime; 10537c478bd9Sstevel@tonic-gate pos->is_snaptime = ksp->ks_snaptime; 10547c478bd9Sstevel@tonic-gate pos->is_id.id = IODEV_NO_ID; 10557c478bd9Sstevel@tonic-gate pos->is_parent_id.id = IODEV_NO_ID; 10567c478bd9Sstevel@tonic-gate pos->is_ksp = ksp; 10577c478bd9Sstevel@tonic-gate pos->is_instance = ksp->ks_instance; 10587c478bd9Sstevel@tonic-gate 10597c478bd9Sstevel@tonic-gate (void) strlcpy(pos->is_module, ksp->ks_module, KSTAT_STRLEN); 10607c478bd9Sstevel@tonic-gate (void) strlcpy(pos->is_name, ksp->ks_name, KSTAT_STRLEN); 10617c478bd9Sstevel@tonic-gate get_pretty_name(ss->s_types, pos, kc); 10627c478bd9Sstevel@tonic-gate 10637c478bd9Sstevel@tonic-gate /* 10647c478bd9Sstevel@tonic-gate * We must insert in sort order so e.g. vmstat -l 10657c478bd9Sstevel@tonic-gate * chooses in order. 10667c478bd9Sstevel@tonic-gate */ 10677c478bd9Sstevel@tonic-gate insert_into(&list, pos); 10687c478bd9Sstevel@tonic-gate } 10697c478bd9Sstevel@tonic-gate 10707c478bd9Sstevel@tonic-gate choose_iodevs(ss, list, df); 10717c478bd9Sstevel@tonic-gate 10727c478bd9Sstevel@tonic-gate /* before acquire_stats for collate_controller()'s benefit */ 10737c478bd9Sstevel@tonic-gate if (ss->s_types & SNAP_IODEV_ERRORS) { 10747c478bd9Sstevel@tonic-gate if ((err = acquire_iodev_errors(ss, kc)) != 0) 10757c478bd9Sstevel@tonic-gate goto out; 10767c478bd9Sstevel@tonic-gate } 10777c478bd9Sstevel@tonic-gate 10787c478bd9Sstevel@tonic-gate if ((err = acquire_iodev_stats(ss->s_iodevs, kc)) != 0) 10797c478bd9Sstevel@tonic-gate goto out; 10807c478bd9Sstevel@tonic-gate 108137fbbce5Scth if (ss->s_types & SNAP_IOPATHS_LTI) { 108237fbbce5Scth /* 108337fbbce5Scth * -Y: kstats are LTI, need to create a synthetic LT 108437fbbce5Scth * for -Y output. 108537fbbce5Scth */ 108637fbbce5Scth if ((err = create_lt(ss, ss->s_iodevs)) != 0) { 108737fbbce5Scth return (err); 108837fbbce5Scth } 108937fbbce5Scth } 109037fbbce5Scth if (ss->s_types & SNAP_IOPATHS_LI) { 109137fbbce5Scth /* 109237fbbce5Scth * -X: kstats are LTI, need to create a synthetic LI and 109337fbbce5Scth * delete the LTI for -X output 109437fbbce5Scth */ 109537fbbce5Scth if ((err = create_li_delete_lti(ss, ss->s_iodevs)) != 0) { 109637fbbce5Scth return (err); 109737fbbce5Scth } 109837fbbce5Scth } 109937fbbce5Scth 110037fbbce5Scth /* determine width of longest is_name */ 110137fbbce5Scth ss->s_iodevs_is_name_maxlen = iodevs_is_name_maxlen(ss->s_iodevs); 110237fbbce5Scth 11037c478bd9Sstevel@tonic-gate err = 0; 11047c478bd9Sstevel@tonic-gate out: 11057c478bd9Sstevel@tonic-gate return (err); 11067c478bd9Sstevel@tonic-gate } 11077c478bd9Sstevel@tonic-gate 11087c478bd9Sstevel@tonic-gate void 11097c478bd9Sstevel@tonic-gate free_iodev(struct iodev_snapshot *iodev) 11107c478bd9Sstevel@tonic-gate { 11117c478bd9Sstevel@tonic-gate while (iodev->is_children) { 11127c478bd9Sstevel@tonic-gate struct iodev_snapshot *tmp = iodev->is_children; 11137c478bd9Sstevel@tonic-gate iodev->is_children = iodev->is_children->is_next; 11147c478bd9Sstevel@tonic-gate free_iodev(tmp); 11157c478bd9Sstevel@tonic-gate } 11167c478bd9Sstevel@tonic-gate 1117*4be70790Swroche if (iodev->avl_list) { 1118*4be70790Swroche avl_remove(iodev->avl_list, iodev); 1119*4be70790Swroche if (avl_numnodes(iodev->avl_list) == 0) { 1120*4be70790Swroche avl_destroy(iodev->avl_list); 1121*4be70790Swroche free(iodev->avl_list); 1122*4be70790Swroche } 1123*4be70790Swroche } 1124*4be70790Swroche 11257c478bd9Sstevel@tonic-gate free(iodev->is_errors.ks_data); 11267c478bd9Sstevel@tonic-gate free(iodev->is_pretty); 11277c478bd9Sstevel@tonic-gate free(iodev->is_dname); 11287c478bd9Sstevel@tonic-gate free(iodev->is_devid); 11297c478bd9Sstevel@tonic-gate free(iodev); 11307c478bd9Sstevel@tonic-gate } 1131