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