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