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> 325c5f1371SRichard 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 * 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 * 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 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 917c478bd9Sstevel@tonic-gate id_match(struct iodev_id *id1, struct iodev_id *id2) 927c478bd9Sstevel@tonic-gate { 937c478bd9Sstevel@tonic-gate return (id1->id == id2->id && 944be70790Swroche strcmp(id1->tid, id2->tid) == 0); 957c478bd9Sstevel@tonic-gate } 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate static struct iodev_snapshot * 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 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 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 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 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 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 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 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 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 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 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 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 3457c478bd9Sstevel@tonic-gate iodev_match(struct iodev_snapshot *dev, struct iodev_filter *df) 3467c478bd9Sstevel@tonic-gate { 34737fbbce5Scth 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 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 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 } 44437fbbce5Scth } 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)) { 45537fbbce5Scth /* now insert any iodevs into the remaining slots */ 45637fbbce5Scth pos = iodevs; 45737fbbce5Scth while (pos && nr_iodevs) { 45837fbbce5Scth tmp = pos; 45937fbbce5Scth pos = pos->is_next; 46037fbbce5Scth 461*9a25c41fSJohn Levon if (df->if_skip_floppy && 4624be70790Swroche strncmp(tmp->is_name, "fd", 2) == 0) 46337fbbce5Scth continue; 4647c478bd9Sstevel@tonic-gate 46537fbbce5Scth list_del(&iodevs, tmp); 46637fbbce5Scth insert_iodev(ss, tmp); 4677c478bd9Sstevel@tonic-gate 46837fbbce5Scth --nr_iodevs; 46937fbbce5Scth } 4707c478bd9Sstevel@tonic-gate } 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 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 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 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 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, 6214be70790Swroche 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 6297c478bd9Sstevel@tonic-gate get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev, 6307c478bd9Sstevel@tonic-gate kstat_ctl_t *kc) 6317c478bd9Sstevel@tonic-gate { 63237fbbce5Scth disk_list_t *dl; 63337fbbce5Scth 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 */ 66537fbbce5Scth iodev->is_pretty = pretty; 6667c478bd9Sstevel@tonic-gate } 6677c478bd9Sstevel@tonic-gate 6687c478bd9Sstevel@tonic-gate static enum iodev_type 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 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 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 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))) { 75337fbbce5Scth /* 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); 75937fbbce5Scth } 7607c478bd9Sstevel@tonic-gate } 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) { 7787c478bd9Sstevel@tonic-gate return (1); 77937fbbce5Scth } 7807c478bd9Sstevel@tonic-gate } 7817c478bd9Sstevel@tonic-gate 78237fbbce5Scth return (strcmp(name1, name2)); 78337fbbce5Scth } 78437fbbce5Scth 78537fbbce5Scth /* update the target reads and writes */ 78637fbbce5Scth static void 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 * 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, 8334be70790Swroche 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 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) { 89537fbbce5Scth return (1); 89637fbbce5Scth } 89737fbbce5Scth 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 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 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 10097c478bd9Sstevel@tonic-gate acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df) 10107c478bd9Sstevel@tonic-gate { 101137fbbce5Scth kstat_t *ksp; 101237fbbce5Scth struct iodev_snapshot *pos; 101337fbbce5Scth 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 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