1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include "statcommon.h"
30*0Sstevel@tonic-gate #include "dsr.h"
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate #include <sys/dklabel.h>
33*0Sstevel@tonic-gate #include <sys/dktp/fdisk.h>
34*0Sstevel@tonic-gate #include <stdlib.h>
35*0Sstevel@tonic-gate #include <stdarg.h>
36*0Sstevel@tonic-gate #include <unistd.h>
37*0Sstevel@tonic-gate #include <strings.h>
38*0Sstevel@tonic-gate #include <errno.h>
39*0Sstevel@tonic-gate #include <limits.h>
40*0Sstevel@tonic-gate 
41*0Sstevel@tonic-gate static void insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev);
42*0Sstevel@tonic-gate 
43*0Sstevel@tonic-gate static struct iodev_snapshot *
44*0Sstevel@tonic-gate make_controller(int cid)
45*0Sstevel@tonic-gate {
46*0Sstevel@tonic-gate 	struct iodev_snapshot *new;
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate 	new = safe_alloc(sizeof (struct iodev_snapshot));
49*0Sstevel@tonic-gate 	(void) memset(new, 0, sizeof (struct iodev_snapshot));
50*0Sstevel@tonic-gate 	new->is_type = IODEV_CONTROLLER;
51*0Sstevel@tonic-gate 	new->is_id.id = cid;
52*0Sstevel@tonic-gate 	new->is_parent_id.id = IODEV_NO_ID;
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate 	(void) snprintf(new->is_name, sizeof (new->is_name), "c%d", cid);
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate 	return (new);
57*0Sstevel@tonic-gate }
58*0Sstevel@tonic-gate 
59*0Sstevel@tonic-gate static struct iodev_snapshot *
60*0Sstevel@tonic-gate find_iodev_by_name(struct iodev_snapshot *list, const char *name)
61*0Sstevel@tonic-gate {
62*0Sstevel@tonic-gate 	struct iodev_snapshot *pos;
63*0Sstevel@tonic-gate 	struct iodev_snapshot *pos2;
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate 	for (pos = list; pos; pos = pos->is_next) {
66*0Sstevel@tonic-gate 		if (strcmp(pos->is_name, name) == 0)
67*0Sstevel@tonic-gate 			return (pos);
68*0Sstevel@tonic-gate 
69*0Sstevel@tonic-gate 		pos2 = find_iodev_by_name(pos->is_children, name);
70*0Sstevel@tonic-gate 		if (pos2 != NULL)
71*0Sstevel@tonic-gate 			return (pos2);
72*0Sstevel@tonic-gate 	}
73*0Sstevel@tonic-gate 
74*0Sstevel@tonic-gate 	return (NULL);
75*0Sstevel@tonic-gate }
76*0Sstevel@tonic-gate 
77*0Sstevel@tonic-gate static enum iodev_type
78*0Sstevel@tonic-gate parent_iodev_type(enum iodev_type type)
79*0Sstevel@tonic-gate {
80*0Sstevel@tonic-gate 	switch (type) {
81*0Sstevel@tonic-gate 		case IODEV_CONTROLLER: return (0);
82*0Sstevel@tonic-gate 		case IODEV_NFS: return (0);
83*0Sstevel@tonic-gate 		case IODEV_TAPE: return (0);
84*0Sstevel@tonic-gate 		case IODEV_IOPATH: return (IODEV_DISK);
85*0Sstevel@tonic-gate 		case IODEV_DISK: return (IODEV_CONTROLLER);
86*0Sstevel@tonic-gate 		case IODEV_PARTITION: return (IODEV_DISK);
87*0Sstevel@tonic-gate 	}
88*0Sstevel@tonic-gate 	return (IODEV_UNKNOWN);
89*0Sstevel@tonic-gate }
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate static int
92*0Sstevel@tonic-gate id_match(struct iodev_id *id1, struct iodev_id *id2)
93*0Sstevel@tonic-gate {
94*0Sstevel@tonic-gate 	return (id1->id == id2->id &&
95*0Sstevel@tonic-gate 		strcmp(id1->tid, id2->tid) == 0);
96*0Sstevel@tonic-gate }
97*0Sstevel@tonic-gate 
98*0Sstevel@tonic-gate static struct iodev_snapshot *
99*0Sstevel@tonic-gate find_parent(struct snapshot *ss, struct iodev_snapshot *iodev)
100*0Sstevel@tonic-gate {
101*0Sstevel@tonic-gate 	enum iodev_type parent_type = parent_iodev_type(iodev->is_type);
102*0Sstevel@tonic-gate 	struct iodev_snapshot *pos;
103*0Sstevel@tonic-gate 	struct iodev_snapshot *pos2;
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 	if (parent_type == 0 || parent_type == IODEV_UNKNOWN)
106*0Sstevel@tonic-gate 		return (NULL);
107*0Sstevel@tonic-gate 
108*0Sstevel@tonic-gate 	if (iodev->is_parent_id.id == IODEV_NO_ID &&
109*0Sstevel@tonic-gate 	    iodev->is_parent_id.tid[0] == '\0')
110*0Sstevel@tonic-gate 		return (NULL);
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate 	if (parent_type == IODEV_CONTROLLER) {
113*0Sstevel@tonic-gate 		for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
114*0Sstevel@tonic-gate 			if (pos->is_type != IODEV_CONTROLLER)
115*0Sstevel@tonic-gate 				continue;
116*0Sstevel@tonic-gate 			if (pos->is_id.id != iodev->is_parent_id.id)
117*0Sstevel@tonic-gate 				continue;
118*0Sstevel@tonic-gate 			return (pos);
119*0Sstevel@tonic-gate 		}
120*0Sstevel@tonic-gate 
121*0Sstevel@tonic-gate 		if (!(ss->s_types & SNAP_CONTROLLERS))
122*0Sstevel@tonic-gate 			return (NULL);
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate 		pos = make_controller(iodev->is_parent_id.id);
125*0Sstevel@tonic-gate 		insert_iodev(ss, pos);
126*0Sstevel@tonic-gate 		return (pos);
127*0Sstevel@tonic-gate 	}
128*0Sstevel@tonic-gate 
129*0Sstevel@tonic-gate 	/* IODEV_DISK parent */
130*0Sstevel@tonic-gate 	for (pos = ss->s_iodevs; pos; pos = pos->is_next) {
131*0Sstevel@tonic-gate 		if (id_match(&iodev->is_parent_id, &pos->is_id) &&
132*0Sstevel@tonic-gate 		    pos->is_type == IODEV_DISK)
133*0Sstevel@tonic-gate 			return (pos);
134*0Sstevel@tonic-gate 		if (pos->is_type != IODEV_CONTROLLER)
135*0Sstevel@tonic-gate 			continue;
136*0Sstevel@tonic-gate 		for (pos2 = pos->is_children; pos2; pos2 = pos2->is_next) {
137*0Sstevel@tonic-gate 			if (pos2->is_type != IODEV_DISK)
138*0Sstevel@tonic-gate 				continue;
139*0Sstevel@tonic-gate 			if (id_match(&iodev->is_parent_id, &pos2->is_id))
140*0Sstevel@tonic-gate 				return (pos2);
141*0Sstevel@tonic-gate 		}
142*0Sstevel@tonic-gate 	}
143*0Sstevel@tonic-gate 
144*0Sstevel@tonic-gate 	return (NULL);
145*0Sstevel@tonic-gate }
146*0Sstevel@tonic-gate 
147*0Sstevel@tonic-gate static void
148*0Sstevel@tonic-gate list_del(struct iodev_snapshot **list, struct iodev_snapshot *pos)
149*0Sstevel@tonic-gate {
150*0Sstevel@tonic-gate 	if (*list == pos)
151*0Sstevel@tonic-gate 		*list = pos->is_next;
152*0Sstevel@tonic-gate 	if (pos->is_next)
153*0Sstevel@tonic-gate 		pos->is_next->is_prev = pos->is_prev;
154*0Sstevel@tonic-gate 	if (pos->is_prev)
155*0Sstevel@tonic-gate 		pos->is_prev->is_next = pos->is_next;
156*0Sstevel@tonic-gate 	pos->is_prev = pos->is_next = NULL;
157*0Sstevel@tonic-gate }
158*0Sstevel@tonic-gate 
159*0Sstevel@tonic-gate static void
160*0Sstevel@tonic-gate insert_before(struct iodev_snapshot **list, struct iodev_snapshot *pos,
161*0Sstevel@tonic-gate     struct iodev_snapshot *new)
162*0Sstevel@tonic-gate {
163*0Sstevel@tonic-gate 	if (pos == NULL) {
164*0Sstevel@tonic-gate 		new->is_prev = new->is_next = NULL;
165*0Sstevel@tonic-gate 		*list = new;
166*0Sstevel@tonic-gate 		return;
167*0Sstevel@tonic-gate 	}
168*0Sstevel@tonic-gate 
169*0Sstevel@tonic-gate 	new->is_next = pos;
170*0Sstevel@tonic-gate 	new->is_prev = pos->is_prev;
171*0Sstevel@tonic-gate 	if (pos->is_prev)
172*0Sstevel@tonic-gate 		pos->is_prev->is_next = new;
173*0Sstevel@tonic-gate 	else
174*0Sstevel@tonic-gate 		*list = new;
175*0Sstevel@tonic-gate 	pos->is_prev = new;
176*0Sstevel@tonic-gate }
177*0Sstevel@tonic-gate 
178*0Sstevel@tonic-gate static void
179*0Sstevel@tonic-gate insert_after(struct iodev_snapshot **list, struct iodev_snapshot *pos,
180*0Sstevel@tonic-gate     struct iodev_snapshot *new)
181*0Sstevel@tonic-gate {
182*0Sstevel@tonic-gate 	if (pos == NULL) {
183*0Sstevel@tonic-gate 		new->is_prev = new->is_next = NULL;
184*0Sstevel@tonic-gate 		*list = new;
185*0Sstevel@tonic-gate 		return;
186*0Sstevel@tonic-gate 	}
187*0Sstevel@tonic-gate 
188*0Sstevel@tonic-gate 	new->is_next = pos->is_next;
189*0Sstevel@tonic-gate 	new->is_prev = pos;
190*0Sstevel@tonic-gate 	if (pos->is_next)
191*0Sstevel@tonic-gate 		pos->is_next->is_prev = new;
192*0Sstevel@tonic-gate 	pos->is_next = new;
193*0Sstevel@tonic-gate }
194*0Sstevel@tonic-gate 
195*0Sstevel@tonic-gate static void
196*0Sstevel@tonic-gate insert_into(struct iodev_snapshot **list, struct iodev_snapshot *iodev)
197*0Sstevel@tonic-gate {
198*0Sstevel@tonic-gate 	struct iodev_snapshot *tmp = *list;
199*0Sstevel@tonic-gate 	if (*list == NULL) {
200*0Sstevel@tonic-gate 		*list = iodev;
201*0Sstevel@tonic-gate 		return;
202*0Sstevel@tonic-gate 	}
203*0Sstevel@tonic-gate 
204*0Sstevel@tonic-gate 	for (;;) {
205*0Sstevel@tonic-gate 		if (iodev_cmp(tmp, iodev) > 0) {
206*0Sstevel@tonic-gate 			insert_before(list, tmp, iodev);
207*0Sstevel@tonic-gate 			return;
208*0Sstevel@tonic-gate 		}
209*0Sstevel@tonic-gate 
210*0Sstevel@tonic-gate 		if (tmp->is_next == NULL)
211*0Sstevel@tonic-gate 			break;
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate 		tmp = tmp->is_next;
214*0Sstevel@tonic-gate 	}
215*0Sstevel@tonic-gate 
216*0Sstevel@tonic-gate 	insert_after(list, tmp, iodev);
217*0Sstevel@tonic-gate }
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate static int
220*0Sstevel@tonic-gate disk_or_partition(enum iodev_type type)
221*0Sstevel@tonic-gate {
222*0Sstevel@tonic-gate 	return (type == IODEV_DISK || type == IODEV_PARTITION);
223*0Sstevel@tonic-gate }
224*0Sstevel@tonic-gate 
225*0Sstevel@tonic-gate static void
226*0Sstevel@tonic-gate insert_iodev(struct snapshot *ss, struct iodev_snapshot *iodev)
227*0Sstevel@tonic-gate {
228*0Sstevel@tonic-gate 	struct iodev_snapshot *parent = find_parent(ss, iodev);
229*0Sstevel@tonic-gate 	struct iodev_snapshot **list;
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 	if (parent != NULL) {
232*0Sstevel@tonic-gate 		list = &parent->is_children;
233*0Sstevel@tonic-gate 		parent->is_nr_children++;
234*0Sstevel@tonic-gate 	} else {
235*0Sstevel@tonic-gate 		list = &ss->s_iodevs;
236*0Sstevel@tonic-gate 		ss->s_nr_iodevs++;
237*0Sstevel@tonic-gate 	}
238*0Sstevel@tonic-gate 
239*0Sstevel@tonic-gate 	insert_into(list, iodev);
240*0Sstevel@tonic-gate }
241*0Sstevel@tonic-gate 
242*0Sstevel@tonic-gate static int
243*0Sstevel@tonic-gate iodev_match(struct iodev_snapshot *dev, struct iodev_filter *df)
244*0Sstevel@tonic-gate {
245*0Sstevel@tonic-gate 	size_t i;
246*0Sstevel@tonic-gate 	int is_floppy = (strncmp(dev->is_name, "fd", 2) == 0);
247*0Sstevel@tonic-gate 
248*0Sstevel@tonic-gate 	/* no filter, pass */
249*0Sstevel@tonic-gate 	if (df == NULL)
250*0Sstevel@tonic-gate 		return (1);
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate 	/* no filtered names, pass if not floppy and skipped */
253*0Sstevel@tonic-gate 	if (df->if_nr_names == NULL)
254*0Sstevel@tonic-gate 		return (!(df->if_skip_floppy && is_floppy));
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	for (i = 0; i < df->if_nr_names; i++) {
257*0Sstevel@tonic-gate 		if (strcmp(dev->is_name, df->if_names[i]) == 0)
258*0Sstevel@tonic-gate 			return (1);
259*0Sstevel@tonic-gate 		if (dev->is_pretty != NULL &&
260*0Sstevel@tonic-gate 		    strcmp(dev->is_pretty, df->if_names[i]) == 0)
261*0Sstevel@tonic-gate 			return (1);
262*0Sstevel@tonic-gate 	}
263*0Sstevel@tonic-gate 
264*0Sstevel@tonic-gate 	/* not found in specified names, fail match */
265*0Sstevel@tonic-gate 	return (0);
266*0Sstevel@tonic-gate }
267*0Sstevel@tonic-gate 
268*0Sstevel@tonic-gate /* select which I/O devices to collect stats for */
269*0Sstevel@tonic-gate static void
270*0Sstevel@tonic-gate choose_iodevs(struct snapshot *ss, struct iodev_snapshot *iodevs,
271*0Sstevel@tonic-gate     struct iodev_filter *df)
272*0Sstevel@tonic-gate {
273*0Sstevel@tonic-gate 	struct iodev_snapshot *pos = iodevs;
274*0Sstevel@tonic-gate 	int nr_iodevs = df ? df->if_max_iodevs : UNLIMITED_IODEVS;
275*0Sstevel@tonic-gate 
276*0Sstevel@tonic-gate 	if (nr_iodevs == UNLIMITED_IODEVS)
277*0Sstevel@tonic-gate 		nr_iodevs = INT_MAX;
278*0Sstevel@tonic-gate 
279*0Sstevel@tonic-gate 	while (pos && nr_iodevs) {
280*0Sstevel@tonic-gate 		struct iodev_snapshot *tmp = pos;
281*0Sstevel@tonic-gate 		pos = pos->is_next;
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 		if (!iodev_match(tmp, df))
284*0Sstevel@tonic-gate 			continue;
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate 		list_del(&iodevs, tmp);
287*0Sstevel@tonic-gate 		insert_iodev(ss, tmp);
288*0Sstevel@tonic-gate 
289*0Sstevel@tonic-gate 		--nr_iodevs;
290*0Sstevel@tonic-gate 	}
291*0Sstevel@tonic-gate 
292*0Sstevel@tonic-gate 	pos = iodevs;
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	/* now insert any iodevs into the remaining slots */
295*0Sstevel@tonic-gate 	while (pos && nr_iodevs) {
296*0Sstevel@tonic-gate 		struct iodev_snapshot *tmp = pos;
297*0Sstevel@tonic-gate 		pos = pos->is_next;
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate 		if (df && df->if_skip_floppy &&
300*0Sstevel@tonic-gate 			strncmp(tmp->is_name, "fd", 2) == 0)
301*0Sstevel@tonic-gate 			continue;
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate 		list_del(&iodevs, tmp);
304*0Sstevel@tonic-gate 		insert_iodev(ss, tmp);
305*0Sstevel@tonic-gate 
306*0Sstevel@tonic-gate 		--nr_iodevs;
307*0Sstevel@tonic-gate 	}
308*0Sstevel@tonic-gate 
309*0Sstevel@tonic-gate 	/* clear the unwanted ones */
310*0Sstevel@tonic-gate 	pos = iodevs;
311*0Sstevel@tonic-gate 	while (pos) {
312*0Sstevel@tonic-gate 		struct iodev_snapshot *tmp = pos;
313*0Sstevel@tonic-gate 		pos = pos->is_next;
314*0Sstevel@tonic-gate 		free_iodev(tmp);
315*0Sstevel@tonic-gate 	}
316*0Sstevel@tonic-gate }
317*0Sstevel@tonic-gate 
318*0Sstevel@tonic-gate static int
319*0Sstevel@tonic-gate collate_controller(struct iodev_snapshot *controller,
320*0Sstevel@tonic-gate     struct iodev_snapshot *disk)
321*0Sstevel@tonic-gate {
322*0Sstevel@tonic-gate 	controller->is_stats.nread += disk->is_stats.nread;
323*0Sstevel@tonic-gate 	controller->is_stats.nwritten += disk->is_stats.nwritten;
324*0Sstevel@tonic-gate 	controller->is_stats.reads += disk->is_stats.reads;
325*0Sstevel@tonic-gate 	controller->is_stats.writes += disk->is_stats.writes;
326*0Sstevel@tonic-gate 	controller->is_stats.wtime += disk->is_stats.wtime;
327*0Sstevel@tonic-gate 	controller->is_stats.wlentime += disk->is_stats.wlentime;
328*0Sstevel@tonic-gate 	controller->is_stats.rtime += disk->is_stats.rtime;
329*0Sstevel@tonic-gate 	controller->is_stats.rlentime += disk->is_stats.rlentime;
330*0Sstevel@tonic-gate 	controller->is_crtime += disk->is_crtime;
331*0Sstevel@tonic-gate 	controller->is_snaptime += disk->is_snaptime;
332*0Sstevel@tonic-gate 	if (kstat_add(&disk->is_errors, &controller->is_errors))
333*0Sstevel@tonic-gate 		return (errno);
334*0Sstevel@tonic-gate 	return (0);
335*0Sstevel@tonic-gate }
336*0Sstevel@tonic-gate 
337*0Sstevel@tonic-gate static int
338*0Sstevel@tonic-gate acquire_iodev_stats(struct iodev_snapshot *list, kstat_ctl_t *kc)
339*0Sstevel@tonic-gate {
340*0Sstevel@tonic-gate 	struct iodev_snapshot *pos;
341*0Sstevel@tonic-gate 	int err = 0;
342*0Sstevel@tonic-gate 
343*0Sstevel@tonic-gate 	for (pos = list; pos; pos = pos->is_next) {
344*0Sstevel@tonic-gate 		/* controllers don't have stats (yet) */
345*0Sstevel@tonic-gate 		if (pos->is_ksp != NULL) {
346*0Sstevel@tonic-gate 			if (kstat_read(kc, pos->is_ksp, &pos->is_stats) == -1)
347*0Sstevel@tonic-gate 				return (errno);
348*0Sstevel@tonic-gate 			/* make sure crtime/snaptime is updated */
349*0Sstevel@tonic-gate 			pos->is_crtime = pos->is_ksp->ks_crtime;
350*0Sstevel@tonic-gate 			pos->is_snaptime = pos->is_ksp->ks_snaptime;
351*0Sstevel@tonic-gate 		}
352*0Sstevel@tonic-gate 
353*0Sstevel@tonic-gate 		if ((err = acquire_iodev_stats(pos->is_children, kc)))
354*0Sstevel@tonic-gate 			return (err);
355*0Sstevel@tonic-gate 
356*0Sstevel@tonic-gate 		if (pos->is_type == IODEV_CONTROLLER) {
357*0Sstevel@tonic-gate 			struct iodev_snapshot *pos2 = pos->is_children;
358*0Sstevel@tonic-gate 
359*0Sstevel@tonic-gate 			for (; pos2; pos2 = pos2->is_next) {
360*0Sstevel@tonic-gate 				if ((err = collate_controller(pos, pos2)))
361*0Sstevel@tonic-gate 					return (err);
362*0Sstevel@tonic-gate 			}
363*0Sstevel@tonic-gate 		}
364*0Sstevel@tonic-gate 	}
365*0Sstevel@tonic-gate 
366*0Sstevel@tonic-gate 	return (0);
367*0Sstevel@tonic-gate }
368*0Sstevel@tonic-gate 
369*0Sstevel@tonic-gate static int
370*0Sstevel@tonic-gate acquire_iodev_errors(struct snapshot *ss, kstat_ctl_t *kc)
371*0Sstevel@tonic-gate {
372*0Sstevel@tonic-gate 	kstat_t *ksp;
373*0Sstevel@tonic-gate 
374*0Sstevel@tonic-gate 	if (!(ss->s_types && SNAP_IODEV_ERRORS))
375*0Sstevel@tonic-gate 		return (0);
376*0Sstevel@tonic-gate 
377*0Sstevel@tonic-gate 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
378*0Sstevel@tonic-gate 		char kstat_name[KSTAT_STRLEN];
379*0Sstevel@tonic-gate 		char *dname = kstat_name;
380*0Sstevel@tonic-gate 		char *ename = ksp->ks_name;
381*0Sstevel@tonic-gate 		struct iodev_snapshot *iodev;
382*0Sstevel@tonic-gate 
383*0Sstevel@tonic-gate 		if (ksp->ks_type != KSTAT_TYPE_NAMED)
384*0Sstevel@tonic-gate 			continue;
385*0Sstevel@tonic-gate 		if (strncmp(ksp->ks_class, "device_error", 12) != 0 &&
386*0Sstevel@tonic-gate 		    strncmp(ksp->ks_class, "iopath_error", 12) != 0)
387*0Sstevel@tonic-gate 			continue;
388*0Sstevel@tonic-gate 
389*0Sstevel@tonic-gate 		/*
390*0Sstevel@tonic-gate 		 * Some drivers may not follow the naming convention
391*0Sstevel@tonic-gate 		 * for error kstats (i.e., drivername,err) so
392*0Sstevel@tonic-gate 		 * be sure we don't walk off the end.
393*0Sstevel@tonic-gate 		 */
394*0Sstevel@tonic-gate 		while (*ename && *ename != ',') {
395*0Sstevel@tonic-gate 			*dname = *ename;
396*0Sstevel@tonic-gate 			dname++;
397*0Sstevel@tonic-gate 			ename++;
398*0Sstevel@tonic-gate 		}
399*0Sstevel@tonic-gate 		*dname = '\0';
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate 		iodev = find_iodev_by_name(ss->s_iodevs, kstat_name);
402*0Sstevel@tonic-gate 
403*0Sstevel@tonic-gate 		if (iodev == NULL)
404*0Sstevel@tonic-gate 			continue;
405*0Sstevel@tonic-gate 
406*0Sstevel@tonic-gate 		if (kstat_read(kc, ksp, NULL) == -1)
407*0Sstevel@tonic-gate 			return (errno);
408*0Sstevel@tonic-gate 		if (kstat_copy(ksp, &iodev->is_errors) == -1)
409*0Sstevel@tonic-gate 			return (errno);
410*0Sstevel@tonic-gate 	}
411*0Sstevel@tonic-gate 
412*0Sstevel@tonic-gate 	return (0);
413*0Sstevel@tonic-gate }
414*0Sstevel@tonic-gate 
415*0Sstevel@tonic-gate static void
416*0Sstevel@tonic-gate get_ids(struct iodev_snapshot *iodev, const char *pretty)
417*0Sstevel@tonic-gate {
418*0Sstevel@tonic-gate 	int ctr, disk, slice, ret;
419*0Sstevel@tonic-gate 	char *target;
420*0Sstevel@tonic-gate 	const char *p1;
421*0Sstevel@tonic-gate 	const char *p2;
422*0Sstevel@tonic-gate 
423*0Sstevel@tonic-gate 	if (pretty == NULL)
424*0Sstevel@tonic-gate 		return;
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate 	if (sscanf(pretty, "c%d", &ctr) != 1)
427*0Sstevel@tonic-gate 		return;
428*0Sstevel@tonic-gate 
429*0Sstevel@tonic-gate 	p1 = pretty;
430*0Sstevel@tonic-gate 	while (*p1 && *p1 != 't')
431*0Sstevel@tonic-gate 		++p1;
432*0Sstevel@tonic-gate 
433*0Sstevel@tonic-gate 	if (!*p1)
434*0Sstevel@tonic-gate 		return;
435*0Sstevel@tonic-gate 	++p1;
436*0Sstevel@tonic-gate 
437*0Sstevel@tonic-gate 	p2 = p1;
438*0Sstevel@tonic-gate 	while (*p2 && *p2 != 'd')
439*0Sstevel@tonic-gate 		++p2;
440*0Sstevel@tonic-gate 
441*0Sstevel@tonic-gate 	if (!*p2 || p2 == p1)
442*0Sstevel@tonic-gate 		return;
443*0Sstevel@tonic-gate 
444*0Sstevel@tonic-gate 	target = safe_alloc(1 + p2 - p1);
445*0Sstevel@tonic-gate 	(void) strlcpy(target, p1, 1 + p2 - p1);
446*0Sstevel@tonic-gate 
447*0Sstevel@tonic-gate 	ret = sscanf(p2, "d%d%*[sp]%d", &disk, &slice);
448*0Sstevel@tonic-gate 
449*0Sstevel@tonic-gate 	if (ret == 2 && iodev->is_type == IODEV_PARTITION) {
450*0Sstevel@tonic-gate 		iodev->is_id.id = slice;
451*0Sstevel@tonic-gate 		iodev->is_parent_id.id = disk;
452*0Sstevel@tonic-gate 		(void) strlcpy(iodev->is_parent_id.tid, target, KSTAT_STRLEN);
453*0Sstevel@tonic-gate 	} else if (ret == 1) {
454*0Sstevel@tonic-gate 		if (iodev->is_type == IODEV_DISK) {
455*0Sstevel@tonic-gate 			iodev->is_id.id = disk;
456*0Sstevel@tonic-gate 			(void) strlcpy(iodev->is_id.tid, target, KSTAT_STRLEN);
457*0Sstevel@tonic-gate 			iodev->is_parent_id.id = ctr;
458*0Sstevel@tonic-gate 		} else if (iodev->is_type == IODEV_IOPATH) {
459*0Sstevel@tonic-gate 			iodev->is_parent_id.id = disk;
460*0Sstevel@tonic-gate 			(void) strlcpy(iodev->is_parent_id.tid,
461*0Sstevel@tonic-gate 				target, KSTAT_STRLEN);
462*0Sstevel@tonic-gate 		}
463*0Sstevel@tonic-gate 	}
464*0Sstevel@tonic-gate 
465*0Sstevel@tonic-gate 	free(target);
466*0Sstevel@tonic-gate }
467*0Sstevel@tonic-gate 
468*0Sstevel@tonic-gate static char *
469*0Sstevel@tonic-gate get_slice(int partition, disk_list_t *dl)
470*0Sstevel@tonic-gate {
471*0Sstevel@tonic-gate 	char *tmpbuf;
472*0Sstevel@tonic-gate 	size_t tmplen;
473*0Sstevel@tonic-gate 
474*0Sstevel@tonic-gate 	if (!(dl->flags & SLICES_OK))
475*0Sstevel@tonic-gate 		return (NULL);
476*0Sstevel@tonic-gate 	if (partition < 0 || partition >= NDKMAP)
477*0Sstevel@tonic-gate 		return (NULL);
478*0Sstevel@tonic-gate 
479*0Sstevel@tonic-gate 	/* space for 's', and integer < NDKMAP (16) */
480*0Sstevel@tonic-gate 	tmplen = strlen(dl->dsk) + strlen("sXX") + 1;
481*0Sstevel@tonic-gate 	tmpbuf = safe_alloc(tmplen);
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate 	/*
484*0Sstevel@tonic-gate 	 * This is a regular slice. Create the name and
485*0Sstevel@tonic-gate 	 * copy it for use by the calling routine.
486*0Sstevel@tonic-gate 	 */
487*0Sstevel@tonic-gate 	(void) snprintf(tmpbuf, tmplen, "%ss%d", dl->dsk, partition);
488*0Sstevel@tonic-gate 	return (tmpbuf);
489*0Sstevel@tonic-gate }
490*0Sstevel@tonic-gate 
491*0Sstevel@tonic-gate static char *
492*0Sstevel@tonic-gate get_intel_partition(int partition, disk_list_t *dl)
493*0Sstevel@tonic-gate {
494*0Sstevel@tonic-gate 	char *tmpbuf;
495*0Sstevel@tonic-gate 	size_t tmplen;
496*0Sstevel@tonic-gate 
497*0Sstevel@tonic-gate 	if (partition <= 0 || !(dl->flags & PARTITIONS_OK))
498*0Sstevel@tonic-gate 		return (NULL);
499*0Sstevel@tonic-gate 
500*0Sstevel@tonic-gate 	/*
501*0Sstevel@tonic-gate 	 * See if it falls in the range of allowable partitions. The
502*0Sstevel@tonic-gate 	 * fdisk partitions show up after the traditional slices so we
503*0Sstevel@tonic-gate 	 * determine which partition we're in and return that.
504*0Sstevel@tonic-gate 	 * The NUMPART + 1 is not a mistake. There are currently
505*0Sstevel@tonic-gate 	 * FD_NUMPART + 1 partitions that show up in the device directory.
506*0Sstevel@tonic-gate 	 */
507*0Sstevel@tonic-gate 	partition -= NDKMAP;
508*0Sstevel@tonic-gate 	if (partition < 0 || partition >= (FD_NUMPART + 1))
509*0Sstevel@tonic-gate 		return (NULL);
510*0Sstevel@tonic-gate 
511*0Sstevel@tonic-gate 	/* space for 'p', and integer < NDKMAP (16) */
512*0Sstevel@tonic-gate 	tmplen = strlen(dl->dsk) + strlen("pXX") + 1;
513*0Sstevel@tonic-gate 	tmpbuf = safe_alloc(tmplen);
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 	(void) snprintf(tmpbuf, tmplen, "%sp%d", dl->dsk, partition);
516*0Sstevel@tonic-gate 	return (tmpbuf);
517*0Sstevel@tonic-gate }
518*0Sstevel@tonic-gate 
519*0Sstevel@tonic-gate static void
520*0Sstevel@tonic-gate get_pretty_name(enum snapshot_types types, struct iodev_snapshot *iodev,
521*0Sstevel@tonic-gate 	kstat_ctl_t *kc)
522*0Sstevel@tonic-gate {
523*0Sstevel@tonic-gate 	disk_list_t *dl;
524*0Sstevel@tonic-gate 	char *pretty = NULL;
525*0Sstevel@tonic-gate 	char *tmp;
526*0Sstevel@tonic-gate 	int partition;
527*0Sstevel@tonic-gate 
528*0Sstevel@tonic-gate 	if (iodev->is_type == IODEV_NFS) {
529*0Sstevel@tonic-gate 		if (!(types & SNAP_IODEV_PRETTY))
530*0Sstevel@tonic-gate 			return;
531*0Sstevel@tonic-gate 
532*0Sstevel@tonic-gate 		iodev->is_pretty = lookup_nfs_name(iodev->is_name, kc);
533*0Sstevel@tonic-gate 		return;
534*0Sstevel@tonic-gate 	}
535*0Sstevel@tonic-gate 
536*0Sstevel@tonic-gate 	if (iodev->is_type == IODEV_IOPATH) {
537*0Sstevel@tonic-gate 		char buf[KSTAT_STRLEN];
538*0Sstevel@tonic-gate 		size_t len;
539*0Sstevel@tonic-gate 
540*0Sstevel@tonic-gate 		tmp = iodev->is_name;
541*0Sstevel@tonic-gate 		while (*tmp && *tmp != '.')
542*0Sstevel@tonic-gate 			tmp++;
543*0Sstevel@tonic-gate 		if (!*tmp)
544*0Sstevel@tonic-gate 			return;
545*0Sstevel@tonic-gate 		(void) strlcpy(buf, iodev->is_name, 1 + tmp - iodev->is_name);
546*0Sstevel@tonic-gate 		dl = lookup_ks_name(buf);
547*0Sstevel@tonic-gate 		if (dl == NULL || dl->dsk == NULL)
548*0Sstevel@tonic-gate 			return;
549*0Sstevel@tonic-gate 		len = strlen(dl->dsk) + strlen(tmp) + 1;
550*0Sstevel@tonic-gate 		pretty = safe_alloc(len);
551*0Sstevel@tonic-gate 		(void) strlcpy(pretty, dl->dsk, len);
552*0Sstevel@tonic-gate 		(void) strlcat(pretty, tmp, len);
553*0Sstevel@tonic-gate 		goto out;
554*0Sstevel@tonic-gate 	}
555*0Sstevel@tonic-gate 
556*0Sstevel@tonic-gate 	dl = lookup_ks_name(iodev->is_name);
557*0Sstevel@tonic-gate 	if (dl == NULL)
558*0Sstevel@tonic-gate 		return;
559*0Sstevel@tonic-gate 
560*0Sstevel@tonic-gate 	if (dl->dsk)
561*0Sstevel@tonic-gate 		pretty = safe_strdup(dl->dsk);
562*0Sstevel@tonic-gate 
563*0Sstevel@tonic-gate 	if (types & SNAP_IODEV_PRETTY) {
564*0Sstevel@tonic-gate 		if (dl->dname)
565*0Sstevel@tonic-gate 			iodev->is_dname = safe_strdup(dl->dname);
566*0Sstevel@tonic-gate 	}
567*0Sstevel@tonic-gate 
568*0Sstevel@tonic-gate 	if (dl->devidstr)
569*0Sstevel@tonic-gate 		iodev->is_devid = safe_strdup(dl->devidstr);
570*0Sstevel@tonic-gate 
571*0Sstevel@tonic-gate 	/* look for a possible partition number */
572*0Sstevel@tonic-gate 	tmp = iodev->is_name;
573*0Sstevel@tonic-gate 	while (*tmp && *tmp != ',')
574*0Sstevel@tonic-gate 		tmp++;
575*0Sstevel@tonic-gate 	if (*tmp != ',')
576*0Sstevel@tonic-gate 		goto out;
577*0Sstevel@tonic-gate 
578*0Sstevel@tonic-gate 	tmp++;
579*0Sstevel@tonic-gate 	partition = (int)(*tmp - 'a');
580*0Sstevel@tonic-gate 
581*0Sstevel@tonic-gate 	if (iodev->is_type == IODEV_PARTITION) {
582*0Sstevel@tonic-gate 		char *part;
583*0Sstevel@tonic-gate 		if ((part = get_slice(partition, dl)) == NULL)
584*0Sstevel@tonic-gate 			part = get_intel_partition(partition, dl);
585*0Sstevel@tonic-gate 		if (part != NULL) {
586*0Sstevel@tonic-gate 			free(pretty);
587*0Sstevel@tonic-gate 			pretty = part;
588*0Sstevel@tonic-gate 		}
589*0Sstevel@tonic-gate 	}
590*0Sstevel@tonic-gate 
591*0Sstevel@tonic-gate out:
592*0Sstevel@tonic-gate 	get_ids(iodev, pretty);
593*0Sstevel@tonic-gate 
594*0Sstevel@tonic-gate 	/* only fill in the pretty name if specifically asked for */
595*0Sstevel@tonic-gate 	if (types & SNAP_IODEV_PRETTY) {
596*0Sstevel@tonic-gate 		iodev->is_pretty = pretty;
597*0Sstevel@tonic-gate 	} else {
598*0Sstevel@tonic-gate 		free(pretty);
599*0Sstevel@tonic-gate 	}
600*0Sstevel@tonic-gate }
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate static enum iodev_type
603*0Sstevel@tonic-gate get_iodev_type(kstat_t *ksp)
604*0Sstevel@tonic-gate {
605*0Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "disk") == 0)
606*0Sstevel@tonic-gate 		return (IODEV_DISK);
607*0Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "partition") == 0)
608*0Sstevel@tonic-gate 		return (IODEV_PARTITION);
609*0Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "nfs") == 0)
610*0Sstevel@tonic-gate 		return (IODEV_NFS);
611*0Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "iopath") == 0)
612*0Sstevel@tonic-gate 		return (IODEV_IOPATH);
613*0Sstevel@tonic-gate 	if (strcmp(ksp->ks_class, "tape") == 0)
614*0Sstevel@tonic-gate 		return (IODEV_TAPE);
615*0Sstevel@tonic-gate 	return (IODEV_UNKNOWN);
616*0Sstevel@tonic-gate }
617*0Sstevel@tonic-gate 
618*0Sstevel@tonic-gate int
619*0Sstevel@tonic-gate iodev_cmp(struct iodev_snapshot *io1, struct iodev_snapshot *io2)
620*0Sstevel@tonic-gate {
621*0Sstevel@tonic-gate 	/* neutral sort order between disk and part */
622*0Sstevel@tonic-gate 	if (!disk_or_partition(io1->is_type) ||
623*0Sstevel@tonic-gate 		!disk_or_partition(io2->is_type)) {
624*0Sstevel@tonic-gate 		if (io1->is_type < io2->is_type)
625*0Sstevel@tonic-gate 			return (-1);
626*0Sstevel@tonic-gate 		if (io1->is_type > io2->is_type)
627*0Sstevel@tonic-gate 			return (1);
628*0Sstevel@tonic-gate 	}
629*0Sstevel@tonic-gate 
630*0Sstevel@tonic-gate 	/* controller doesn't have ksp */
631*0Sstevel@tonic-gate 	if (io1->is_ksp && io2->is_ksp) {
632*0Sstevel@tonic-gate 		if (strcmp(io1->is_module, io2->is_module) != 0)
633*0Sstevel@tonic-gate 			return (strcmp(io1->is_module, io2->is_module));
634*0Sstevel@tonic-gate 		if (io1->is_instance < io2->is_instance)
635*0Sstevel@tonic-gate 			return (-1);
636*0Sstevel@tonic-gate 		if (io1->is_instance > io2->is_instance)
637*0Sstevel@tonic-gate 			return (1);
638*0Sstevel@tonic-gate 	} else {
639*0Sstevel@tonic-gate 		if (io1->is_id.id < io2->is_id.id)
640*0Sstevel@tonic-gate 			return (-1);
641*0Sstevel@tonic-gate 		if (io1->is_id.id > io2->is_id.id)
642*0Sstevel@tonic-gate 			return (1);
643*0Sstevel@tonic-gate 	}
644*0Sstevel@tonic-gate 
645*0Sstevel@tonic-gate 	return (strcmp(io1->is_name, io2->is_name));
646*0Sstevel@tonic-gate }
647*0Sstevel@tonic-gate 
648*0Sstevel@tonic-gate int
649*0Sstevel@tonic-gate acquire_iodevs(struct snapshot *ss, kstat_ctl_t *kc, struct iodev_filter *df)
650*0Sstevel@tonic-gate {
651*0Sstevel@tonic-gate 	kstat_t *ksp;
652*0Sstevel@tonic-gate 	int err = 0;
653*0Sstevel@tonic-gate 	struct iodev_snapshot *pos;
654*0Sstevel@tonic-gate 	struct iodev_snapshot *list = NULL;
655*0Sstevel@tonic-gate 
656*0Sstevel@tonic-gate 	ss->s_nr_iodevs = 0;
657*0Sstevel@tonic-gate 
658*0Sstevel@tonic-gate 	for (ksp = kc->kc_chain; ksp; ksp = ksp->ks_next) {
659*0Sstevel@tonic-gate 		enum iodev_type type;
660*0Sstevel@tonic-gate 
661*0Sstevel@tonic-gate 		if (ksp->ks_type != KSTAT_TYPE_IO)
662*0Sstevel@tonic-gate 			continue;
663*0Sstevel@tonic-gate 
664*0Sstevel@tonic-gate 		/* e.g. "usb_byte_count" is not handled */
665*0Sstevel@tonic-gate 		if ((type = get_iodev_type(ksp)) == IODEV_UNKNOWN)
666*0Sstevel@tonic-gate 			continue;
667*0Sstevel@tonic-gate 
668*0Sstevel@tonic-gate 		if (df && !(type & df->if_allowed_types))
669*0Sstevel@tonic-gate 			continue;
670*0Sstevel@tonic-gate 
671*0Sstevel@tonic-gate 		if ((pos = malloc(sizeof (struct iodev_snapshot))) == NULL) {
672*0Sstevel@tonic-gate 			err = errno;
673*0Sstevel@tonic-gate 			goto out;
674*0Sstevel@tonic-gate 		}
675*0Sstevel@tonic-gate 
676*0Sstevel@tonic-gate 		(void) memset(pos, 0, sizeof (struct iodev_snapshot));
677*0Sstevel@tonic-gate 
678*0Sstevel@tonic-gate 		pos->is_type = type;
679*0Sstevel@tonic-gate 		pos->is_crtime = ksp->ks_crtime;
680*0Sstevel@tonic-gate 		pos->is_snaptime = ksp->ks_snaptime;
681*0Sstevel@tonic-gate 		pos->is_id.id = IODEV_NO_ID;
682*0Sstevel@tonic-gate 		pos->is_parent_id.id = IODEV_NO_ID;
683*0Sstevel@tonic-gate 		pos->is_ksp = ksp;
684*0Sstevel@tonic-gate 		pos->is_instance = ksp->ks_instance;
685*0Sstevel@tonic-gate 
686*0Sstevel@tonic-gate 		(void) strlcpy(pos->is_module, ksp->ks_module, KSTAT_STRLEN);
687*0Sstevel@tonic-gate 		(void) strlcpy(pos->is_name, ksp->ks_name, KSTAT_STRLEN);
688*0Sstevel@tonic-gate 		get_pretty_name(ss->s_types, pos, kc);
689*0Sstevel@tonic-gate 
690*0Sstevel@tonic-gate 		/*
691*0Sstevel@tonic-gate 		 * We must insert in sort order so e.g. vmstat -l
692*0Sstevel@tonic-gate 		 * chooses in order.
693*0Sstevel@tonic-gate 		 */
694*0Sstevel@tonic-gate 		insert_into(&list, pos);
695*0Sstevel@tonic-gate 	}
696*0Sstevel@tonic-gate 
697*0Sstevel@tonic-gate 	choose_iodevs(ss, list, df);
698*0Sstevel@tonic-gate 
699*0Sstevel@tonic-gate 	/* before acquire_stats for collate_controller()'s benefit */
700*0Sstevel@tonic-gate 	if (ss->s_types & SNAP_IODEV_ERRORS) {
701*0Sstevel@tonic-gate 		if ((err = acquire_iodev_errors(ss, kc)) != 0)
702*0Sstevel@tonic-gate 			goto out;
703*0Sstevel@tonic-gate 	}
704*0Sstevel@tonic-gate 
705*0Sstevel@tonic-gate 	if ((err = acquire_iodev_stats(ss->s_iodevs, kc)) != 0)
706*0Sstevel@tonic-gate 		goto out;
707*0Sstevel@tonic-gate 
708*0Sstevel@tonic-gate 	err = 0;
709*0Sstevel@tonic-gate out:
710*0Sstevel@tonic-gate 	return (err);
711*0Sstevel@tonic-gate }
712*0Sstevel@tonic-gate 
713*0Sstevel@tonic-gate void
714*0Sstevel@tonic-gate free_iodev(struct iodev_snapshot *iodev)
715*0Sstevel@tonic-gate {
716*0Sstevel@tonic-gate 	while (iodev->is_children) {
717*0Sstevel@tonic-gate 		struct iodev_snapshot *tmp = iodev->is_children;
718*0Sstevel@tonic-gate 		iodev->is_children = iodev->is_children->is_next;
719*0Sstevel@tonic-gate 		free_iodev(tmp);
720*0Sstevel@tonic-gate 	}
721*0Sstevel@tonic-gate 
722*0Sstevel@tonic-gate 	free(iodev->is_errors.ks_data);
723*0Sstevel@tonic-gate 	free(iodev->is_pretty);
724*0Sstevel@tonic-gate 	free(iodev->is_dname);
725*0Sstevel@tonic-gate 	free(iodev->is_devid);
726*0Sstevel@tonic-gate 	free(iodev);
727*0Sstevel@tonic-gate }
728